mirror of
https://github.com/mrvasil/ctf-ad-mirror-tool-rust.git
synced 2026-10-23 14:31:02 +00:00
89 lines
7.0 KiB
HTML
89 lines
7.0 KiB
HTML
<!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>◉ Основной маршрут □ Реплика</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>
|