- ${alerts}
Alerta se gap > ${health.webhook_gap_alert_minutes || 15} min sem eventos VM112.
+diff --git a/projects/ops-desk/frontend/assets/app.js b/projects/ops-desk/frontend/assets/app.js index 06e86a3..1ad8c52 100644 --- a/projects/ops-desk/frontend/assets/app.js +++ b/projects/ops-desk/frontend/assets/app.js @@ -3819,6 +3819,12 @@ async function renderInfra2() { } } +function infraKvHtml(items) { + return `
Verificando…
'; @@ -3832,58 +3838,97 @@ async function renderInfra() { const onboard = health.vm112_onboard || {}; const last = onboard.last_webhook; const gap = onboard.gap_minutes != null ? `${Math.round(onboard.gap_minutes)} min` : '—'; + const vmOk = onboard.vm112_api?.reachable; + const wazuhOk = wazuh.http_status === 200; const statusCls = health.status === 'ok' ? 'ok' : health.status === 'critical' ? 'escalated' : 'assisting'; + const heroHealthDot = health.status === 'ok' ? '' : health.status === 'critical' ? 'infra-hero-dot--bad' : 'infra-hero-dot--warn'; const alerts = (health.alerts || []).map((a) => `Alerta se gap > ${health.webhook_gap_alert_minutes || 15} min sem eventos VM112.
+A carregar…
Spec 028 · VM123 bridge :18087 · multidomínio · conta temporária com cleanup.
+${esc(JSON.stringify(integrations, null, 2))}
+ Alerta se gap > ${health.webhook_gap_alert_minutes || 15} min sem eventos VM112.
-Domínios protegidos (ex.: myvexx.com) exigem código único gerado aqui pelo root — válido para conferência / purge em Serviços.
A carregar…
Spec 028 · VM123 bridge :18087 · multidomínio · conta temporária com cleanup automático.
-${esc(JSON.stringify(integrations, null, 2))}
${esc(d)}`).join(', ') || '—';
+ const domainChips = (meta.domains || []).map((d) =>
+ `${esc(d)}`
+ ).join('') || '';
const canGen = meta.can_generate && typeof canManageUsers === 'function' && canManageUsers();
let codesHtml = '';
if (canGen) {
const data = await api('/v1/infra/purge-auth-codes?limit=20');
const rows = (data.codes || []).map((c) => `
${esc(c.domain)}Gere código com senha Root — use na conferência antes do purge em Serviços.
+| Domínio | Nota | Expira | Por |
|---|---|---|---|
| Nenhum código activo | |||
| Domínio | Nota | Expira | Por |
|---|---|---|---|
| Nenhum código activo | |||
Apenas super_admin (root) gera códigos. Peça o código ao root antes do purge em Serviços.
`; } - panel.innerHTML = ` -Domínios protegidos: ${domains}
- ${codesHtml}`; + panel.innerHTML = codesHtml; const form = panel.querySelector('#purge-auth-generate-form'); if (form) { form.addEventListener('submit', async (ev) => { diff --git a/projects/ops-desk/frontend/assets/styles.css b/projects/ops-desk/frontend/assets/styles.css index 2603b2a..ed9ab49 100644 --- a/projects/ops-desk/frontend/assets/styles.css +++ b/projects/ops-desk/frontend/assets/styles.css @@ -3771,6 +3771,7 @@ button.health-card { border-radius: 8px; border: 1px dashed #cbd5e1; } +.servicos-tile-icon { font-size: 1.35rem; margin-bottom: 0.35rem; } @@ -3888,29 +3889,224 @@ button.health-card { overflow-y: auto; margin-top: 0.75rem; } -.purge-auth-generated { - margin: 0.75rem 0; - padding: 0.75rem 1rem; - background: rgba(46, 125, 50, 0.12); - border-radius: 6px; -} -.purge-auth-generated.hidden { display: none; } -.purge-auth-code-display { - font-size: 1.25rem; - letter-spacing: 0.08em; -} -.purge-auth-form { - display: grid; - gap: 0.5rem; - max-width: 28rem; - margin-top: 0.75rem; -} .purge-history-removed { font-size: 0.85rem; color: var(--muted, #6b7280); max-width: 14rem; } +/* Infra — layout tipo wizard (ws-panel + aqua/teal) */ +.infra-page { + display: flex; + flex-direction: column; + gap: 0.85rem; +} +.infra-hero { + display: flex; + flex-wrap: wrap; + gap: 0.65rem; + align-items: stretch; +} +.infra-hero-chip { + flex: 1 1 200px; + display: flex; + align-items: center; + gap: 0.65rem; + padding: 0.75rem 1rem; + border-radius: 12px; + border: 1px solid #e2e8f0; + background: linear-gradient(135deg, #f0fdfa 0%, #fff 55%); + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06); +} +.infra-hero-chip--alert { + background: linear-gradient(135deg, #fff7ed 0%, #fff 55%); + border-color: #fed7aa; +} +.infra-hero-dot { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; + background: #14b8a6; + box-shadow: 0 0 0 3px rgba(20, 184, 166, 0.25); +} +.infra-hero-dot--warn { background: #f59e0b; box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.25); } +.infra-hero-dot--bad { background: #ef4444; box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.25); } +.infra-hero-body strong { + display: block; + font-size: 0.88rem; + color: #0f172a; +} +.infra-hero-body span { + display: block; + font-size: 0.75rem; + color: #64748b; + margin-top: 0.1rem; +} +.infra-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.85rem; +} +.infra-panel--wide { grid-column: 1 / -1; } +@media (max-width: 900px) { + .infra-grid { grid-template-columns: 1fr; } +} +.infra-panel .ws-panel-body { padding: 0.85rem 1rem; } +.infra-kv { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 0.65rem 1rem; + margin: 0; +} +.infra-kv div { + padding: 0.55rem 0.65rem; + border-radius: 8px; + background: #f8fafc; + border: 1px solid #e2e8f0; +} +.infra-kv dt { + font-size: 0.68rem; + text-transform: uppercase; + letter-spacing: 0.04em; + color: #64748b; + margin: 0; +} +.infra-kv dd { + margin: 0.2rem 0 0; + font-size: 0.9rem; + font-weight: 600; + color: #0f172a; + font-variant-numeric: tabular-nums; +} +.infra-actions { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.75rem; +} +.infra-hint { + margin: 0.65rem 0 0; + font-size: 0.78rem; + color: #64748b; + line-height: 1.45; +} +.infra-alert-list { + list-style: none; + margin: 0.65rem 0 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.35rem; +} +.infra-alert-list li { + padding: 0.45rem 0.65rem; + border-radius: 8px; + background: #f8fafc; + border: 1px solid #e2e8f0; + font-size: 0.82rem; +} +.ws-panel-head--aqua { + background: linear-gradient(135deg, #0891b2 0%, #22d3ee 45%, #2dd4bf 100%); +} +.ws-panel-head--slate { + background: linear-gradient(90deg, #334155, #64748b); +} +.ws-panel-head--violet { + background: linear-gradient(90deg, #6d28d9, #8b5cf6); +} +.ws-panel-head--rose { + background: linear-gradient(90deg, #be123c, #f43f5e); +} +.infra-domain-chips { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin-bottom: 0.75rem; +} +.infra-domain-chip { + font-size: 0.78rem; + padding: 0.25rem 0.55rem; + border-radius: 999px; + background: #ecfeff; + border: 1px solid #99f6e4; + color: #0f766e; + font-family: ui-monospace, monospace; +} +.purge-auth-form { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.65rem 0.85rem; + margin-top: 0.5rem; + padding: 0.85rem; + border-radius: 10px; + background: linear-gradient(180deg, #f0fdfa 0%, #f8fafc 100%); + border: 1px solid #ccfbf1; +} +.purge-auth-form label { + display: block; + font-size: 0.72rem; + font-weight: 600; + color: #475569; + margin-bottom: 0.25rem; +} +.purge-auth-form input { + width: 100%; + padding: 0.5rem 0.65rem; + border: 1px solid #cbd5e1; + border-radius: 8px; + font: inherit; + background: #fff; +} +.purge-auth-form input:focus { + outline: none; + border-color: #14b8a6; + box-shadow: 0 0 0 3px rgba(20, 184, 166, 0.2); +} +.purge-auth-form button { + grid-column: 1 / -1; + justify-self: start; +} +.purge-auth-generated { + margin: 0.75rem 0; + padding: 1rem 1.1rem; + border-radius: 12px; + background: linear-gradient(135deg, #ecfdf5 0%, #f0fdfa 100%); + border: 1px solid #6ee7b7; + text-align: center; +} +.purge-auth-generated.hidden { display: none; } +.purge-auth-code-display { + font-size: 1.5rem; + letter-spacing: 0.12em; + color: #0f766e; + font-weight: 700; +} +.infra-table-wrap { + margin-top: 0.75rem; + border: 1px solid #e2e8f0; + border-radius: 10px; + overflow: hidden; + background: #fff; +} +.infra-table-wrap table { + margin: 0; +} +.infra-table-wrap th { + background: #f1f5f9; + font-size: 0.68rem; +} +.infra-table-wrap td { + font-size: 0.82rem; +} +.infra-json-panel pre.raw { + margin: 0; + max-height: 240px; + border-radius: 0; + border: none; + background: #0f172a; +} + /* Spec 021 — Acesso utilizador (separado do VM112 Onboard) */ .ws-access-zone { margin-bottom: 1.25rem;