941 lines
52 KiB
HTML
941 lines
52 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#071019">
|
||
<meta name="robots" content="noindex,nofollow">
|
||
<title>Вход — OpenWrt RMM</title>
|
||
<link rel="icon" href="/favicon.svg?v=1" type="image/svg+xml">
|
||
<link rel="icon" href="/favicon-32x32.png?v=1" sizes="32x32" type="image/png">
|
||
<link rel="shortcut icon" href="/favicon.ico?v=1">
|
||
<link rel="apple-touch-icon" href="/apple-touch-icon.png?v=1">
|
||
<link rel="manifest" href="/site.webmanifest">
|
||
<link rel="stylesheet" href="/styles.css?v=25">
|
||
</head>
|
||
<body class="account-page">
|
||
<main id="loginView" class="landing-view">
|
||
<header class="landing-header">
|
||
<a class="landing-brand" href="/" aria-label="OpenWrt RMM — главная">
|
||
<img src="/favicon.svg?v=1" alt="" width="42" height="42">
|
||
<span><strong>OpenWrt RMM</strong><small>Remote network management</small></span>
|
||
</a>
|
||
<nav aria-label="Разделы страницы">
|
||
<a href="/#capabilities">Возможности</a>
|
||
<a href="/#security">Безопасность</a>
|
||
<a class="landing-login-link" href="#login">Войти</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<section id="top" class="landing-hero">
|
||
<div class="landing-copy">
|
||
<span class="landing-kicker"><i></i> Роутеры под контролем из любой точки</span>
|
||
<h1>Единый центр управления <em>OpenWrt</em></h1>
|
||
<p>Следите за состоянием сети, клиентами и обновлениями агента. Открывайте LuCI через временный защищённый доступ без публикации панели роутера в интернет.</p>
|
||
<div class="landing-actions">
|
||
<a class="landing-primary" href="#login">Открыть кабинет</a>
|
||
<a class="landing-secondary" href="#capabilities">Как это работает</a>
|
||
</div>
|
||
<div class="landing-trust">
|
||
<span><b>●</b> Heartbeat в реальном времени</span>
|
||
<span><b>✓</b> Изоляция аккаунтов</span>
|
||
<span><b>↗</b> Временные LuCI-сессии</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="landing-console" aria-label="Пример панели мониторинга">
|
||
<div class="console-top"><span></span><span></span><span></span><small>fleet / overview</small></div>
|
||
<div class="console-summary">
|
||
<div><small>На связи</small><strong>12</strong></div>
|
||
<div><small>Требуют внимания</small><strong class="warn">2</strong></div>
|
||
<div><small>Средний uptime</small><strong>18 дн.</strong></div>
|
||
</div>
|
||
<div class="console-router online"><i></i><span><strong>Домашняя сеть</strong><small>OpenWrt 25.12 · 8 клиентов</small></span><b>На связи</b></div>
|
||
<div class="console-router online"><i></i><span><strong>Офис</strong><small>OpenWrt 24.10 · 24 клиента</small></span><b>На связи</b></div>
|
||
<div class="console-router warning"><i></i><span><strong>Загородный дом</strong><small>Высокая загрузка процессора</small></span><b>Проверить</b></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="capabilities" class="landing-features">
|
||
<article><span>01</span><h2>Состояние сети</h2><p>WAN, нагрузка, память, интерфейсы и активные клиенты в одном понятном обзоре.</p></article>
|
||
<article><span>02</span><h2>Безопасный LuCI</h2><p>Доступ создаётся по запросу, ограничен по времени и привязан к конкретному роутеру.</p></article>
|
||
<article><span>03</span><h2>Свои устройства</h2><p>Каждый пользователь видит только добавленные им роутеры и собственные DNS-имена.</p></article>
|
||
</section>
|
||
|
||
<section class="landing-partners" aria-labelledby="partnersTitle">
|
||
<div class="landing-partners-copy">
|
||
<span class="eyebrow">Экосистема</span>
|
||
<h2 id="partnersTitle">Наши партнёры</h2>
|
||
<p>Компании, с которыми мы развиваем инфраструктуру и сетевые сервисы.</p>
|
||
</div>
|
||
<div class="partner-logos" aria-label="Компании-партнёры">
|
||
<div class="partner-logo"><img src="/assets/partners/ks.png" alt="KS" width="512" height="512" loading="lazy"></div>
|
||
<div class="partner-logo"><img src="/assets/partners/vz.png" alt="VZ" width="512" height="512" loading="lazy"></div>
|
||
<div class="partner-logo"><img src="/assets/partners/partner-blue.png" alt="Партнёр OpenWrt RMM" width="240" height="240" loading="lazy"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="security" class="landing-security">
|
||
<div><span class="eyebrow">Безопасность по умолчанию</span><h2>Панель управления не должна становиться новой точкой входа</h2></div>
|
||
<ul><li>Пароли защищены Argon2id</li><li>Сессии в HttpOnly cookie</li><li>Все важные действия попадают в аудит</li></ul>
|
||
</section>
|
||
|
||
<section id="login" class="landing-login-section">
|
||
<div><span class="eyebrow">Личный кабинет</span><h2>Продолжить работу</h2><p>Используйте данные своего аккаунта. Администратор может создать отдельный доступ для каждого пользователя.</p></div>
|
||
<form id="loginForm" class="login-form">
|
||
<label>Логин<input id="loginUsername" type="text" autocomplete="username" placeholder="Введите логин" required></label>
|
||
<label>Пароль<input id="loginPassword" type="password" autocomplete="current-password" required></label>
|
||
<button class="primary" type="submit">Войти в RMM</button>
|
||
<button id="forgotPasswordBtn" class="text-button" type="button">Забыли пароль?</button>
|
||
<p id="loginError" class="login-error" role="alert"></p>
|
||
</form>
|
||
</section>
|
||
<footer class="landing-footer">
|
||
<span>OpenWrt RMM</span>
|
||
<span class="landing-footer-links"><small>Безопасное управление вашей сетью</small><a href="/legal.html">Лицензия и исходный код</a></span>
|
||
</footer>
|
||
</main>
|
||
|
||
<div id="appShell" class="app-shell is-hidden">
|
||
<aside class="sidebar">
|
||
<div class="brand">
|
||
<img class="brand-mark" src="/favicon.svg?v=1" alt="" width="42" height="42">
|
||
<div>
|
||
<h1>OpenWrt RMM</h1>
|
||
<p id="apiState">Проверка сервера</p>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="main-nav" aria-label="Основная навигация">
|
||
<button id="fleetNavBtn" class="nav-item is-active" type="button">
|
||
<span class="nav-icon">◫</span>
|
||
Объекты
|
||
</button>
|
||
<button id="problemsNavBtn" class="nav-item" type="button">
|
||
<span class="nav-icon">!</span>
|
||
Проблемы
|
||
<span id="navAlertCount" class="nav-count">0</span>
|
||
</button>
|
||
<button id="operationsNavBtn" class="nav-item" type="button">
|
||
<span class="nav-icon">⌁</span>
|
||
Операции
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="sidebar-spacer"></div>
|
||
|
||
<a class="sidebar-legal-link" href="/legal.html">Лицензия · Исходный код</a>
|
||
|
||
<div class="operator-row">
|
||
<button id="profileBtn" class="operator-profile" type="button" aria-label="Открыть личный кабинет">
|
||
<span class="operator-avatar">О</span>
|
||
<span class="operator-copy">
|
||
<small>Личный кабинет</small>
|
||
<strong id="operatorName">operator</strong>
|
||
</span>
|
||
</button>
|
||
<button id="logoutBtn" class="icon-button" type="button" title="Выйти" aria-label="Выйти">↪</button>
|
||
</div>
|
||
|
||
<div class="bulk-command-grid is-hidden" aria-hidden="true">
|
||
<select id="bulkCommandType">
|
||
<option value="ping">ping</option>
|
||
<option value="traceroute">traceroute</option>
|
||
<option value="pkg_list_installed">pkg_list_installed</option>
|
||
</select>
|
||
<input id="bulkCommandTarget" type="text" placeholder="цель">
|
||
<button id="sendBulkCommandBtn" type="button">Запустить для выбранных</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<main class="workspace">
|
||
<header class="topbar">
|
||
<div>
|
||
<p class="eyebrow">Управление</p>
|
||
<h2 id="pageTitle">Объекты</h2>
|
||
</div>
|
||
<div class="topbar-actions">
|
||
<span id="liveState" class="live-state is-connecting" title="Состояние автоматического обновления"><i></i><span>Подключение</span></span>
|
||
<span id="statusLine" class="status-line">Готово</span>
|
||
<button id="addRouterBtn" class="primary" type="button">Добавить роутер</button>
|
||
<button id="addUserBtn" class="is-hidden" type="button">Новый пользователь</button>
|
||
<button id="refreshBtn" class="icon-button" type="button" title="Обновить данные" aria-label="Обновить данные">↻</button>
|
||
</div>
|
||
</header>
|
||
|
||
<section id="fleetView" class="fleet-view">
|
||
<div class="fleet-summary">
|
||
<div>
|
||
<span>Всего</span>
|
||
<strong id="fleetTotalCount">0</strong>
|
||
</div>
|
||
<div class="summary-good">
|
||
<span>На связи</span>
|
||
<strong id="fleetOnlineCount">0</strong>
|
||
</div>
|
||
<div class="summary-bad">
|
||
<span>Не на связи</span>
|
||
<strong id="fleetOfflineCount">0</strong>
|
||
</div>
|
||
<div class="summary-warn">
|
||
<span>Требуют внимания</span>
|
||
<strong id="fleetAlertCount">0</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="fleet-toolbar">
|
||
<input id="fleetSearch" type="search" placeholder="Поиск по имени, модели, ID или тегу">
|
||
<button id="fleetFilterToggle" class="filter-toggle" type="button" aria-expanded="false">Фильтры</button>
|
||
<div class="filters">
|
||
<button class="filter is-active" data-filter="all" type="button">Все</button>
|
||
<button class="filter" data-filter="online" type="button">На связи</button>
|
||
<button class="filter" data-filter="offline" type="button">Не на связи</button>
|
||
<button class="filter" data-filter="alerts" type="button">Проблемы</button>
|
||
</div>
|
||
<div id="fleetAdvancedFilters" class="fleet-advanced-filters">
|
||
<input id="fleetGroupFilter" type="text" placeholder="Группа">
|
||
<input id="fleetTagFilter" type="text" placeholder="Тег">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="fleet-table-shell">
|
||
<div class="fleet-table-head">
|
||
<span>Статус</span>
|
||
<span>Объект</span>
|
||
<span>Модель и группа</span>
|
||
<span>WAN / интернет</span>
|
||
<span>Онлайн / всего</span>
|
||
<span>Проблемы</span>
|
||
<span>Последний контакт</span>
|
||
<span></span>
|
||
</div>
|
||
<div id="deviceList" class="device-list"></div>
|
||
</div>
|
||
|
||
<section id="emptyState" class="empty-state is-hidden">
|
||
<h2>Объекты не найдены</h2>
|
||
<p>Измените фильтры или дождитесь подключения агента.</p>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="deviceView" class="device-view is-hidden">
|
||
<div class="device-header">
|
||
<div>
|
||
<button id="backToFleetBtn" class="back-button" type="button">← Объекты</button>
|
||
<p class="eyebrow">Объект</p>
|
||
<h2 id="deviceName"></h2>
|
||
<p id="deviceMeta"></p>
|
||
</div>
|
||
<div class="device-header-actions">
|
||
<span id="deviceBadge" class="badge"></span>
|
||
<button id="quickDiagnosticBtn" type="button">Диагностика</button>
|
||
<button id="openLuciBtn" class="primary" type="button">Открыть LuCI</button>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="device-tabs" aria-label="Разделы объекта" role="tablist">
|
||
<button class="device-tab is-active" data-device-tab-target="overview" type="button" role="tab" aria-selected="true">Обзор</button>
|
||
<button class="device-tab" data-device-tab-target="clients" type="button" role="tab" aria-selected="false">Клиенты</button>
|
||
<button class="device-tab" data-device-tab-target="network" type="button" role="tab" aria-selected="false">Сеть</button>
|
||
<button class="device-tab" data-device-tab-target="operations" type="button" role="tab" aria-selected="false">Операции</button>
|
||
<button class="device-tab" data-device-tab-target="information" type="button" role="tab" aria-selected="false">Сведения</button>
|
||
<button class="device-tab" data-device-tab-target="expert" type="button" role="tab" aria-selected="false">Эксперт</button>
|
||
</nav>
|
||
|
||
<section class="panel overview-status-panel" data-device-tab="overview">
|
||
<div class="section-title">
|
||
<h2>Состояние объекта</h2>
|
||
<span class="summary-pill">обновляется автоматически</span>
|
||
</div>
|
||
<div id="deviceStatusHero" class="device-status-hero"></div>
|
||
<div id="healthSummary" class="health-summary"></div>
|
||
<div id="agentHealthSummary" class="agent-health-summary"></div>
|
||
</section>
|
||
|
||
<section class="panel device-information-panel" data-device-tab="information">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Сведения об устройстве</h2>
|
||
<p class="section-subtitle">Основная информация, полученная от роутера</p>
|
||
</div>
|
||
<span id="informationStatus" class="badge">-</span>
|
||
</div>
|
||
<div class="information-grid">
|
||
<div class="information-item">
|
||
<span>Имя устройства</span>
|
||
<strong id="infoHostname">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Модель</span>
|
||
<strong id="infoModel">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Версия OpenWrt</span>
|
||
<strong id="infoOpenWrt">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Платформа</span>
|
||
<strong id="infoTarget">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Система</span>
|
||
<strong id="infoSystem">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Версия ядра</span>
|
||
<strong id="infoKernel">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Версия агента</span>
|
||
<strong id="infoAgentVersion">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Зарегистрирован</span>
|
||
<strong id="infoCreatedAt">-</strong>
|
||
</div>
|
||
<div class="information-item">
|
||
<span>Последний контакт</span>
|
||
<strong id="infoLastSeenAt">-</strong>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="information">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Организация</h2>
|
||
<p class="section-subtitle">Группа и теги помогают находить и фильтровать объекты</p>
|
||
</div>
|
||
<button id="saveFleetBtn" class="primary" type="button">Сохранить</button>
|
||
</div>
|
||
<div class="fleet-form">
|
||
<label>
|
||
Группа
|
||
<input id="fleetGroup" type="text" placeholder="Офис, дом, филиал">
|
||
</label>
|
||
<label>
|
||
Теги через запятую
|
||
<input id="fleetTags" type="text" placeholder="VPN, тестовый, резервный">
|
||
</label>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="information">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Технические реквизиты</h2>
|
||
<p class="section-subtitle">Используются при диагностике и обращении в поддержку</p>
|
||
</div>
|
||
</div>
|
||
<div class="technical-details">
|
||
<div>
|
||
<span>ID устройства</span>
|
||
<code id="infoDeviceId">-</code>
|
||
<button class="copy-info-btn" data-copy-info="infoDeviceId" type="button">Копировать</button>
|
||
</div>
|
||
<div>
|
||
<span>Board name</span>
|
||
<code id="infoBoardName">-</code>
|
||
<button class="copy-info-btn" data-copy-info="infoBoardName" type="button">Копировать</button>
|
||
</div>
|
||
<div>
|
||
<span>Root filesystem</span>
|
||
<code id="infoRootfs">-</code>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="metrics-grid" data-device-tab="overview">
|
||
<div class="metric">
|
||
<span>Последний контакт</span>
|
||
<strong id="lastSeen">-</strong>
|
||
</div>
|
||
<div class="metric">
|
||
<span>Нагрузка</span>
|
||
<strong id="loadAvg">-</strong>
|
||
</div>
|
||
<div class="metric">
|
||
<span>Время работы</span>
|
||
<strong id="uptime">-</strong>
|
||
</div>
|
||
<div class="metric secondary-metric">
|
||
<span>Маршрут по умолчанию</span>
|
||
<strong id="defaultRoute">-</strong>
|
||
</div>
|
||
<div class="metric">
|
||
<span>WAN-адрес</span>
|
||
<strong id="wanIp">-</strong>
|
||
</div>
|
||
<div class="metric">
|
||
<span>Память</span>
|
||
<strong id="memoryUsage">-</strong>
|
||
</div>
|
||
<div class="metric secondary-metric">
|
||
<span>Хранилище</span>
|
||
<strong id="diskUsage">-</strong>
|
||
</div>
|
||
<div class="metric">
|
||
<span>Клиенты</span>
|
||
<strong id="clientCounts">-</strong>
|
||
</div>
|
||
<div class="metric secondary-metric">
|
||
<span>Проверки связи</span>
|
||
<strong id="connectivityStatus">-</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="expert-notice" data-device-tab="expert">
|
||
<div>
|
||
<strong>Экспертный режим</strong>
|
||
<span>Здесь находятся низкоуровневые команды и настройки OpenWrt. Ошибочное изменение может нарушить связь с роутером.</span>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="two-column" data-device-tab="expert">
|
||
<section class="panel">
|
||
<div class="section-title">
|
||
<h2>Inventory</h2>
|
||
</div>
|
||
<pre id="inventoryJson" class="code-block"></pre>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<div class="section-title">
|
||
<h2>Команда вручную</h2>
|
||
</div>
|
||
<div class="command-form">
|
||
<select id="commandType">
|
||
<option value="ping">ping</option>
|
||
<option value="traceroute">traceroute</option>
|
||
<option value="route_show">show routes</option>
|
||
<option value="interfaces_show">show interfaces</option>
|
||
<option value="pkg_list_installed">pkg_list_installed</option>
|
||
<option value="reboot">reboot</option>
|
||
</select>
|
||
<input id="commandTarget" type="text" placeholder="1.1.1.1">
|
||
<button id="sendCommandBtn" type="button">Запустить</button>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<section class="panel" data-device-tab="operations">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Диагностика</h2>
|
||
<p class="section-subtitle">Проверки выполняются агентом без подключения по SSH</p>
|
||
</div>
|
||
<button id="runFullDiagnosticBtn" class="primary" type="button">Запустить полную диагностику</button>
|
||
</div>
|
||
<div id="diagnosticStatus" class="operation-status">
|
||
<span class="operation-icon">✓</span>
|
||
<div>
|
||
<strong>Готово к проверке</strong>
|
||
<small>Будут проверены сервер, интернет, маршруты и интерфейсы</small>
|
||
</div>
|
||
</div>
|
||
<h3 class="operation-subheading">Выборочная проверка</h3>
|
||
<div class="diagnostic-grid">
|
||
<button class="diagnostic-btn" data-diagnostic="ping_server" type="button">Проверить связь с сервером</button>
|
||
<button class="diagnostic-btn" data-diagnostic="ping_internet" type="button">Проверить интернет</button>
|
||
<button class="diagnostic-btn" data-diagnostic="traceroute_internet" type="button">Маршрут до интернета</button>
|
||
<button class="diagnostic-btn" data-diagnostic="show_routes" type="button">Показать маршруты</button>
|
||
<button class="diagnostic-btn" data-diagnostic="show_interfaces" type="button">Показать интерфейсы</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="clients">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Клиенты сети</h2>
|
||
<p class="section-subtitle">Онлайн подтверждается Wi-Fi или таблицей соседей; DHCP-аренда сама по себе не означает подключение</p>
|
||
</div>
|
||
<span id="clientSummary" class="summary-pill">0 клиентов</span>
|
||
</div>
|
||
<div class="client-toolbar">
|
||
<input id="clientSearch" type="search" placeholder="Поиск по имени, IP или MAC">
|
||
<div class="filters client-filters">
|
||
<button class="client-filter is-active" data-client-filter="all" type="button">Все</button>
|
||
<button class="client-filter" data-client-filter="online" type="button">Сейчас в сети</button>
|
||
<button class="client-filter" data-client-filter="wifi" type="button">Wi-Fi</button>
|
||
<button class="client-filter" data-client-filter="wired" type="button">Проводные</button>
|
||
<button class="client-filter" data-client-filter="unconfirmed" type="button">Не подтверждены</button>
|
||
</div>
|
||
</div>
|
||
<div class="client-table-head">
|
||
<span>Устройство</span>
|
||
<span>IP-адрес</span>
|
||
<span>MAC-адрес</span>
|
||
<span>Подключение</span>
|
||
<span>Сигнал / скорость</span>
|
||
<span>Состояние</span>
|
||
</div>
|
||
<div id="clientList" class="client-list"></div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="network">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Сеть и интерфейсы</h2>
|
||
<p class="section-subtitle">Адреса и трафик интерфейсов роутера</p>
|
||
</div>
|
||
<span id="networkSummary" class="summary-pill">0 интерфейсов</span>
|
||
</div>
|
||
<div id="networkHealth" class="network-health"></div>
|
||
<div class="network-table-head">
|
||
<span>Интерфейс</span>
|
||
<span>Адреса</span>
|
||
<span>Получено</span>
|
||
<span>Передано</span>
|
||
<span>Ошибки</span>
|
||
</div>
|
||
<div id="interfaceCounters" class="network-list"></div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="overview">
|
||
<div class="section-title">
|
||
<h2>Проблемы</h2>
|
||
<div class="section-actions">
|
||
<select id="alertStatusFilter" aria-label="Фильтр алертов">
|
||
<option value="open">Открытые</option>
|
||
<option value="all">Все</option>
|
||
<option value="resolved">Resolved</option>
|
||
</select>
|
||
<span id="alertSummary" class="summary-pill">0 active</span>
|
||
</div>
|
||
</div>
|
||
<div id="alertList" class="table-list"></div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="overview">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>История мониторинга</h2>
|
||
<p class="section-subtitle">Последние показатели с временной шкалой</p>
|
||
</div>
|
||
<span id="metricsHistorySummary" class="summary-pill">12 замеров</span>
|
||
</div>
|
||
<div id="metricsHistory" class="metrics-chart-grid"></div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="expert">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Передача роутера</h2>
|
||
<p class="section-subtitle">Новый владелец получит управление устройством. Текущие LuCI-сессии будут закрыты.</p>
|
||
</div>
|
||
</div>
|
||
<form id="deviceTransferForm" class="device-transfer-form">
|
||
<label>Логин нового владельца<input id="transferUsername" type="text" minlength="3" maxlength="64" autocomplete="off" required></label>
|
||
<label>Ваш текущий пароль<input id="transferPassword" type="password" autocomplete="current-password" required></label>
|
||
<button class="danger" type="submit">Передать роутер</button>
|
||
<p id="transferMessage" class="form-message" role="status"></p>
|
||
</form>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="expert">
|
||
<div class="section-title">
|
||
<h2>UCI configuration</h2>
|
||
</div>
|
||
<div class="uci-grid">
|
||
<label>
|
||
Package
|
||
<select id="uciConfig">
|
||
<option value="network">network</option>
|
||
<option value="wireless">wireless</option>
|
||
<option value="dhcp">dhcp</option>
|
||
<option value="firewall">firewall</option>
|
||
<option value="system">system</option>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Section
|
||
<input id="uciSection" type="text" placeholder="lan or @wifi-iface[0]">
|
||
</label>
|
||
<label>
|
||
Option
|
||
<input id="uciOption" type="text" placeholder="ipaddr">
|
||
</label>
|
||
<label>
|
||
Value
|
||
<input id="uciValue" type="text" placeholder="192.168.1.1">
|
||
</label>
|
||
<label class="check-row">
|
||
<input id="uciCommitNow" type="checkbox">
|
||
Commit immediately
|
||
</label>
|
||
</div>
|
||
<div class="uci-actions">
|
||
<button id="uciBackupBtn" type="button">Backup</button>
|
||
<button id="uciPreviewBtn" type="button">Preview</button>
|
||
<button id="uciShowBtn" type="button">Show</button>
|
||
<button id="uciSetBtn" type="button">Stage set</button>
|
||
<button id="uciCommitBtn" type="button">Commit</button>
|
||
<button id="uciCommitConfirmedBtn" type="button">Commit + check</button>
|
||
<button id="uciRevertBtn" type="button">Revert</button>
|
||
<button id="uciRestoreBtn" type="button">Restore backup</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="expert">
|
||
<div class="section-title">
|
||
<h2>Packages</h2>
|
||
</div>
|
||
<div class="package-grid">
|
||
<select id="packageCommand">
|
||
<option value="pkg_list_installed">List installed</option>
|
||
<option value="pkg_update">Update package index</option>
|
||
<option value="pkg_list_upgradable">List upgradable</option>
|
||
<option value="pkg_install">Install package</option>
|
||
<option value="pkg_remove">Remove package</option>
|
||
</select>
|
||
<input id="packageName" type="text" placeholder="package name">
|
||
<button id="sendPackageCommandBtn" type="button">Queue</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="remoteAccessPanel" class="panel" data-device-tab="operations">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Удаленный доступ</h2>
|
||
<p class="section-subtitle">Временный доступ к LuCI и SSH автоматически закроется по таймеру</p>
|
||
</div>
|
||
<div class="section-actions">
|
||
<span id="remoteSummary" class="summary-pill">Нет активных сессий</span>
|
||
</div>
|
||
</div>
|
||
<div id="cloudAccessCard" class="cloud-access-card" data-state="closed">
|
||
<div class="cloud-access-icon" aria-hidden="true">☁</div>
|
||
<div class="cloud-access-copy">
|
||
<strong>Доступ через облако RMM</strong>
|
||
<span id="cloudAccessStatus">Соединение создаётся только по вашему запросу</span>
|
||
</div>
|
||
<button id="openCloudAccessBtn" class="primary" type="button">Открыть LuCI</button>
|
||
</div>
|
||
<details class="advanced-settings">
|
||
<summary>Экспертный режим: SSH и параметры туннеля</summary>
|
||
<div class="remote-quick-form">
|
||
<label>
|
||
Длительность доступа
|
||
<select id="remoteDuration">
|
||
<option value="900">15 минут</option>
|
||
<option value="1800">30 минут</option>
|
||
<option value="3600">1 час</option>
|
||
<option value="7200">2 часа</option>
|
||
</select>
|
||
</label>
|
||
<button id="createRemoteSessionBtn" type="button">Создать ручную сессию</button>
|
||
</div>
|
||
<div class="remote-form">
|
||
<label>
|
||
Сервер туннеля
|
||
<input id="remoteServerHost" type="text" placeholder="10.10.10.2">
|
||
</label>
|
||
<label>
|
||
SSH-порт сервера
|
||
<input id="remoteServerPort" type="number" min="1" max="65535" value="2222">
|
||
</label>
|
||
<label>
|
||
Внешний порт
|
||
<input id="remotePort" type="number" min="1" max="65535" placeholder="автоматически">
|
||
</label>
|
||
<label>
|
||
SSH-порт роутера
|
||
<input id="remoteLocalPort" type="number" min="1" max="65535" value="22">
|
||
</label>
|
||
<label>
|
||
Протокол LuCI
|
||
<select id="remoteLuCIScheme">
|
||
<option value="http">HTTP</option>
|
||
<option value="https">HTTPS</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
</details>
|
||
<div id="remoteSessionList" class="table-list remote-session-list"></div>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="operations">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Быстрые настройки</h2>
|
||
<p class="section-subtitle">Сначала проверьте изменения, затем примените их</p>
|
||
</div>
|
||
</div>
|
||
<div class="preset-grid">
|
||
<div class="preset">
|
||
<h3>LAN-адрес</h3>
|
||
<p>Изменяет адрес локальной сети роутера</p>
|
||
<input id="presetLanIp" type="text" placeholder="10.10.10.1/24">
|
||
<button class="preset-review-btn" data-preset="lan_ip" type="button">Проверить изменение</button>
|
||
</div>
|
||
<div class="preset">
|
||
<h3>Имя устройства</h3>
|
||
<p>Изменяет hostname роутера</p>
|
||
<input id="presetHostname" type="text" placeholder="OpenWrt">
|
||
<button class="preset-review-btn" data-preset="hostname" type="button">Проверить изменение</button>
|
||
</div>
|
||
<div class="preset">
|
||
<h3>Название Wi-Fi</h3>
|
||
<p>Изменяет SSID основной беспроводной сети</p>
|
||
<input id="presetWifiSsid" type="text" placeholder="SSID">
|
||
<button class="preset-review-btn" data-preset="wifi_ssid" type="button">Проверить изменение</button>
|
||
</div>
|
||
<div class="preset">
|
||
<h3>Пароль Wi-Fi</h3>
|
||
<p>Изменяет пароль основной беспроводной сети</p>
|
||
<input id="presetWifiKey" type="password" placeholder="Новый пароль">
|
||
<button class="preset-review-btn" data-preset="wifi_key" type="button">Проверить изменение</button>
|
||
</div>
|
||
<div class="preset">
|
||
<h3>DHCP-сервер</h3>
|
||
<p>Включает или отключает выдачу адресов в LAN</p>
|
||
<select id="presetDhcpLan">
|
||
<option value="0">Включен</option>
|
||
<option value="1">Отключен</option>
|
||
</select>
|
||
<button class="preset-review-btn" data-preset="dhcp_lan" type="button">Проверить изменение</button>
|
||
</div>
|
||
</div>
|
||
<section id="presetReviewPanel" class="preset-review-panel is-hidden">
|
||
<div class="preset-review-header">
|
||
<div>
|
||
<span>Подготовленное изменение</span>
|
||
<strong id="presetReviewTitle">-</strong>
|
||
</div>
|
||
<span id="presetReviewStatus" class="summary-pill">Проверка</span>
|
||
</div>
|
||
<div id="presetReviewChange" class="preset-review-change"></div>
|
||
<pre id="presetReviewOutput" class="preset-review-output">Ожидание результата проверки...</pre>
|
||
<div class="preset-review-actions">
|
||
<button id="cancelPresetReviewBtn" type="button">Отменить</button>
|
||
<button id="applyPresetReviewBtn" class="primary" type="button" disabled>Применить безопасно</button>
|
||
</div>
|
||
<p class="preset-safety-note">После применения роутер проверит связь с сервером. Если связь пропадет, конфигурация будет автоматически восстановлена.</p>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="commandsPanel" class="panel" data-device-tab="expert">
|
||
<div class="section-title">
|
||
<h2>История команд</h2>
|
||
<div class="section-actions">
|
||
<span id="commandSummary" class="summary-pill">0 total</span>
|
||
</div>
|
||
</div>
|
||
<div class="filters command-filters">
|
||
<button class="command-filter is-active" data-command-filter="all" type="button">Все</button>
|
||
<button class="command-filter" data-command-filter="running" type="button">Выполняются</button>
|
||
<button class="command-filter" data-command-filter="failed" type="button">Ошибки</button>
|
||
<button class="command-filter" data-command-filter="completed" type="button">Готовые</button>
|
||
<select id="commandStatusFilter" aria-label="Точный статус команды">
|
||
<option value="">Любой статус</option>
|
||
<option value="queued">В очереди</option>
|
||
<option value="claimed">Выполняется</option>
|
||
<option value="completed">Готово</option>
|
||
<option value="failed">Ошибка</option>
|
||
<option value="expired">Истекла</option>
|
||
<option value="cancelled">Отменена</option>
|
||
</select>
|
||
</div>
|
||
<div id="commandList" class="table-list"></div>
|
||
<button id="loadMoreCommandsBtn" class="load-more-btn is-hidden" type="button">Загрузить ещё</button>
|
||
</section>
|
||
|
||
<section id="commandDetailPanel" class="panel is-hidden" data-device-tab="expert">
|
||
<div class="section-title">
|
||
<h2>Детали команды</h2>
|
||
<button id="copyCommandOutputBtn" type="button">Копировать вывод</button>
|
||
</div>
|
||
<div id="commandDetailMeta" class="detail-meta"></div>
|
||
<pre id="commandDetailOutput" class="command-detail-output"></pre>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="expert">
|
||
<div class="section-title">
|
||
<h2>Audit</h2>
|
||
<div class="section-actions">
|
||
<span id="auditSummary" class="summary-pill">0 events</span>
|
||
</div>
|
||
</div>
|
||
<div id="auditList" class="table-list"></div>
|
||
<button id="loadMoreAuditBtn" class="load-more-btn is-hidden" type="button">Загрузить ещё</button>
|
||
</section>
|
||
|
||
<section class="panel" data-device-tab="expert">
|
||
<div class="section-title">
|
||
<div>
|
||
<h2>Обслуживание данных</h2>
|
||
<p class="section-subtitle">Очистка локальной истории RMM. Настройки роутера не меняются.</p>
|
||
</div>
|
||
</div>
|
||
<div class="maintenance-grid">
|
||
<button id="clearAlertsBtn" type="button">Очистить алерты</button>
|
||
<button id="clearCommandsBtn" type="button">Очистить историю команд</button>
|
||
<button id="clearAuditBtn" type="button">Очистить аудит устройства</button>
|
||
<button id="deleteDeviceBtn" class="danger" type="button">Удалить устройство</button>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
</main>
|
||
|
||
<nav class="mobile-nav" aria-label="Мобильная навигация">
|
||
<button class="mobile-nav-item is-active" data-mobile-route="fleet" type="button"><span>◫</span>Объекты</button>
|
||
<button class="mobile-nav-item" data-mobile-route="problems" type="button"><span>!</span>Проблемы</button>
|
||
<button class="mobile-nav-item" data-mobile-route="operations" type="button"><span>⌁</span>Операции</button>
|
||
<button class="mobile-nav-item" data-mobile-route="profile" type="button"><span>○</span>Профиль</button>
|
||
</nav>
|
||
</div>
|
||
|
||
<dialog id="enrollmentGrantDialog">
|
||
<form method="dialog" class="login-form">
|
||
<h2>Добавление роутера</h2>
|
||
<p>Grant действует 15 минут и используется только один раз. Вставьте его в LuCI → Службы → RMM agent.</p>
|
||
<label>
|
||
Одноразовый grant
|
||
<input id="enrollmentTokenOutput" type="text" readonly>
|
||
</label>
|
||
<div class="topbar-actions">
|
||
<button id="copyEnrollmentTokenBtn" type="button">Копировать</button>
|
||
<button class="primary" type="submit">Готово</button>
|
||
</div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="createUserDialog">
|
||
<form id="createUserForm" method="dialog" class="login-form">
|
||
<h2>Новый пользователь</h2>
|
||
<label>
|
||
Логин
|
||
<input id="newUsername" type="text" minlength="3" maxlength="64" autocomplete="off" required>
|
||
</label>
|
||
<label>
|
||
E-mail для восстановления
|
||
<input id="newUserEmail" type="email" maxlength="254" autocomplete="off" placeholder="name@example.com">
|
||
</label>
|
||
<label>
|
||
Временный пароль
|
||
<input id="newUserPassword" type="password" minlength="12" maxlength="1024" autocomplete="new-password" required>
|
||
</label>
|
||
<label>
|
||
Роль
|
||
<select id="newUserRole"><option value="user">Пользователь</option><option value="admin">Администратор</option></select>
|
||
</label>
|
||
<div class="topbar-actions">
|
||
<button id="cancelCreateUserBtn" value="cancel" type="button">Отмена</button>
|
||
<button class="primary" type="submit">Создать</button>
|
||
</div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="profileDialog" class="profile-dialog">
|
||
<div class="profile-sheet">
|
||
<div class="dialog-heading">
|
||
<div>
|
||
<span class="eyebrow">Аккаунт пользователя</span>
|
||
<h2>Личный кабинет</h2>
|
||
</div>
|
||
<button id="closeProfileBtn" class="icon-button" type="button" aria-label="Закрыть">×</button>
|
||
</div>
|
||
<div class="profile-identity">
|
||
<span id="profileAvatar" class="profile-avatar">О</span>
|
||
<div>
|
||
<strong id="profileUsername">operator</strong>
|
||
<span id="profileRole">Пользователь</span>
|
||
</div>
|
||
</div>
|
||
<form id="profileForm" class="profile-form">
|
||
<div class="profile-section-heading"><strong>Контактные данные</strong><span>Используются только в вашем аккаунте</span></div>
|
||
<label>Отображаемое имя<input id="profileDisplayName" type="text" maxlength="80" autocomplete="name" placeholder="Как к вам обращаться"></label>
|
||
<label>E-mail<input id="profileEmail" type="email" maxlength="254" autocomplete="email" placeholder="name@example.com"></label>
|
||
<button class="primary" type="submit">Сохранить профиль</button>
|
||
<p id="profileMessage" class="form-message" role="status"></p>
|
||
</form>
|
||
<form id="passwordForm" class="profile-form profile-security-form">
|
||
<div class="profile-section-heading"><strong>Смена пароля</strong><span>Остальные активные сессии будут завершены</span></div>
|
||
<label>Текущий пароль<input id="currentPassword" type="password" autocomplete="current-password" required></label>
|
||
<label>Новый пароль<input id="newPassword" type="password" minlength="12" maxlength="1024" autocomplete="new-password" required></label>
|
||
<label>Повторите новый пароль<input id="confirmPassword" type="password" minlength="12" maxlength="1024" autocomplete="new-password" required></label>
|
||
<button type="submit">Изменить пароль</button>
|
||
<p id="passwordMessage" class="form-message" role="status"></p>
|
||
</form>
|
||
<form id="notificationSettingsForm" class="profile-form notification-settings-form">
|
||
<div class="profile-section-heading"><strong>Уведомления и пороги</strong><span>Настройки применяются к вашим роутерам</span></div>
|
||
<div class="notification-channel-grid">
|
||
<label class="notification-toggle"><input id="notificationEmailEnabled" type="checkbox"><span><strong>E-mail</strong><small id="notificationEmailHint">Используется e-mail из профиля</small></span></label>
|
||
<label class="notification-toggle"><input id="notificationTelegramEnabled" type="checkbox"><span><strong>Telegram</strong><small id="notificationTelegramHint">Нужен Chat ID</small></span></label>
|
||
</div>
|
||
<label id="notificationTelegramChatRow">Telegram Chat ID<input id="notificationTelegramChatId" type="text" maxlength="32" inputmode="numeric" placeholder="Например, 123456789"></label>
|
||
<div class="notification-toggle-grid">
|
||
<label class="notification-toggle compact"><input id="notificationWarningEnabled" type="checkbox"><span>Предупреждения</span></label>
|
||
<label class="notification-toggle compact"><input id="notificationCriticalEnabled" type="checkbox"><span>Критические</span></label>
|
||
<label class="notification-toggle compact"><input id="notificationResolvedEnabled" type="checkbox"><span>Восстановление</span></label>
|
||
</div>
|
||
<div class="notification-threshold-grid">
|
||
<label>Память, %<input id="notificationMemoryThreshold" type="number" min="50" max="99" required></label>
|
||
<label>Диск, %<input id="notificationDiskThreshold" type="number" min="50" max="99" required></label>
|
||
<label>Потери, %<input id="notificationPacketLossThreshold" type="number" min="1" max="100" required></label>
|
||
<label>Задержка, мс<input id="notificationLatencyThreshold" type="number" min="10" max="5000" required></label>
|
||
</div>
|
||
<label>Повторять активную проблему<select id="notificationRepeatMinutes"><option value="0">Не повторять</option><option value="15">Каждые 15 минут</option><option value="60">Каждый час</option><option value="360">Каждые 6 часов</option><option value="1440">Раз в сутки</option></select></label>
|
||
<div class="notification-form-actions"><button class="primary" type="submit">Сохранить уведомления</button><button id="testNotificationsBtn" type="button">Отправить тест</button></div>
|
||
<p id="notificationSettingsMessage" class="form-message" role="status"></p>
|
||
<div class="notification-history-heading"><strong>Последние отправки</strong><button id="refreshNotificationsBtn" class="text-button" type="button">Обновить</button></div>
|
||
<div id="notificationHistory" class="notification-history"></div>
|
||
</form>
|
||
<section id="userManagementSection" class="profile-form user-management is-hidden">
|
||
<div class="profile-section-heading"><strong>Пользователи</strong><span>Роли и доступы всех аккаунтов</span></div>
|
||
<div id="userList" class="user-list"></div>
|
||
</section>
|
||
<div class="profile-session-actions">
|
||
<button id="logoutAllBtn" class="danger" type="button">Завершить все сессии</button>
|
||
<button id="profileLogoutBtn" type="button">Выйти на этом устройстве</button>
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
|
||
<dialog id="forgotPasswordDialog">
|
||
<form id="forgotPasswordForm" class="login-form">
|
||
<div class="dialog-heading"><div><span class="eyebrow">Восстановление доступа</span><h2>Сбросить пароль</h2></div><button id="closeForgotPasswordBtn" class="icon-button" type="button" aria-label="Закрыть">×</button></div>
|
||
<p>Введите логин или e-mail. Если для аккаунта настроено восстановление, мы отправим одноразовую ссылку на 30 минут.</p>
|
||
<label>Логин или e-mail<input id="passwordResetIdentifier" type="text" maxlength="254" autocomplete="username" required></label>
|
||
<button class="primary" type="submit">Отправить ссылку</button>
|
||
<p id="forgotPasswordMessage" class="form-message" role="status"></p>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="passwordResetDialog">
|
||
<form id="passwordResetForm" class="login-form">
|
||
<div class="dialog-heading"><div><span class="eyebrow">Новый пароль</span><h2>Восстановление доступа</h2></div><button id="closePasswordResetBtn" class="icon-button" type="button" aria-label="Закрыть">×</button></div>
|
||
<label>Новый пароль<input id="resetNewPassword" type="password" minlength="12" maxlength="1024" autocomplete="new-password" required></label>
|
||
<label>Повторите пароль<input id="resetConfirmPassword" type="password" minlength="12" maxlength="1024" autocomplete="new-password" required></label>
|
||
<button class="primary" type="submit">Установить новый пароль</button>
|
||
<p id="passwordResetMessage" class="form-message" role="status"></p>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="luciStateDialog" class="luci-state-dialog">
|
||
<div class="luci-state-sheet">
|
||
<div class="luci-state-route" aria-hidden="true">
|
||
<span class="luci-cloud">☁</span>
|
||
<i></i>
|
||
<span class="luci-warning">!</span>
|
||
<i></i>
|
||
<span class="luci-router">R</span>
|
||
</div>
|
||
<span id="luciStateCode" class="luci-state-code">409 · SESSION REQUIRED</span>
|
||
<h2 id="luciStateTitle">Удалённый доступ не запущен</h2>
|
||
<p id="luciStateDescription">Создайте временную сессию, чтобы безопасно открыть LuCI этого роутера.</p>
|
||
<div id="luciStateContext" class="luci-state-context"></div>
|
||
<div class="luci-state-actions">
|
||
<button id="luciStatePrimaryBtn" class="primary" type="button">Настроить доступ</button>
|
||
<button id="luciStateDiagnosticBtn" type="button">Открыть диагностику</button>
|
||
<button id="closeLuciStateBtn" class="text-button" type="button">Вернуться к роутеру</button>
|
||
</div>
|
||
<small id="luciStateRequestId" class="luci-request-id"></small>
|
||
</div>
|
||
</dialog>
|
||
|
||
<script src="/landing-motion.js?v=1" defer></script>
|
||
<script src="/app.js?v=28" defer></script>
|
||
</body>
|
||
</html>
|