ctf-ad-mirror-tool-rust/web/index.html

89 lines
7.0 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">
<meta name="color-scheme" content="dark">
<title>Mirror Tool — операторская</title>
<link rel="stylesheet" href="/app.css">
<script src="/app.js" defer></script>
</head>
<body>
<div id="login-view" class="login-wrap">
<main class="login-panel">
<p class="eyebrow">TCP / ОПЕРАТОРСКАЯ</p>
<h1>Mirror Tool<span class="brand-mark" aria-hidden="true">↗↗</span></h1>
<p class="muted">Маршруты, реплики и состояние сервисов.</p>
<form id="login-form">
<label for="password">Пароль оператора</label>
<input id="password" type="password" autocomplete="current-password" required>
<button class="accent" id="login-button" type="submit">Войти</button>
</form>
<p id="login-status" class="muted" role="status">Проверяем сессию…</p>
<p id="login-error" class="error-text" role="alert" hidden></p>
</main>
</div>
<main id="dashboard" hidden>
<header class="masthead">
<div><p class="eyebrow">TCP / ОПЕРАТОРСКАЯ</p><h1>Mirror Tool<span class="brand-mark" aria-hidden="true">↗↗</span></h1></div>
<nav class="actions" aria-label="Управление">
<button id="add-team" data-write>+ Команда</button>
<button id="add-service" class="accent" data-write>+ Сервис</button>
<button id="refresh">Обновить</button>
<button id="logout" data-write>Выйти</button>
</nav>
</header>
<section class="telemetry" aria-label="Состояние панели">
<div><span class="label">СВЯЗЬ</span><strong id="stream-state" class="warn">Подключение…</strong></div>
<div><span class="label">СЕРВИСЫ</span><strong id="service-count">—</strong></div>
<div><span class="label">FORCAD</span><strong id="scoreboard-state">—</strong></div>
<div><span class="label">СНИМОК</span><strong id="snapshot-time">—</strong></div>
</section>
<p id="scoreboard-detail" class="status-detail muted"></p>
<div id="global-error" class="notice error-text" role="alert" hidden></div>
<p id="action-status" class="action-status" role="status" aria-live="polite"></p>
<section id="service-board" class="service-board" aria-label="Сервисы"></section>
<section id="empty-state" class="empty-state" hidden><h2>Добавьте первый сервис</h2><p>Создайте команду с адресом, затем задайте порт слушателя и основной маршрут.</p></section>
<footer class="dashboard-footer"><p>◉ Основной маршрут &nbsp; □ Реплика</p><p>Изменения сохраняются сразу. Новые маршруты применяются к новым соединениям.</p></footer>
<details class="journal"><summary>Журнал событий</summary><ol id="event-log"></ol></details>
</main>
<dialog id="service-dialog" aria-labelledby="service-dialog-title">
<form id="service-form">
<header class="dialog-header"><h2 id="service-dialog-title">Добавить сервис</h2><button type="button" data-close="service-dialog" aria-label="Закрыть окно сервиса">×</button></header>
<div class="form-grid">
<label>ID<input name="id" required pattern="[A-Za-z0-9_-]+" maxlength="80" placeholder="notes"><small>Латинские буквы, цифры, _ и -</small></label>
<label>Название<input name="name" required maxlength="120" placeholder="Notes"></label>
<label>Адрес слушателя<input name="listen" required placeholder="0.0.0.0:18080"></label>
<label>Порт сервиса команды<input name="upstream_port" type="number" min="1" max="65535" required value="8080"></label>
<label>Основной маршрут<select name="primary"></select></label>
<label>Политика реплик<select name="replica_policy"><option value="isolate">Isolate — отключать отстающую</option><option value="strict">Strict — ждать все реплики</option></select></label>
</div>
<fieldset><legend>Реплики</legend><div id="dialog-replicas" class="replica-options"></div></fieldset>
<div class="form-checks"><label><input name="enabled" type="checkbox"> Запустить слушатель</label><label><input name="auto_switch" type="checkbox"> Автопереключение</label></div>
<p class="muted hint">Основной маршрут можно не выбирать у остановленного сервиса. Strict замедляет поток до скорости реплик; Isolate явно отключает отстающую реплику.</p>
<p id="service-form-error" class="error-text" role="alert" hidden></p>
<footer class="dialog-actions"><button type="button" data-close="service-dialog">Отмена</button><button type="submit" class="accent" data-write>Сохранить сервис</button></footer>
</form>
</dialog>
<dialog id="team-dialog" aria-labelledby="team-dialog-title">
<form id="team-form">
<header class="dialog-header"><h2 id="team-dialog-title">Добавить команду</h2><button type="button" data-close="team-dialog" aria-label="Закрыть окно команды">×</button></header>
<label>ID<input name="id" required pattern="[A-Za-z0-9_-]+" maxlength="80" placeholder="team-a"></label>
<label>Название<input name="name" required maxlength="120" placeholder="Команда A"></label>
<label>Адрес<input name="address" required placeholder="192.0.2.10"></label>
<p class="muted hint">Адрес существующей команды неизменяем. Для другого адреса добавьте команду с новым ID.</p>
<p id="team-form-error" class="error-text" role="alert" hidden></p>
<footer class="dialog-actions"><button type="button" data-close="team-dialog">Отмена</button><button type="submit" class="accent" data-write>Сохранить команду</button></footer>
</form>
</dialog>
<dialog id="delete-dialog" aria-labelledby="delete-title">
<form id="delete-form">
<h2 id="delete-title">Удалить сервис?</h2><p id="delete-detail"></p>
<p class="muted">Настройки сервиса будут удалены. Установленные соединения завершатся в режиме дренирования.</p>
<p id="delete-error" class="error-text" role="alert" hidden></p>
<footer class="dialog-actions"><button type="button" data-close="delete-dialog" autofocus>Отмена</button><button type="submit" class="danger" data-write>Удалить сервис</button></footer>
</form>
</dialog>
</body>
</html>