diff options
Diffstat (limited to 'src/zenserver/frontend/html/pages/hub.js')
| -rw-r--r-- | src/zenserver/frontend/html/pages/hub.js | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/zenserver/frontend/html/pages/hub.js b/src/zenserver/frontend/html/pages/hub.js new file mode 100644 index 000000000..f9e4fff33 --- /dev/null +++ b/src/zenserver/frontend/html/pages/hub.js @@ -0,0 +1,122 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +"use strict"; + +import { ZenPage } from "./page.js" +import { Fetcher } from "../util/fetcher.js" +import { Friendly } from "../util/friendly.js" +import { Table } from "../util/widgets.js" + +//////////////////////////////////////////////////////////////////////////////// +export class Page extends ZenPage +{ + async main() + { + this.set_title("hub"); + + // Capacity + const stats_section = this.add_section("Capacity"); + this._stats_grid = stats_section.tag().classify("grid").classify("stats-tiles"); + + // Modules + const mod_section = this.add_section("Modules"); + this._mod_host = mod_section; + this._mod_table = null; + + await this._update(); + this._poll_timer = setInterval(() => this._update(), 2000); + } + + async _update() + { + try + { + const [stats, status] = await Promise.all([ + new Fetcher().resource("/hub/stats").json(), + new Fetcher().resource("/hub/status").json(), + ]); + + this._render_capacity(stats); + this._render_modules(status); + } + catch (e) { /* service unavailable */ } + } + + _render_capacity(data) + { + const grid = this._stats_grid; + grid.inner().innerHTML = ""; + + const current = data.currentInstanceCount || 0; + const max = data.maxInstanceCount || 0; + const limit = data.instanceLimit || 0; + + { + const tile = grid.tag().classify("card").classify("stats-tile"); + tile.tag().classify("card-title").text("Active Modules"); + const body = tile.tag().classify("tile-metrics"); + this._metric(body, Friendly.sep(current), "currently provisioned", true); + } + + { + const tile = grid.tag().classify("card").classify("stats-tile"); + tile.tag().classify("card-title").text("Peak Modules"); + const body = tile.tag().classify("tile-metrics"); + this._metric(body, Friendly.sep(max), "high watermark", true); + } + + { + const tile = grid.tag().classify("card").classify("stats-tile"); + tile.tag().classify("card-title").text("Instance Limit"); + const body = tile.tag().classify("tile-metrics"); + this._metric(body, Friendly.sep(limit), "maximum allowed", true); + if (limit > 0) + { + const pct = ((current / limit) * 100).toFixed(0) + "%"; + this._metric(body, pct, "utilization"); + } + } + } + + _render_modules(data) + { + const modules = data.modules || []; + + if (this._mod_table) + { + this._mod_table.clear(); + } + else + { + this._mod_table = this._mod_host.add_widget( + Table, + ["module ID", "status"], + Table.Flag_FitLeft|Table.Flag_PackRight + ); + } + + if (modules.length === 0) + { + return; + } + + for (const m of modules) + { + this._mod_table.add_row( + m.moduleId || "", + m.provisioned ? "provisioned" : "inactive", + ); + } + } + + _metric(parent, value, label, hero = false) + { + const m = parent.tag().classify("tile-metric"); + if (hero) + { + m.classify("tile-metric-hero"); + } + m.tag().classify("metric-value").text(value); + m.tag().classify("metric-label").text(label); + } +} |