diff options
author | Yarden Shoham <git@yardenshoham.com> | 2024-02-16 03:52:25 +0100 |
---|---|---|
committer | Earl Warren <contact@earl-warren.org> | 2024-02-17 23:24:31 +0100 |
commit | 4f050f358a15dd51903e01b330a5419b2ac06693 (patch) | |
tree | bba745a6897a104d66d2846933b3b763168a726f | |
parent | Avoid vue warning in dev mode (#29188) (diff) | |
download | forgejo-4f050f358a15dd51903e01b330a5419b2ac06693.tar.xz forgejo-4f050f358a15dd51903e01b330a5419b2ac06693.zip |
Auto-update the system status in admin dashboard (#29163)
- Refactor the system status list into its own template
- Change the backend to return only the system status if htmx initiated
the request
- `hx-get="{{$.Link}}/system_status`: reuse the backend handler
- `hx-swap="innerHTML"`: replace the `<div>`'s innerHTML (essentially
the new template)
- `hx-trigger="every 5s"`: call every 5 seconds
- `hx-indicator=".divider"`: the `is-loading` class shouldn't be added
to the div during the request, so set it on an element it has no effect
on
- Render "Since Last GC Time" with `<relative-time>`, so we send a
timestamp
# Auto-update in action GIF
![action](https://github.com/go-gitea/gitea/assets/20454870/c6e1f220-f0fb-4460-ac3b-59f315e30e29)
---------
Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit c70f65e83bc1876fb368fd117d342573ff18a9e8)
-rw-r--r-- | package-lock.json | 6 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | routers/web/admin/admin.go | 26 | ||||
-rw-r--r-- | routers/web/web.go | 1 | ||||
-rw-r--r-- | templates/admin/dashboard.tmpl | 66 | ||||
-rw-r--r-- | templates/admin/system_status.tmpl | 62 | ||||
-rw-r--r-- | templates/base/head.tmpl | 2 | ||||
-rw-r--r-- | web_src/js/htmx.js | 3 | ||||
-rw-r--r-- | webpack.config.js | 4 |
9 files changed, 97 insertions, 74 deletions
diff --git a/package-lock.json b/package-lock.json index 48da8124e0..13f03b8d28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "escape-goat": "4.0.0", "fast-glob": "3.3.2", "htmx.org": "1.9.10", + "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", @@ -6174,6 +6175,11 @@ "postcss": "^8.1.0" } }, + "node_modules/idiomorph": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/idiomorph/-/idiomorph-0.3.0.tgz", + "integrity": "sha512-UhV1Ey5xCxIwR9B+OgIjQa+1Jx99XQ1vQHUsKBU1RpQzCx1u+b+N6SOXgf5mEJDqemUI/ffccu6+71l2mJUsRA==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", diff --git a/package.json b/package.json index ac79741711..3d753a567c 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "escape-goat": "4.0.0", "fast-glob": "3.3.2", "htmx.org": "1.9.10", + "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index d31cb1cd25..9fbd429f74 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -28,13 +28,14 @@ import ( ) const ( - tplDashboard base.TplName = "admin/dashboard" - tplSelfCheck base.TplName = "admin/self_check" - tplCron base.TplName = "admin/cron" - tplQueue base.TplName = "admin/queue" - tplStacktrace base.TplName = "admin/stacktrace" - tplQueueManage base.TplName = "admin/queue_manage" - tplStats base.TplName = "admin/stats" + tplDashboard base.TplName = "admin/dashboard" + tplSystemStatus base.TplName = "admin/system_status" + tplSelfCheck base.TplName = "admin/self_check" + tplCron base.TplName = "admin/cron" + tplQueue base.TplName = "admin/queue" + tplStacktrace base.TplName = "admin/stacktrace" + tplQueueManage base.TplName = "admin/queue_manage" + tplStats base.TplName = "admin/stats" ) var sysStatus struct { @@ -72,7 +73,7 @@ var sysStatus struct { // Garbage collector statistics. NextGC string // next run in HeapAlloc time (bytes) - LastGC string // last run in absolute time (ns) + LastGCTime string // last run time PauseTotalNs string PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] NumGC uint32 @@ -110,7 +111,7 @@ func updateSystemStatus() { sysStatus.OtherSys = base.FileSize(int64(m.OtherSys)) sysStatus.NextGC = base.FileSize(int64(m.NextGC)) - sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000) + sysStatus.LastGCTime = time.Unix(0, int64(m.LastGC)).Format(time.RFC3339) sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000) sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000) sysStatus.NumGC = m.NumGC @@ -132,7 +133,6 @@ func Dashboard(ctx *context.Context) { ctx.Data["PageIsAdminDashboard"] = true ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate(ctx) ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion(ctx) - // FIXME: update periodically updateSystemStatus() ctx.Data["SysStatus"] = sysStatus ctx.Data["SSH"] = setting.SSH @@ -140,6 +140,12 @@ func Dashboard(ctx *context.Context) { ctx.HTML(http.StatusOK, tplDashboard) } +func SystemStatus(ctx *context.Context) { + updateSystemStatus() + ctx.Data["SysStatus"] = sysStatus + ctx.HTML(http.StatusOK, tplSystemStatus) +} + // DashboardPost run an admin operation func DashboardPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AdminDashboardForm) diff --git a/routers/web/web.go b/routers/web/web.go index cdec6759fd..400bed9288 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -681,6 +681,7 @@ func registerRoutes(m *web.Route) { // ***** START: Admin ***** m.Group("/admin", func() { m.Get("", admin.Dashboard) + m.Get("/system_status", admin.SystemStatus) m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost) if setting.Database.Type.IsMySQL() || setting.Database.Type.IsMSSQL() { diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index f43b4c5385..8088315f17 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -75,69 +75,9 @@ <h4 class="ui top attached header"> {{ctx.Locale.Tr "admin.dashboard.system_status"}} </h4> - <div class="ui attached table segment"> - <dl class="admin-dl-horizontal"> - <dt>{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}</dt> - <dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}</dt> - <dd>{{.SysStatus.NumGoroutine}}</dd> - <div class="divider"></div> - <dt>{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}</dt> - <dd>{{.SysStatus.MemAllocated}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}</dt> - <dd>{{.SysStatus.MemTotal}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}</dt> - <dd>{{.SysStatus.MemSys}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}</dt> - <dd>{{.SysStatus.Lookups}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}</dt> - <dd>{{.SysStatus.MemMallocs}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}</dt> - <dd>{{.SysStatus.MemFrees}}</dd> - <div class="divider"></div> - <dt>{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}</dt> - <dd>{{.SysStatus.HeapAlloc}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt> - <dd>{{.SysStatus.HeapSys}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}</dt> - <dd>{{.SysStatus.HeapIdle}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}</dt> - <dd>{{.SysStatus.HeapInuse}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}</dt> - <dd>{{.SysStatus.HeapReleased}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}</dt> - <dd>{{.SysStatus.HeapObjects}}</dd> - <div class="divider"></div> - <dt>{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt> - <dd>{{.SysStatus.StackInuse}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt> - <dd>{{.SysStatus.StackSys}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}</dt> - <dd>{{.SysStatus.MSpanInuse}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}</dt> - <dd>{{.SysStatus.MSpanSys}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}</dt> - <dd>{{.SysStatus.MCacheInuse}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}</dt> - <dd>{{.SysStatus.MCacheSys}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}</dt> - <dd>{{.SysStatus.BuckHashSys}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}</dt> - <dd>{{.SysStatus.GCSys}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt> - <dd>{{.SysStatus.OtherSys}}</dd> - <div class="divider"></div> - <dt>{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}</dt> - <dd>{{.SysStatus.NextGC}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}</dt> - <dd>{{.SysStatus.LastGC}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}</dt> - <dd>{{.SysStatus.PauseTotalNs}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}</dt> - <dd>{{.SysStatus.PauseNs}}</dd> - <dt>{{ctx.Locale.Tr "admin.dashboard.gc_times"}}</dt> - <dd>{{.SysStatus.NumGC}}</dd> - </dl> + {{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}} + <div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".divider" class="ui attached table segment"> + {{template "admin/system_status" .}} </div> </div> {{template "admin/layout_footer" .}} diff --git a/templates/admin/system_status.tmpl b/templates/admin/system_status.tmpl new file mode 100644 index 0000000000..7b5c9be6cc --- /dev/null +++ b/templates/admin/system_status.tmpl @@ -0,0 +1,62 @@ +<dl class="admin-dl-horizontal"> + <dt>{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}</dt> + <dd><relative-time format="duration" datetime="{{.SysStatus.StartTime}}">{{.SysStatus.StartTime}}</relative-time></dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}</dt> + <dd>{{.SysStatus.NumGoroutine}}</dd> + <div class="divider"></div> + <dt>{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}</dt> + <dd>{{.SysStatus.MemAllocated}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}</dt> + <dd>{{.SysStatus.MemTotal}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}</dt> + <dd>{{.SysStatus.MemSys}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}</dt> + <dd>{{.SysStatus.Lookups}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}</dt> + <dd>{{.SysStatus.MemMallocs}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}</dt> + <dd>{{.SysStatus.MemFrees}}</dd> + <div class="divider"></div> + <dt>{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}</dt> + <dd>{{.SysStatus.HeapAlloc}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}</dt> + <dd>{{.SysStatus.HeapSys}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}</dt> + <dd>{{.SysStatus.HeapIdle}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}</dt> + <dd>{{.SysStatus.HeapInuse}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}</dt> + <dd>{{.SysStatus.HeapReleased}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}</dt> + <dd>{{.SysStatus.HeapObjects}}</dd> + <div class="divider"></div> + <dt>{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}</dt> + <dd>{{.SysStatus.StackInuse}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}</dt> + <dd>{{.SysStatus.StackSys}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}</dt> + <dd>{{.SysStatus.MSpanInuse}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}</dt> + <dd>{{.SysStatus.MSpanSys}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}</dt> + <dd>{{.SysStatus.MCacheInuse}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}</dt> + <dd>{{.SysStatus.MCacheSys}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}</dt> + <dd>{{.SysStatus.BuckHashSys}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}</dt> + <dd>{{.SysStatus.GCSys}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}</dt> + <dd>{{.SysStatus.OtherSys}}</dd> + <div class="divider"></div> + <dt>{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}</dt> + <dd>{{.SysStatus.NextGC}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}</dt> + <dd><relative-time format="duration" datetime="{{.SysStatus.LastGCTime}}">{{.SysStatus.LastGCTime}}</relative-time></dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}</dt> + <dd>{{.SysStatus.PauseTotalNs}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}</dt> + <dd>{{.SysStatus.PauseNs}}</dd> + <dt>{{ctx.Locale.Tr "admin.dashboard.gc_times"}}</dt> + <dd>{{.SysStatus.NumGC}}</dd> +</dl> diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 21addc7176..d865d58b8e 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -30,7 +30,7 @@ {{template "base/head_style" .}} {{template "custom/header" .}} </head> -<body hx-headers='{"x-csrf-token": "{{.CsrfToken}}"}' hx-swap="outerHTML" hx-push-url="false"> +<body hx-headers='{"x-csrf-token": "{{.CsrfToken}}"}' hx-swap="outerHTML" hx-ext="morph" hx-push-url="false"> {{ctx.DataRaceCheck $.Context}} {{template "custom/body_outer_pre" .}} diff --git a/web_src/js/htmx.js b/web_src/js/htmx.js index 92400d1cbe..5ca3018308 100644 --- a/web_src/js/htmx.js +++ b/web_src/js/htmx.js @@ -1,6 +1,9 @@ import * as htmx from 'htmx.org'; import {showErrorToast} from './modules/toast.js'; +// https://github.com/bigskysoftware/idiomorph#htmx +import 'idiomorph/dist/idiomorph-ext.js'; + // https://htmx.org/reference/#config htmx.config.requestClass = 'is-loading'; htmx.config.scrollIntoViewOnBoost = false; diff --git a/webpack.config.js b/webpack.config.js index 066c26a686..d4700ebe2b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -173,6 +173,9 @@ export default { ], }, plugins: [ + new webpack.ProvidePlugin({ // for htmx extensions + htmx: 'htmx.org', + }), new DefinePlugin({ __VUE_OPTIONS_API__: true, // at the moment, many Vue components still use the Vue Options API __VUE_PROD_DEVTOOLS__: false, // do not enable devtools support in production @@ -211,6 +214,7 @@ export default { override: { 'khroma@*': {licenseName: 'MIT'}, // https://github.com/fabiospampinato/khroma/pull/33 'htmx.org@1.9.10': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" + 'idiomorph@0.3.0': {licenseName: 'BSD-2-Clause'}, // "BSD 2-Clause" -> "BSD-2-Clause" }, emitError: true, allow: '(Apache-2.0 OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)', |