diff options
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | src/zenserver/frontend/html/pages/hub.js | 21 | ||||
| -rw-r--r-- | src/zenserver/frontend/html/zen.css | 5 | ||||
| -rw-r--r-- | src/zenserver/hub/httphubservice.cpp | 1 | ||||
| -rw-r--r-- | src/zenserver/hub/hub.cpp | 2 | ||||
| -rw-r--r-- | src/zenserver/hub/hub.h | 1 |
6 files changed, 29 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 32b9e7255..54c7ba0de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,7 +51,9 @@ - Improvement: `zen up` refactored to use shared `StartupZenServer`/`ShutdownZenServer` helpers (also used by `zen hub up`/`zen hub down`) - Improvement: Hub dashboard module list improved - Animated state dots, with flashing transitions for hibernating, waking, provisioning, and deprovisioning + - Per-row port used by the provisioned instance - Per-row hibernate, wake, and deprovision actions with confirmation for destructive operations + - Per-row button to open the instance dashboard in a new window - Multi-select with bulk hibernate/wake/deprovision and select-all - Pagination at 50 lines per page - Inline fold-out panel per row with human-readable process metrics diff --git a/src/zenserver/frontend/html/pages/hub.js b/src/zenserver/frontend/html/pages/hub.js index a226f0924..3a5b67483 100644 --- a/src/zenserver/frontend/html/pages/hub.js +++ b/src/zenserver/frontend/html/pages/hub.js @@ -114,6 +114,7 @@ export class Page extends ZenPage // Module table const table = document.createElement("table"); + table.className = "module-table"; const thead = document.createElement("thead"); const hrow = document.createElement("tr"); @@ -125,7 +126,7 @@ export class Page extends ZenPage th_check.appendChild(this._select_all_cb); hrow.appendChild(th_check); - for (const label of ["#", "MODULE ID", "STATUS", "ACTIONS"]) + for (const label of ["#", "MODULE ID", "STATUS", "PORT", "ACTIONS"]) { const th = document.createElement("th"); th.textContent = label; @@ -279,6 +280,8 @@ export class Page extends ZenPage row.dot.removeAttribute("data-prev-state"); } row.state_text.nodeValue = state; + row.port_text.nodeValue = m.port ? String(m.port) : ""; + row.btn_open.disabled = state !== "provisioned"; row.btn_hibernate.disabled = !_btn_enabled(state, "hibernate"); row.btn_wake.disabled = !_btn_enabled(state, "wake"); row.btn_deprov.disabled = !_btn_enabled(state, "deprovision"); @@ -357,8 +360,19 @@ export class Page extends ZenPage td_status.appendChild(state_node); tr.appendChild(td_status); + const port = m.port || 0; + const td_port = document.createElement("td"); + td_port.style.cssText = "font-variant-numeric:tabular-nums;"; + const port_node = document.createTextNode(port ? String(port) : ""); + td_port.appendChild(port_node); + tr.appendChild(td_port); + const td_action = document.createElement("td"); td_action.className = "module-action-cell"; + const [wrap_o, btn_o] = _make_action_btn("\u2197", "Open dashboard", () => { + window.open(`${window.location.protocol}//${window.location.hostname}:${port}`, "_blank"); + }); + btn_o.disabled = state !== "provisioned"; const [wrap_h, btn_h] = _make_action_btn("\u23F8", "Hibernate", () => this._post_module_action(id, "hibernate").then(() => this._update())); const [wrap_w, btn_w] = _make_action_btn("\u25B6", "Wake", () => this._post_module_action(id, "wake").then(() => this._update())); const [wrap_d, btn_d] = _make_action_btn("\u2715", "Deprovision", () => this._confirm_deprovision([id])); @@ -368,6 +382,7 @@ export class Page extends ZenPage td_action.appendChild(wrap_h); td_action.appendChild(wrap_w); td_action.appendChild(wrap_d); + td_action.appendChild(wrap_o); tr.appendChild(td_action); // Build metrics grid from process_metrics keys. @@ -375,7 +390,7 @@ export class Page extends ZenPage // top-to-bottom in the left column before continuing in the right column. const metric_nodes = new Map(); const metrics_td = document.createElement("td"); - metrics_td.colSpan = 5; + metrics_td.colSpan = 6; const metrics_grid = document.createElement("div"); metrics_grid.className = "module-metrics-grid"; const keys = Object.keys(m.process_metrics || {}); @@ -405,7 +420,7 @@ export class Page extends ZenPage metrics_td.appendChild(metrics_grid); metrics_tr.appendChild(metrics_td); - row = { tr, metrics_tr, idx: td_idx, cb, dot, state_text: state_node, btn_expand, btn_hibernate: btn_h, btn_wake: btn_w, btn_deprov: btn_d, metric_nodes }; + row = { tr, metrics_tr, idx: td_idx, cb, dot, state_text: state_node, port_text: port_node, btn_expand, btn_open: btn_o, btn_hibernate: btn_h, btn_wake: btn_w, btn_deprov: btn_d, metric_nodes }; this._row_cache.set(id, row); } diff --git a/src/zenserver/frontend/html/zen.css b/src/zenserver/frontend/html/zen.css index c47b35904..5ce60d2d2 100644 --- a/src/zenserver/frontend/html/zen.css +++ b/src/zenserver/frontend/html/zen.css @@ -1253,6 +1253,11 @@ tr:last-child td { text-align: center; } +.module-table td, .module-table th { + padding-top: 4px; + padding-bottom: 4px; +} + .module-expand-btn { background: transparent; border: none; diff --git a/src/zenserver/hub/httphubservice.cpp b/src/zenserver/hub/httphubservice.cpp index 03be6e85d..a91e36128 100644 --- a/src/zenserver/hub/httphubservice.cpp +++ b/src/zenserver/hub/httphubservice.cpp @@ -42,6 +42,7 @@ HttpHubService::HttpHubService(Hub& Hub) : m_Hub(Hub) { Obj << "moduleId" << ModuleId; Obj << "state" << ToString(Info.State); + Obj << "port" << Info.Port; Obj.BeginObject("process_metrics"); { Obj << "MemoryBytes" << Info.Metrics.MemoryBytes; diff --git a/src/zenserver/hub/hub.cpp b/src/zenserver/hub/hub.cpp index 54f45e511..ebbb9432a 100644 --- a/src/zenserver/hub/hub.cpp +++ b/src/zenserver/hub/hub.cpp @@ -605,6 +605,7 @@ Hub::Find(std::string_view ModuleId, InstanceInfo* OutInstanceInfo) std::chrono::system_clock::now() // TODO }; Instance->GetProcessMetrics(Info.Metrics); + Info.Port = Instance->GetBasePort(); *OutInstanceInfo = Info; } @@ -628,6 +629,7 @@ Hub::EnumerateModules(std::function<void(std::string_view ModuleId, const Instan std::chrono::system_clock::now() // TODO }; Instance->GetProcessMetrics(Info.Metrics); + Info.Port = Instance->GetBasePort(); Infos.push_back(std::make_pair(std::string(Instance->GetModuleId()), Info)); } diff --git a/src/zenserver/hub/hub.h b/src/zenserver/hub/hub.h index 9a84f7744..8c4039c38 100644 --- a/src/zenserver/hub/hub.h +++ b/src/zenserver/hub/hub.h @@ -69,6 +69,7 @@ public: HubInstanceState State = HubInstanceState::Unprovisioned; std::chrono::system_clock::time_point ProvisionTime; ProcessMetrics Metrics; + uint16_t Port = 0; }; /** |