hermes-hub/src/antigravity_provider/router/web/static/index.html

443 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hermes Hub — Панель управления</title>
<link rel="stylesheet" href="style.css">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
</head>
<body>
<div id="app" class="app-layout">
<!-- Sidebar Navigation (9 Full Views) -->
<aside class="sidebar">
<div class="brand">
<div class="brand-logo"></div>
<div class="brand-text">
<span class="brand-title">HERMES HUB</span>
<span class="brand-subtitle">Multi-Account Router</span>
</div>
</div>
<nav class="nav-menu">
<button class="nav-item active" data-view="overview">
<span class="nav-icon">📊</span>
<span class="nav-label">Обзор</span>
</button>
<button class="nav-item" data-view="accounts">
<span class="nav-icon">👥</span>
<span class="nav-label">Аккаунты</span>
<span class="nav-badge" id="nav-accounts-count">0</span>
</button>
<button class="nav-item" data-view="routing">
<span class="nav-icon">🔀</span>
<span class="nav-label">Маршрутизация</span>
</button>
<button class="nav-item" data-view="analytics">
<span class="nav-icon">📈</span>
<span class="nav-label">Аналитика</span>
</button>
<button class="nav-item" data-view="health">
<span class="nav-icon">🩺</span>
<span class="nav-label">Состояние</span>
</button>
<button class="nav-item" data-view="logs">
<span class="nav-icon">📜</span>
<span class="nav-label">Журнал событий</span>
</button>
<button class="nav-item" data-view="settings">
<span class="nav-icon">⚙️</span>
<span class="nav-label">Настройки</span>
</button>
</nav>
<div class="sidebar-footer">
<div class="source-indicator" id="source-indicator">
<span class="status-dot healthy"></span>
<span class="source-text" id="source-text">Загрузка данных...</span>
</div>
<div class="version-tag" id="version-tag">Hermes Hub Web v0.1.1</div>
</div>
</aside>
<!-- Main Content Container -->
<main class="main-wrapper">
<!-- Top Header -->
<header class="top-header">
<div class="header-left">
<h1 class="header-title" id="page-title">Обзор системы</h1>
<span class="header-readiness-badge" id="header-readiness-badge">
<span class="status-dot"></span>
<span id="header-readiness-text">Инициализация...</span>
</span>
</div>
<div class="header-actions">
<button class="btn btn-secondary" id="btn-refresh-all" title="Обновить квоты всех профилей">
<span class="btn-icon"></span>
<span>Обновить всё</span>
</button>
<button class="btn btn-primary" id="btn-add-account">
<span class="btn-icon">+</span>
<span>Добавить аккаунт</span>
</button>
</div>
</header>
<!-- Dynamic Views (7 Control Sections) -->
<div class="content-scroll">
<!-- 1. OVERVIEW VIEW -->
<section id="view-overview" class="view-pane active">
<div class="overview-grid">
<div class="kpi-card">
<div class="kpi-label">Состояние системы</div>
<div class="kpi-value text-healthy" id="kpi-system-readiness"></div>
<div class="kpi-sub" id="kpi-readiness-summary"></div>
</div>
<div class="kpi-card">
<div class="kpi-label">Подключено аккаунтов</div>
<div class="kpi-value text-accent" id="kpi-total-accounts"></div>
<div class="kpi-sub" id="kpi-accounts-sub"></div>
</div>
<div class="kpi-card">
<div class="kpi-label">Готовых ролей</div>
<div class="kpi-value text-healthy" id="kpi-ready-roles"></div>
<div class="kpi-sub" id="kpi-roles-sub"></div>
</div>
<div class="kpi-card">
<div class="kpi-label">Провайдеры ИИ</div>
<div class="kpi-value text-info" id="kpi-providers-count"></div>
<div class="kpi-sub" id="kpi-providers-sub">5 поддерживаемых систем</div>
</div>
</div>
<div class="section-card">
<div class="section-card-header">
<div class="section-card-title">Схема маршрутизации запросов</div>
<div class="section-card-subtitle">Распределение агентов и цепочки отказоустойчивости</div>
</div>
<div class="route-diagram-container" id="overview-route-diagram">
<!-- Rendered by app.js -->
</div>
</div>
<div class="section-card">
<div class="section-card-header">
<div class="section-card-title">Провайдеры и доступность runtime</div>
<div class="section-card-subtitle">Статус локальных адаптеров и обнаруженные модели</div>
</div>
<div class="providers-grid" id="overview-providers-summary">
<!-- Rendered by app.js -->
</div>
</div>
</section>
<!-- 2. ACCOUNTS VIEW -->
<section id="view-accounts" class="view-pane">
<div class="toolbar">
<div class="search-box">
<span class="search-icon">🔍</span>
<input type="text" id="accounts-search" placeholder="Поиск по аккаунту, почте, роли или модели...">
</div>
<div class="filters-row">
<select id="filter-provider" class="select-filter">
<option value="all">Все провайдеры</option>
<option value="antigravity">Google Antigravity</option>
<option value="openai-codex">OpenAI Codex</option>
<option value="opencode-go">OpenCode Go</option>
<option value="claude">Claude (Anthropic)</option>
<option value="grok">Grok (xAI)</option>
</select>
<select id="filter-health" class="select-filter">
<option value="all">Все состояния</option>
<option value="healthy">Работает</option>
<option value="warning">Предупреждение</option>
<option value="quota_exhausted">Квота исчерпана</option>
<option value="auth_required">Требуется вход</option>
<option value="disabled">Отключён / Резерв</option>
</select>
<div class="toolbar-stats" id="accounts-stats-summary">
Показано: <strong>0</strong> из <strong>0</strong> аккаунтов
</div>
</div>
</div>
<div id="accounts-container" class="accounts-groups-container">
<!-- Rendered by app.js -->
</div>
</section>
<!-- 3. ROUTING VIEW (Main Control Center) -->
<section id="view-routing" class="view-pane">
<div class="view-header-note">
Главный центр управления маршрутизацией: перетаскивайте узлы для смены приоритета (Основной → Резерв 1 → Резерв 2), настраивайте рабочие модели и управляйте составом цепочек.
</div>
<div class="routing-pipelines-list" id="routing-pipelines-container">
<!-- Rendered by app.js -->
</div>
</section>
<!-- 4. ANALYTICS VIEW (P0-1 & P0-5) -->
<section id="view-analytics" class="view-pane">
<div class="view-header-note">
Телеметрия вызовов и отказоустойчивости за 24 часа. Метрики отражают реальные замеры задержки и частоты ошибок при маршрутизации.
</div>
<div class="overview-grid">
<div class="kpi-card">
<div class="kpi-label">Всего вызовов (24ч)</div>
<div class="kpi-value text-accent" id="analytics-total-calls"></div>
<div class="kpi-sub" id="analytics-calls-breakdown"></div>
</div>
<div class="kpi-card">
<div class="kpi-label">Доля отказов / ошибок</div>
<div class="kpi-value" id="analytics-error-rate"></div>
<div class="kpi-sub" id="analytics-error-rate-sub"></div>
</div>
<div class="kpi-card">
<div class="kpi-label">Латентность (p50 / p95 / max)</div>
<div class="kpi-value text-info" id="analytics-latency-p50"></div>
<div class="kpi-sub" id="analytics-latency-sub"></div>
</div>
<div class="kpi-card">
<div class="kpi-label">Расход токенов</div>
<div class="kpi-value text-muted" id="analytics-tokens-total">Н</div>
<div class="kpi-sub" id="analytics-tokens-sub">Н/Д: провайдеры не отдают токены</div>
</div>
</div>
<div class="section-card">
<div class="section-card-header">
<div class="section-card-title">Телеметрия по провайдерам ИИ</div>
<div class="section-card-subtitle">Распределение запросов, отказов и задержек в окне измерений</div>
</div>
<div class="table-responsive" id="analytics-providers-table">
<!-- Rendered by app.js -->
</div>
</div>
<div class="section-card">
<div class="section-card-header">
<div class="section-card-title">Телеметрия по ролям агентов</div>
<div class="section-card-subtitle">Нагрузка по ролям команды, частота сбоев и тайминги</div>
</div>
<div class="table-responsive" id="analytics-roles-table">
<!-- Rendered by app.js -->
</div>
</div>
</section>
<!-- 7. HEALTH VIEW (P0-2) -->
<section id="view-health" class="view-pane">
<div class="readiness-banner" id="health-readiness-banner">
<!-- Readiness state banner rendered by app.js -->
</div>
<div class="section-card">
<div class="section-card-header">
<div class="section-card-title">Ресурсы хост-системы</div>
<div class="section-card-subtitle">Мониторинг CPU, памяти, диска и сетевых параметров</div>
</div>
<div class="host-resources-grid" id="health-host-resources">
<!-- Rendered by app.js -->
</div>
</div>
<div class="section-card">
<div class="section-card-header">
<div class="section-card-title">Предупреждения и диагностика</div>
<div class="section-card-subtitle">Анализ рисков, отсутствие резервов и деградация маршрутов</div>
</div>
<div class="warnings-list" id="health-warnings-list">
<!-- Rendered by app.js -->
</div>
</div>
</section>
<!-- 8. LOGS / EVENTS VIEW (P0-3) -->
<section id="view-logs" class="view-pane">
<div class="toolbar">
<div class="search-box">
<span class="search-icon">🔍</span>
<input type="text" id="logs-search" placeholder="Поиск по событиям или деталям...">
</div>
<div class="filters-row">
<select id="logs-filter-level" class="select-filter">
<option value="all">Все уровни</option>
<option value="info">INFO</option>
<option value="success">SUCCESS</option>
<option value="warning">WARNING</option>
<option value="error">ERROR</option>
</select>
<select id="logs-filter-category" class="select-filter">
<option value="all">Все категории</option>
<option value="system">Система (system)</option>
<option value="account">Аккаунты (account)</option>
<option value="quota">Квоты (quota)</option>
<option value="routing">Маршрутизация (routing)</option>
<option value="auth">Авторизация (auth)</option>
</select>
<button class="btn btn-secondary btn-sm" id="btn-refresh-logs">↻ Обновить журнал</button>
</div>
</div>
<div class="section-card" style="margin-top:12px;">
<div class="section-card-header">
<div class="section-card-title">Журнал событий Hermes Hub</div>
<div class="section-card-subtitle">События маршрутизатора, обновления квот, авторизации и тестов</div>
</div>
<div class="logs-container" id="logs-container">
<!-- Rendered by app.js from /api/events -->
</div>
</div>
</section>
<!-- 9. SETTINGS VIEW (P0-4) -->
<section id="view-settings" class="view-pane">
<!-- Hub Server Settings -->
<div class="settings-card">
<h2 class="settings-group-title">Параметры сервера Hermes Hub</h2>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Хост и порт веб-сервера</div>
<div class="setting-desc">Сетевой адрес и порт для HTTP/REST API и веб-интерфейса</div>
</div>
<div class="setting-control" style="display:flex; gap:8px;">
<input type="text" id="setting-server-host" class="input-text" style="width:140px;" placeholder="127.0.0.1">
<input type="number" id="setting-server-port" class="input-text" style="width:90px;" placeholder="5800">
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Токен безопасности сервера</div>
<div class="setting-desc">Обязателен при внешнем подключении (host != 127.0.0.1)</div>
</div>
<div class="setting-control" style="display:flex; align-items:center; gap:8px;">
<span id="setting-token-status-badge" class="badge">Токен не задан</span>
<input type="password" id="setting-server-token-input" class="input-text" placeholder="Задать новый токен...">
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Интервал обновления квот</div>
<div class="setting-desc">Период фонового опроса квот провайдеров (секунды)</div>
</div>
<div class="setting-control">
<select id="setting-quota-interval" class="select-filter">
<option value="60">60 секунд (1 мин)</option>
<option value="120">120 секунд (2 мин)</option>
<option value="300">300 секунд (5 мин — стандарт)</option>
<option value="600">600 секунд (10 мин)</option>
</select>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Тема оформления</div>
<div class="setting-desc">Цветовая схема интерфейса Hub</div>
</div>
<div class="setting-control">
<select id="setting-theme" class="select-filter">
<option value="system">Системная (по умолчанию)</option>
<option value="dark">Тёмная (Dark)</option>
<option value="light">Светлая (Light)</option>
</select>
</div>
</div>
<div class="settings-actions">
<button class="btn btn-primary" id="btn-save-hub-settings">Сохранить настройки сервера</button>
</div>
</div>
<!-- Read-Only System Paths -->
<div class="settings-card" style="margin-top:16px;">
<h2 class="settings-group-title">Системные пути и окружение</h2>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Каталог данных Hermes</div>
<div class="setting-desc mono-path" id="path-hermes-home"></div>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Каталог конфигурации</div>
<div class="setting-desc mono-path" id="path-config-dir"></div>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Файл журнала событий</div>
<div class="setting-desc mono-path" id="path-log-file"></div>
</div>
</div>
</div>
<!-- Client Local Settings -->
<div class="settings-card" style="margin-top:16px;">
<h2 class="settings-group-title">Параметры веб-клиента (Браузер)</h2>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Источник данных в браузере</div>
<div class="setting-desc">Автоматическое переключение между Live API и фикстурой</div>
</div>
<div class="setting-control">
<select id="setting-source-mode" class="select-filter">
<option value="auto">Авто (Live API с fallback)</option>
<option value="live">Строго Live API (/api/snapshot)</option>
<option value="fixture">Строго фикстура (snapshot.example.json)</option>
</select>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Интервал авто-опроса UI</div>
<div class="setting-desc">Частота обновления снимка состояния в окне браузера</div>
</div>
<div class="setting-control">
<select id="setting-poll-interval" class="select-filter">
<option value="3000">3 секунды</option>
<option value="5000" selected>5 секунд (стандарт)</option>
<option value="10000">10 секунд</option>
<option value="0">Отключить авто-опрос</option>
</select>
</div>
</div>
<div class="setting-row">
<div class="setting-info">
<div class="setting-label">Локальный токен клиента (X-Hub-Token)</div>
<div class="setting-desc">Токен для заголовка X-Hub-Token при внешних вызовах</div>
</div>
<div class="setting-control">
<input type="password" id="setting-client-token-input" class="input-text" placeholder="Введите токен...">
</div>
</div>
<div class="settings-actions">
<button class="btn btn-secondary" id="btn-save-client-settings">Сохранить локальные параметры</button>
</div>
</div>
</section>
</div>
</main>
</div>
<!-- Modal Layer -->
<div id="modal-backdrop" class="modal-backdrop hidden">
<div id="modal-container" class="modal-card">
<div class="modal-header">
<h2 id="modal-title" class="modal-title">Модальное окно</h2>
<button id="modal-close-btn" class="modal-close" title="Закрыть"></button>
</div>
<div id="modal-body" class="modal-body">
<!-- Injected dynamically -->
</div>
<div id="modal-footer" class="modal-footer">
<!-- Injected dynamically -->
</div>
</div>
</div>
<!-- Toast Notification Container -->
<div id="toast-container" class="toast-container"></div>
<!-- Application Logic -->
<script src="app.js"></script>
</body>
</html>