aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/html/pages/start.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenserver/frontend/html/pages/start.js')
-rw-r--r--src/zenserver/frontend/html/pages/start.js281
1 files changed, 177 insertions, 104 deletions
diff --git a/src/zenserver/frontend/html/pages/start.js b/src/zenserver/frontend/html/pages/start.js
index df70ea2f4..d06040b2f 100644
--- a/src/zenserver/frontend/html/pages/start.js
+++ b/src/zenserver/frontend/html/pages/start.js
@@ -6,7 +6,7 @@ import { ZenPage } from "./page.js"
import { Fetcher } from "../util/fetcher.js"
import { Friendly } from "../util/friendly.js"
import { Modal } from "../util/modal.js"
-import { Table, Toolbar } from "../util/widgets.js"
+import { Table, Toolbar, Pager } from "../util/widgets.js"
////////////////////////////////////////////////////////////////////////////////
export class Page extends ZenPage
@@ -36,59 +36,54 @@ export class Page extends ZenPage
all_stats[provider] = await new Fetcher().resource("stats", provider).json();
}));
+ this._http_panel = section.tag().classify("card").classify("stats-tile").classify("stats-http-panel");
+ this._http_panel.inner().addEventListener("click", () => { window.location = "?page=metrics"; });
+ this._http_panel.tag().classify("http-title").text("HTTP");
+ const req_section = this._http_panel.tag().classify("http-section");
+ req_section.tag().classify("http-section-label").text("Requests");
+ this._http_req_metrics = req_section.tag().classify("tile-metrics");
+ const ws_section = this._http_panel.tag().classify("http-section");
+ ws_section.tag().classify("http-section-label").text("Websockets");
+ this._http_ws_metrics = ws_section.tag().classify("tile-metrics");
this._stats_grid = section.tag().classify("grid").classify("stats-tiles");
this._safe_lookup = safe_lookup;
this._render_stats(all_stats);
// project list
- var project_table = null;
if (available.has("/prj/"))
{
var section = this.add_section("Cooked Projects");
- section.tag().classify("dropall").text("drop-all").on_click(() => this.drop_all("projects"));
-
var columns = [
"name",
"project_dir",
"engine_dir",
"actions",
];
- project_table = section.add_widget(Table, columns);
-
- var projects = await new Fetcher().resource("/prj/list").json();
- projects.sort((a, b) => (b.LastAccessTime || 0) - (a.LastAccessTime || 0));
- projects = projects.slice(0, 25);
- projects.sort((a, b) => a.Id.localeCompare(b.Id));
-
- for (const project of projects)
- {
- var row = project_table.add_row(
- "",
- project.ProjectRootDir,
- project.EngineRootDir,
- );
-
- var cell = row.get_cell(0);
- cell.tag().text(project.Id).on_click((x) => this.view_project(x), project.Id);
-
- var cell = row.get_cell(-1);
- var action_tb = new Toolbar(cell, true);
- action_tb.left().add("view").on_click((x) => this.view_project(x), project.Id);
- action_tb.left().add("drop").on_click((x) => this.drop_project(x), project.Id);
-
- row.attr("zs_name", project.Id);
- }
+ this._project_table = section.add_widget(Table, columns);
+
+ this._project_pager = new Pager(section, 25, () => this._render_projects_page(),
+ Pager.make_search_fn(() => this._projects_data, p => p.Id));
+ const drop_link = document.createElement("span");
+ drop_link.className = "dropall zen_action";
+ drop_link.style.position = "static";
+ drop_link.textContent = "drop-all";
+ drop_link.addEventListener("click", () => this.drop_all("projects"));
+ this._project_pager.prepend(drop_link);
+
+ const prj_loading = Pager.loading(section);
+ this._projects_data = await new Fetcher().resource("/prj/list").json();
+ this._projects_data.sort((a, b) => a.Id.localeCompare(b.Id));
+ this._project_pager.set_total(this._projects_data.length);
+ this._render_projects_page();
+ prj_loading.remove();
}
// cache
- var cache_table = null;
if (available.has("/z$/"))
{
var section = this.add_section("Cache");
- section.tag().classify("dropall").text("drop-all").on_click(() => this.drop_all("z$"));
-
var columns = [
"namespace",
"dir",
@@ -98,31 +93,30 @@ export class Page extends ZenPage
"size mem",
"actions",
];
- var zcache_info = await new Fetcher().resource("/z$/").json();
- cache_table = section.add_widget(Table, columns, Table.Flag_FitLeft|Table.Flag_PackRight);
- for (const namespace of zcache_info["Namespaces"] || [])
- {
- new Fetcher().resource(`/z$/${namespace}/`).json().then((data) => {
- const row = cache_table.add_row(
- "",
- data["Configuration"]["RootDir"],
- data["Buckets"].length,
- data["EntryCount"],
- Friendly.bytes(data["StorageSize"].DiskSize),
- Friendly.bytes(data["StorageSize"].MemorySize)
- );
- var cell = row.get_cell(0);
- cell.tag().text(namespace).on_click(() => this.view_zcache(namespace));
- row.get_cell(1).tag().text(namespace);
-
- cell = row.get_cell(-1);
- const action_tb = new Toolbar(cell, true);
- action_tb.left().add("view").on_click(() => this.view_zcache(namespace));
- action_tb.left().add("drop").on_click(() => this.drop_zcache(namespace));
-
- row.attr("zs_name", namespace);
- });
- }
+ this._cache_table = section.add_widget(Table, columns, Table.Flag_FitLeft|Table.Flag_PackRight);
+
+ this._cache_pager = new Pager(section, 25, () => this._render_cache_page(),
+ Pager.make_search_fn(() => this._cache_data, item => item.namespace));
+ const cache_drop_link = document.createElement("span");
+ cache_drop_link.className = "dropall zen_action";
+ cache_drop_link.style.position = "static";
+ cache_drop_link.textContent = "drop-all";
+ cache_drop_link.addEventListener("click", () => this.drop_all("z$"));
+ this._cache_pager.prepend(cache_drop_link);
+
+ const cache_loading = Pager.loading(section);
+ const zcache_info = await new Fetcher().resource("/z$/").json();
+ const namespaces = zcache_info["Namespaces"] || [];
+ const results = await Promise.allSettled(
+ namespaces.map(ns => new Fetcher().resource(`/z$/${ns}/`).json().then(data => ({ namespace: ns, data })))
+ );
+ this._cache_data = results
+ .filter(r => r.status === "fulfilled")
+ .map(r => r.value)
+ .sort((a, b) => a.namespace.localeCompare(b.namespace));
+ this._cache_pager.set_total(this._cache_data.length);
+ this._render_cache_page();
+ cache_loading.remove();
}
// version
@@ -131,57 +125,54 @@ export class Page extends ZenPage
version.param("detailed", "true");
version.text().then((data) => ver_tag.text(data));
- this._project_table = project_table;
- this._cache_table = cache_table;
-
// WebSocket for live stats updates
this.connect_stats_ws((all_stats) => this._render_stats(all_stats));
}
_render_stats(all_stats)
{
+ all_stats = this._merge_last_stats(all_stats);
const grid = this._stats_grid;
const safe_lookup = this._safe_lookup;
- // Clear existing tiles
+ // Clear and repopulate service tiles grid
grid.inner().innerHTML = "";
- // HTTP tile — aggregate request stats across all providers
- {
- const tile = grid.tag().classify("card").classify("stats-tile");
- tile.tag().classify("card-title").text("HTTP");
- const columns = tile.tag().classify("tile-columns");
+ // HTTP panel — update metrics containers built once in main()
+ const left = this._http_req_metrics;
+ left.inner().innerHTML = "";
- // Left column: request stats
- const left = columns.tag().classify("tile-metrics");
-
- let total_requests = 0;
- let total_rate = 0;
- for (const p in all_stats)
- {
- total_requests += (safe_lookup(all_stats[p], "requests.count") || 0);
- total_rate += (safe_lookup(all_stats[p], "requests.rate_1") || 0);
- }
-
- this._add_tile_metric(left, Friendly.sep(total_requests), "total requests", true);
- if (total_rate > 0)
- this._add_tile_metric(left, Friendly.sep(total_rate, 1) + "/s", "req/sec (1m)");
+ let total_requests = 0;
+ let total_rate = 0;
+ for (const p in all_stats)
+ {
+ total_requests += (safe_lookup(all_stats[p], "requests.count") || 0);
+ total_rate += (safe_lookup(all_stats[p], "requests.rate_1") || 0);
+ }
- // Right column: websocket stats
- const ws = all_stats["http"] ? (all_stats["http"]["websockets"] || {}) : {};
- const right = columns.tag().classify("tile-metrics");
+ this._add_tile_metric(left, Friendly.sep(total_requests), "total requests", true);
+ if (total_rate > 0)
+ {
+ this._add_tile_metric(left, Friendly.sep(total_rate, 1) + "/s", "req/sec (1m)");
+ }
- this._add_tile_metric(right, Friendly.sep(ws.active_connections || 0), "ws connections", true);
- const ws_frames = (ws.frames_received || 0) + (ws.frames_sent || 0);
- if (ws_frames > 0)
- this._add_tile_metric(right, Friendly.sep(ws_frames), "ws frames");
- const ws_bytes = (ws.bytes_received || 0) + (ws.bytes_sent || 0);
- if (ws_bytes > 0)
- this._add_tile_metric(right, Friendly.bytes(ws_bytes), "ws traffic");
+ const right = this._http_ws_metrics;
+ right.inner().innerHTML = "";
- tile.on_click(() => { window.location = "?page=metrics"; });
+ const ws = all_stats["http"] ? (all_stats["http"]["websockets"] || {}) : {};
+ this._add_tile_metric(right, Friendly.sep(ws.active_connections || 0), "ws connections", true);
+ const ws_frames = (ws.frames_received || 0) + (ws.frames_sent || 0);
+ if (ws_frames > 0)
+ {
+ this._add_tile_metric(right, Friendly.sep(ws_frames), "ws frames");
+ }
+ const ws_bytes = (ws.bytes_received || 0) + (ws.bytes_sent || 0);
+ if (ws_bytes > 0)
+ {
+ this._add_tile_metric(right, Friendly.bytes(ws_bytes), "ws traffic");
}
+
// Cache tile (z$)
if (all_stats["z$"])
{
@@ -198,7 +189,7 @@ export class Page extends ZenPage
this._add_tile_metric(body, safe_lookup(s, "cache.size.disk", Friendly.bytes) || "-", "disk");
this._add_tile_metric(body, safe_lookup(s, "cache.size.memory", Friendly.bytes) || "-", "memory");
- tile.on_click(() => { window.location = "?page=stat&provider=z$"; });
+ tile.inner().addEventListener("click", () => { window.location = "?page=stat&provider=z$"; });
}
// Project Store tile (prj)
@@ -210,9 +201,9 @@ export class Page extends ZenPage
const body = tile.tag().classify("tile-metrics");
this._add_tile_metric(body, safe_lookup(s, "requests.count", Friendly.sep) || "-", "requests", true);
- this._add_tile_metric(body, safe_lookup(s, "store.size.disk", Friendly.bytes) || "-", "disk");
+ this._add_tile_metric(body, safe_lookup(s, "project_count", Friendly.sep) || "-", "projects");
- tile.on_click(() => { window.location = "?page=stat&provider=prj"; });
+ tile.inner().addEventListener("click", () => { window.location = "?page=stat&provider=prj"; });
}
// Build Store tile (builds)
@@ -226,7 +217,7 @@ export class Page extends ZenPage
this._add_tile_metric(body, safe_lookup(s, "requests.count", Friendly.sep) || "-", "requests", true);
this._add_tile_metric(body, safe_lookup(s, "store.size.disk", Friendly.bytes) || "-", "disk");
- tile.on_click(() => { window.location = "?page=stat&provider=builds"; });
+ tile.inner().addEventListener("click", () => { window.location = "?page=builds"; });
}
// Proxy tile
@@ -250,7 +241,37 @@ export class Page extends ZenPage
this._add_tile_metric(body, Friendly.sep(mappings.length), "mappings");
this._add_tile_metric(body, Friendly.bytes(totalBytes), "traffic");
- tile.on_click(() => { window.location = "?page=proxy"; });
+ tile.inner().addEventListener("click", () => { window.location = "?page=proxy"; });
+ }
+
+ // Hub tile
+ if (all_stats["hub"])
+ {
+ const s = all_stats["hub"];
+ const tile = grid.tag().classify("card").classify("stats-tile");
+ tile.tag().classify("card-title").text("Hub");
+ const body = tile.tag().classify("tile-metrics");
+
+ const current = safe_lookup(s, "currentInstanceCount") || 0;
+ const limit = safe_lookup(s, "instanceLimit") || safe_lookup(s, "maxInstanceCount") || 0;
+ this._add_tile_metric(body, `${current} / ${limit}`, "instances", true);
+ this._add_tile_metric(body, safe_lookup(s, "requests.count", Friendly.sep) || "-", "requests");
+
+ tile.inner().addEventListener("click", () => { window.location = "?page=stat&provider=hub"; });
+ }
+
+ // Object Store tile (obj)
+ if (all_stats["obj"])
+ {
+ const s = all_stats["obj"];
+ const tile = grid.tag().classify("card").classify("stats-tile");
+ tile.tag().classify("card-title").text("Object Store");
+ const body = tile.tag().classify("tile-metrics");
+
+ this._add_tile_metric(body, safe_lookup(s, "requests.count", Friendly.sep) || "-", "requests", true);
+ this._add_tile_metric(body, safe_lookup(s, "total_bytes_served", Friendly.bytes) || "-", "bytes served");
+
+ tile.inner().addEventListener("click", () => { window.location = "?page=stat&provider=obj"; });
}
// Workspace tile (ws)
@@ -262,9 +283,9 @@ export class Page extends ZenPage
const body = tile.tag().classify("tile-metrics");
this._add_tile_metric(body, safe_lookup(s, "requests.count", Friendly.sep) || "-", "requests", true);
- this._add_tile_metric(body, safe_lookup(s, "workspaces.filescount", Friendly.sep) || "-", "files");
+ this._add_tile_metric(body, safe_lookup(s, "workspaces", Friendly.sep) || "-", "workspaces");
- tile.on_click(() => { window.location = "?page=stat&provider=ws"; });
+ tile.inner().addEventListener("click", () => { window.location = "?page=stat&provider=ws"; });
}
}
@@ -279,6 +300,60 @@ export class Page extends ZenPage
m.tag().classify("metric-label").text(label);
}
+ _render_projects_page()
+ {
+ const { start, end } = this._project_pager.page_range();
+ this._project_table.clear(start);
+ for (let i = start; i < end; i++)
+ {
+ const project = this._projects_data[i];
+ const row = this._project_table.add_row(
+ "",
+ project.ProjectRootDir,
+ project.EngineRootDir,
+ );
+
+ const cell = row.get_cell(0);
+ cell.tag().text(project.Id).on_click((x) => this.view_project(x), project.Id);
+
+ const action_cell = row.get_cell(-1);
+ const action_tb = new Toolbar(action_cell, true);
+ action_tb.left().add("view").on_click((x) => this.view_project(x), project.Id);
+ action_tb.left().add("drop").on_click((x) => this.drop_project(x), project.Id);
+
+ row.attr("zs_name", project.Id);
+ }
+ }
+
+ _render_cache_page()
+ {
+ const { start, end } = this._cache_pager.page_range();
+ this._cache_table.clear(start);
+ for (let i = start; i < end; i++)
+ {
+ const item = this._cache_data[i];
+ const data = item.data;
+ const row = this._cache_table.add_row(
+ "",
+ data["Configuration"]["RootDir"],
+ data["Buckets"].length,
+ data["EntryCount"],
+ Friendly.bytes(data["StorageSize"].DiskSize),
+ Friendly.bytes(data["StorageSize"].MemorySize)
+ );
+
+ const cell = row.get_cell(0);
+ cell.tag().text(item.namespace).on_click(() => this.view_zcache(item.namespace));
+
+ const action_cell = row.get_cell(-1);
+ const action_tb = new Toolbar(action_cell, true);
+ action_tb.left().add("view").on_click(() => this.view_zcache(item.namespace));
+ action_tb.left().add("drop").on_click(() => this.drop_zcache(item.namespace));
+
+ row.attr("zs_name", item.namespace);
+ }
+ }
+
view_stat(provider)
{
window.location = "?page=stat&provider=" + provider;
@@ -324,20 +399,18 @@ export class Page extends ZenPage
async drop_all_projects()
{
- for (const row of this._project_table)
+ for (const project of this._projects_data || [])
{
- const project_id = row.attr("zs_name");
- await new Fetcher().resource("prj", project_id).delete();
+ await new Fetcher().resource("prj", project.Id).delete();
}
this.reload();
}
async drop_all_zcache()
{
- for (const row of this._cache_table)
+ for (const item of this._cache_data || [])
{
- const namespace = row.attr("zs_name");
- await new Fetcher().resource("z$", namespace).delete();
+ await new Fetcher().resource("z$", item.namespace).delete();
}
this.reload();
}