/*
* SysDeck - Monitoring Panel (v0.0.43)
* Author: Jeremy Anderson (https://dcos.net)
*
* Shared tabbed module hosting Prometheus + Grafana — the two
* observability stack components. Per user directive: "we have 2
* modules left, we can actually have them share a module with tabs
* similar to the container/vm module. we should add prometheus, and
* graphana webui modules."
*
* The panel has two tabs:
* 1. Prometheus — status card (version, uptime, targets, alerts) +
* iframe of the real Prometheus web UI at http://127.0.0.1:9095
* 2. Grafana — status card (version, dashboards, datasources) +
* iframe of the real Grafana web UI at http://127.0.0.1:3000
*
* Both tabs back their data with REAL bridge helpers (bridge/prometheus.py
* and bridge/grafana.py) that call the actual HTTP APIs. No mock data.
* The iframes load the real web UIs directly — same pattern as the
* v0.0.34 Glances integration.
*
* v0.0.43 hardening applied to both bridge helpers:
* - NoRedirectHandler on all HTTP calls (SSRF defense, CVE-2020-35850)
* - 127.0.0.1-only URL check (SSRF defense)
* - Env scrubbed on every subprocess (CVE-2024-6126)
* - Output sanitized (CVE-2022-36446)
* - No sudo — cockpit superuser channel + polkit handles auth
* (CVE-2022-0824 lesson — the v0.0.15-era sudo shell-out is gone)
*
* Bridge surface (see shared/bridge.js → bridge.prometheus + bridge.grafana):
* Prometheus:
* summary() → overall status + version + targets + alerts count
* targets() → scrape target health (up/down/duration)
* alerts() → current firing + pending alerts
* rules() → alerting + recording rules
* config() → full Prometheus config
* logSummary() → pushgateway log pipeline throughput
* restart() → systemctl restart prometheus.service (superuser)
* reload() → SIGHUP config reload (superuser)
* Grafana:
* summary() → overall status + version + dashboard count
* dashboards() → list of provisioned dashboards
* datasources() → list of configured datasources
* alerts() → Grafana-managed alerts
* health() → Grafana health endpoint
* org() → current organization info
* users() → Grafana user list
* plugins() → installed Grafana plugins
* search(query) → search dashboards by name
* restart() → systemctl restart grafana-server.service (superuser)
* reload() → SIGUSR2 provisioning reload (superuser)
*/
export async function mount(panel, { bridge, EventBus }) {
panel.innerHTML = renderSkeleton();
// Load both summaries in parallel.
const [promSummary, grafSummary] = await Promise.all([
safe(bridge.prometheus.summary(), { installed: false, status: 'uninstalled' }),
safe(bridge.grafana.summary(), { installed: false, status: 'uninstalled' }),
]);
panel.innerHTML = `
Observability stack — Prometheus (metrics) + Grafana (dashboards)
· Prometheus ${promSummary.installed ? 'installed' : 'absent'}
· Grafana ${grafSummary.installed ? 'installed' : 'absent'}
SysDeck Monitoring
| status | ${statusBadge} |
| version | ${escapeHtml(summary.version || '?')} |
| uptime | ${escapeHtml(summary.uptime || '?')} |
| targets | ${targetsUp}/${targetsTotal} up |
| alerts firing | ${alertsFiring > 0 ? `${alertsFiring}` : '0'} |
| API URL | http://127.0.0.1:9095 |
| status | ${statusBadge} |
| version | ${escapeHtml(summary.version || '?')} |
| dashboards | ${dashboards} |
| datasources | ${datasources} |
| URL | http://127.0.0.1:3000 |
| default login | admin / admin (change immediately) |
${escapeHtml(binary)} is not installed.
Install it to enable the ${escapeHtml(name)} tab:
${commands.map(escapeHtml).join('\n')}
After install, enable + start the service:
sudo systemctl enable --now ${escapeHtml(service)}
The web UI will be available at http://127.0.0.1:${escapeHtml(port)}
and will appear in the iframe below once the service is running.