chore(iot): убрать старое (Kafka/K8s) из живого админ-UI и .gitignore
- iot-admin.html: секции Kafka/Pods заменены на Очередь shared-SQS и Рантайм (бэкенд admin.go давно отдаёт sqs/runtime, а фронт рисовал мёртвые секции); удалены CSS .pod-*/.badge-*/.dot-*/.lag-bar*, исправлен заголовок. - .gitignore: удалены сталые пути бинарников старого IoT (/iot-operator, /mqtt-bridge, /kafka-consumer, /sqs-consumer).
This commit is contained in:
@@ -1,8 +1,4 @@
|
|||||||
# Бинарники (только в корне проекта, не директории cmd/)
|
# Бинарники (только в корне проекта, не директории cmd/)
|
||||||
/iot-operator
|
|
||||||
/mqtt-bridge
|
|
||||||
/kafka-consumer
|
|
||||||
/sqs-consumer
|
|
||||||
bin/
|
bin/
|
||||||
ws-probe/ws-probe
|
ws-probe/ws-probe
|
||||||
/iot-service
|
/iot-service
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<!-- Создано: 2026-04-06
|
<!-- Создано: 2026-04-06
|
||||||
iot-admin.html — страница администратора IoT pipeline.
|
iot-admin.html — страница администратора IoT pipeline.
|
||||||
Показывает: PostgreSQL stats per tenant, Kafka consumer lag, K8s pod statuses.
|
Показывает: PostgreSQL stats per tenant, очередь shared-SQS, рантайм процесса.
|
||||||
Auth: ADMIN_STATS_TOKEN вводится вручную и хранится в sessionStorage.
|
Auth: ADMIN_STATS_TOKEN вводится вручную и хранится в sessionStorage.
|
||||||
Раздаётся по GET /iot-admin (go:embed в бинарнике оператора).
|
Раздаётся по GET /iot-admin (go:embed в бинарнике iot-service).
|
||||||
НЕ для конечных пользователей — только для администратора платформы. -->
|
НЕ для конечных пользователей — только для администратора платформы. -->
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
@@ -95,23 +95,6 @@
|
|||||||
.card-ok { color: #34d399; }
|
.card-ok { color: #34d399; }
|
||||||
.card-err { color: #f87171; }
|
.card-err { color: #f87171; }
|
||||||
|
|
||||||
/* Pod status card */
|
|
||||||
.pod-card {
|
|
||||||
background: #001929; border: 1px solid #0b2d50; border-radius: 10px;
|
|
||||||
padding: 20px; display: flex; flex-direction: column; gap: 8px;
|
|
||||||
}
|
|
||||||
.pod-name { font-size: 13px; font-weight: 600; color: #7fc8f8; font-family: monospace; }
|
|
||||||
.pod-row { display: flex; justify-content: space-between; font-size: 12px; }
|
|
||||||
.pod-label { color: #6b8eaa; }
|
|
||||||
.pod-val { color: #e2ecf6; }
|
|
||||||
.badge {
|
|
||||||
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
|
||||||
font-size: 11px; font-weight: 700;
|
|
||||||
}
|
|
||||||
.badge-ok { background: #052e16; color: #34d399; border: 1px solid #064e3b; }
|
|
||||||
.badge-warn { background: #2d1c00; color: #f59e0b; border: 1px solid #4d3000; }
|
|
||||||
.badge-err { background: #300; color: #f87171; border: 1px solid #500; }
|
|
||||||
|
|
||||||
/* Tenant table */
|
/* Tenant table */
|
||||||
.tenant-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
.tenant-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||||
.tenant-table th {
|
.tenant-table th {
|
||||||
@@ -142,18 +125,6 @@
|
|||||||
/* Last updated */
|
/* Last updated */
|
||||||
.last-updated { font-size: 12px; color: #2d5070; text-align: center; margin-top: 16px; }
|
.last-updated { font-size: 12px; color: #2d5070; text-align: center; margin-top: 16px; }
|
||||||
|
|
||||||
/* Status dot */
|
|
||||||
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
|
|
||||||
.dot-ok { background: #34d399; }
|
|
||||||
.dot-warn { background: #f59e0b; }
|
|
||||||
.dot-err { background: #f87171; }
|
|
||||||
|
|
||||||
/* Kafka lag bar */
|
|
||||||
.lag-bar-wrap { background: #001120; border-radius: 4px; height: 6px; margin-top: 8px; overflow: hidden; }
|
|
||||||
.lag-bar { height: 100%; border-radius: 4px; transition: width 0.5s; min-width: 2px; }
|
|
||||||
.lag-bar-ok { background: #34d399; }
|
|
||||||
.lag-bar-warn { background: #f59e0b; }
|
|
||||||
|
|
||||||
/* Spinner */
|
/* Spinner */
|
||||||
.spinner {
|
.spinner {
|
||||||
border: 3px solid #0b2d50; border-top-color: #1a7fd4;
|
border: 3px solid #0b2d50; border-top-color: #1a7fd4;
|
||||||
@@ -223,26 +194,26 @@
|
|||||||
<!-- Данные -->
|
<!-- Данные -->
|
||||||
<div id="dataContent" style="display:none">
|
<div id="dataContent" style="display:none">
|
||||||
|
|
||||||
<!-- Kafka -->
|
<!-- shared-SQS: очередь телеметрии -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<svg class="section-icon" viewBox="0 0 24 24" fill="none" stroke="#7fc8f8" stroke-width="2">
|
<svg class="section-icon" viewBox="0 0 24 24" fill="none" stroke="#7fc8f8" stroke-width="2">
|
||||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
|
<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="section-title">Kafka</span>
|
<span class="section-title">Очередь shared-SQS</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="cards" id="kafkaCards"></div>
|
<div class="cards" id="sqsCards"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- K8s Pods -->
|
<!-- Рантайм процесса -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<svg class="section-icon" viewBox="0 0 24 24" fill="none" stroke="#7fc8f8" stroke-width="2">
|
<svg class="section-icon" viewBox="0 0 24 24" fill="none" stroke="#7fc8f8" stroke-width="2">
|
||||||
<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>
|
<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="section-title">Pods</span>
|
<span class="section-title">Рантайм</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="cards" id="podCards"></div>
|
<div class="cards" id="runtimeCards"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- PostgreSQL per tenant -->
|
<!-- PostgreSQL per tenant -->
|
||||||
@@ -388,83 +359,54 @@ function resetRefreshTimer() {
|
|||||||
|
|
||||||
// ── Render ─────────────────────────────────────────────────────────────────
|
// ── Render ─────────────────────────────────────────────────────────────────
|
||||||
function renderAll(data) {
|
function renderAll(data) {
|
||||||
renderKafka(data.kafka || {});
|
renderSQS(data.sqs || {});
|
||||||
renderPods(data.pods || {});
|
renderRuntime(data.runtime || {});
|
||||||
renderPostgres(data.postgres || {});
|
renderPostgres(data.postgres || {});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kafka section
|
// Очередь shared-SQS
|
||||||
function renderKafka(kafka) {
|
function renderSQS(sqs) {
|
||||||
const el = document.getElementById('kafkaCards');
|
const el = document.getElementById('sqsCards');
|
||||||
if (kafka.error) {
|
if (sqs.error) {
|
||||||
el.innerHTML = `<div class="card"><div class="card-title">Ошибка</div>
|
el.innerHTML = `<div class="card"><div class="card-title">Ошибка</div>
|
||||||
<div class="card-value card-err" style="font-size:14px">${esc(kafka.error)}</div></div>`;
|
<div class="card-value card-err" style="font-size:14px">${esc(sqs.error)}</div></div>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const pending = sqs.approximate_messages || 0;
|
||||||
const lag = kafka.consumer_lag || 0;
|
const inFlight = sqs.approximate_messages_not_visible || 0;
|
||||||
const latest = kafka.latest_offset || 0;
|
const pendingClass = pending === 0 ? 'card-ok' : pending < 100 ? 'card-warn' : 'card-err';
|
||||||
const committed = kafka.committed_offset || 0;
|
|
||||||
const lagClass = lag === 0 ? 'card-ok' : lag < 100 ? 'card-warn' : 'card-err';
|
|
||||||
const barClass = lag === 0 ? 'lag-bar-ok' : 'lag-bar-warn';
|
|
||||||
const barWidth = latest > 0 ? Math.max(2, Math.round((committed / latest) * 100)) : 100;
|
|
||||||
|
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-title">Consumer Lag</div>
|
<div class="card-title">Ожидают обработки</div>
|
||||||
<div class="card-value ${lagClass}">${lag}</div>
|
<div class="card-value ${pendingClass}">${pending.toLocaleString()}</div>
|
||||||
<div class="card-sub">iot-pg-consumer / iot.telemetry</div>
|
<div class="card-sub">ApproximateNumberOfMessages</div>
|
||||||
<div class="lag-bar-wrap"><div class="lag-bar ${barClass}" style="width:${barWidth}%"></div></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-title">Latest Offset (всего прошло)</div>
|
<div class="card-title">В обработке</div>
|
||||||
<div class="card-value card-accent">${latest.toLocaleString()}</div>
|
<div class="card-value card-accent">${inFlight.toLocaleString()}</div>
|
||||||
<div class="card-sub">Kafka log end offset</div>
|
<div class="card-sub">ApproximateNumberOfMessagesNotVisible</div>
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-title">Committed Offset</div>
|
|
||||||
<div class="card-value">${committed.toLocaleString()}</div>
|
|
||||||
<div class="card-sub">Consumer обработал</div>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pods section
|
// Рантайм процесса
|
||||||
function renderPods(pods) {
|
function renderRuntime(rt) {
|
||||||
const el = document.getElementById('podCards');
|
const el = document.getElementById('runtimeCards');
|
||||||
el.innerHTML = '';
|
const uptime = rt.uptime_sec || 0;
|
||||||
|
const h = Math.floor(uptime / 3600);
|
||||||
const labels = {
|
const m = Math.floor((uptime % 3600) / 60);
|
||||||
'iot-mqtt-bridge': 'MQTT Bridge',
|
const s = uptime % 60;
|
||||||
'iot-kafka-consumer': 'Kafka Consumer'
|
const uptimeStr = h > 0 ? `${h}ч ${m}м` : m > 0 ? `${m}м ${s}с` : `${s}с`;
|
||||||
};
|
el.innerHTML = `
|
||||||
|
<div class="card">
|
||||||
for (const [key, pod] of Object.entries(pods)) {
|
<div class="card-title">Версия</div>
|
||||||
const title = labels[key] || key;
|
<div class="card-value" style="font-size:20px">${esc(rt.version || '—')}</div>
|
||||||
if (pod.error || pod.status === 'not found') {
|
<div class="card-sub">iot-service</div>
|
||||||
el.innerHTML += `
|
|
||||||
<div class="pod-card">
|
|
||||||
<div class="pod-name">${esc(title)}</div>
|
|
||||||
<div style="color:#f87171;font-size:12px">${esc(pod.error || 'Pod not found')}</div>
|
|
||||||
</div>`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const readyBadge = pod.ready
|
|
||||||
? '<span class="badge badge-ok">Ready</span>'
|
|
||||||
: '<span class="badge badge-warn">Not Ready</span>';
|
|
||||||
const restartColor = pod.restarts > 5 ? 'card-err' : pod.restarts > 0 ? 'card-warn' : 'card-ok';
|
|
||||||
|
|
||||||
el.innerHTML += `
|
|
||||||
<div class="pod-card">
|
|
||||||
<div class="pod-name">
|
|
||||||
<span class="dot dot-${pod.ready ? 'ok' : 'warn'}"></span>${esc(title)}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pod-row"><span class="pod-label">Pod</span><span class="pod-val" style="font-family:monospace;font-size:11px">${esc(pod.name)}</span></div>
|
<div class="card">
|
||||||
<div class="pod-row"><span class="pod-label">Phase</span><span class="pod-val">${esc(pod.phase)} ${readyBadge}</span></div>
|
<div class="card-title">Uptime</div>
|
||||||
<div class="pod-row"><span class="pod-label">Restarts</span><span class="pod-val ${restartColor}">${pod.restarts}</span></div>
|
<div class="card-value card-accent">${uptimeStr}</div>
|
||||||
<div class="pod-row"><span class="pod-label">Age</span><span class="pod-val">${esc(pod.age)}</span></div>
|
<div class="card-sub">с ${esc(rt.started_at || '—')}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Postgres section
|
// Postgres section
|
||||||
|
|||||||
Reference in New Issue
Block a user