feat: Add comprehensive documentation for Router Lists UI, including setup instructions, API endpoints, and frontend/backend structure; enhance navbar styles for better responsiveness
Publish Fast Tabler Docker image / build-and-push-fast (push) Has been cancelled
Publish Fast Tabler Docker image / build-and-push-fast (push) Has been cancelled
This commit is contained in:
@@ -1,3 +1,115 @@
|
|||||||
|
# Router Lists UI
|
||||||
|
|
||||||
|
Полноценный UI/Backend для управления списками BGP (домены, IP-диапазоны, ASNs), фильтрами и конфигурациями MikroTik на базе S3 (Yandex Object Storage). Интерфейс построен на Tabler, frontend — Vite + React, backend — Express.
|
||||||
|
|
||||||
|
## Быстрый старт
|
||||||
|
|
||||||
|
### Требования
|
||||||
|
- Node.js 18+
|
||||||
|
- S3-совместимое хранилище (Yandex Object Storage)
|
||||||
|
- Доступы AWS: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `S3_BUCKET_NAME`, `AWS_REGION`
|
||||||
|
|
||||||
|
### Запуск backend
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
npm i
|
||||||
|
cp .env.example .env # заполните переменные
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
Сервис поднимется на `http://localhost:3001`.
|
||||||
|
|
||||||
|
### Запуск frontend
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm i
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
Frontend доступен на `http://localhost:5173` (по умолчанию). Production-сборка: `npm run build`.
|
||||||
|
|
||||||
|
## Главные особенности
|
||||||
|
- Единая UX/UI библиотека Tabler, адаптивные панели действий и навигация.
|
||||||
|
- Онлайн-обновление (WebSocket) BGP c потоковым логом и фоновое обновление (HTTP POST) из UI.
|
||||||
|
- Блокировки (soft-lock) ресурсов с TTL, чтобы избежать гонок при одновременном редактировании.
|
||||||
|
- Версионирование данных (история/откат), если включено версии в бакете.
|
||||||
|
- Генерация конфигурации MikroTik из фильтров (`/api/filters/generate-config`) и экспорт в S3.
|
||||||
|
- Метрики Prometheus: `/metrics`, health/ready: `/health`, `/ready`.
|
||||||
|
|
||||||
|
## API (backend)
|
||||||
|
|
||||||
|
Все ответы об ошибке имеют единый формат:
|
||||||
|
```json
|
||||||
|
{ "code": "E_*", "message": "...", "details": {}, "requestId": "..." }
|
||||||
|
```
|
||||||
|
Успешные POST/PUT возвращают:
|
||||||
|
```json
|
||||||
|
{ "ok": true, "etag": "...", "lastModified": "ISO", "contentLength": 123 }
|
||||||
|
```
|
||||||
|
И всегда выставляют заголовки `ETag`, `Last-Modified`, `Content-Length-Source` (если есть). GET поддерживают `countOnly=true` там, где это логично.
|
||||||
|
|
||||||
|
### Данные
|
||||||
|
- GET `/api/domains-new` — список доменов `{ domain, community }`
|
||||||
|
- `?q=`, `?offset=`, `?limit=`, `?countOnly=true`, `?format=std`
|
||||||
|
- POST `/api/domains-new` — `{ domains: [{domain, community}], etag }`
|
||||||
|
|
||||||
|
- GET `/api/ip-ranges` — список `[{ ipRange, community }]`
|
||||||
|
- POST `/api/ip-ranges` — `{ ipRanges: [{ipRange, community}], etag }`
|
||||||
|
|
||||||
|
- GET `/api/asns` — список `[{ domain, type }]` (domain = AS, type = community)
|
||||||
|
- POST `/api/asns` — `{ domains: [{domain, type}], etag }`
|
||||||
|
|
||||||
|
- GET `/api/communities` — справочник community (JSON)
|
||||||
|
- POST `/api/communities` — `{ communities: [...] }` (валидация уникальности value)
|
||||||
|
|
||||||
|
### Фильтры и конфигурации
|
||||||
|
- GET `/api/filters` / POST `/api/filters` — фильтры для всех серверов.
|
||||||
|
- GET `/api/filters/generate-config` — сгенерировать конфиг MikroTik из `filters.json`.
|
||||||
|
- POST `/api/filters/export-config` — сохранить сгенерированный конфиг в S3 (`mikrotik-frouting-config.txt`).
|
||||||
|
|
||||||
|
- GET `/api/server-configs` / POST `/api/server-configs` — список серверов (id, name, ...).
|
||||||
|
- GET `/api/server-configs/:serverId` / POST `/api/server-configs/:serverId` — конфиг конкретного сервера.
|
||||||
|
- DELETE `/api/server-configs/:serverId` — удалить конфиг.
|
||||||
|
- DELETE `/api/server-configs/:serverId/complete` — удалить конфиг и фильтры.
|
||||||
|
|
||||||
|
- GET `/api/server-filters/:serverId` — фильтры сервера.
|
||||||
|
- POST `/api/server-filters/:serverId` — сохранить фильтры сервера.
|
||||||
|
- POST `/api/server-filters/generate-config` — сгенерировать конфиг MikroTik на лету из переданных `{ filters }`.
|
||||||
|
|
||||||
|
### Прочее
|
||||||
|
- GET `/api/servers` / POST `/api/servers` — список серверов.
|
||||||
|
- GET `/api/billing` / POST `/api/billing` — биллинг (ноды, статусы и т.п.).
|
||||||
|
- GET `/api/auto-urls` / POST `/api/auto-urls` — список авто-URL.
|
||||||
|
- POST `/api/auto-urls/process` — обработать авто-URL и добавить IP в `bgp_data/ips.txt`.
|
||||||
|
- GET `/api/servers/availability?ttlSeconds=60` — быстрый TCP‑чек доступности нод.
|
||||||
|
- GET `/api/s3/last-modified` — метаданные S3 (etag/lastModified/contentLength) по ключевым файлам.
|
||||||
|
- Locks: GET `/api/locks/:resource`, POST `/api/locks/:resource`, DELETE `/api/locks/:resource`.
|
||||||
|
- History: GET `/api/history/:resource`, POST `/api/history/:resource/rollback`.
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
- Vite + React, Tabler CSS/JS (`@tabler/core`).
|
||||||
|
- Общий компонент `PageHeaderActions` — единый toolbar на страницах данных.
|
||||||
|
- Модалка `WsUpdateModal` — поток логов online-обновления (ws).
|
||||||
|
- Доступность: роли, подписи, фокус-кольца, hot-path без мыши.
|
||||||
|
|
||||||
|
### Скрипты
|
||||||
|
```bash
|
||||||
|
npm run dev # dev-сервер
|
||||||
|
npm run build # продакшн сборка
|
||||||
|
```
|
||||||
|
|
||||||
|
## Структура репозитория
|
||||||
|
```
|
||||||
|
backend/ # Express API
|
||||||
|
frontend/ # Vite React UI
|
||||||
|
```
|
||||||
|
|
||||||
|
## Безопасность и эксплуатация
|
||||||
|
- Helmet, RateLimit, CORS, отключён слабый etag на JSON.
|
||||||
|
- Prometheus метрики по умолчанию.
|
||||||
|
- Для истории версий включите versioning в бакете S3.
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
MIT
|
||||||
|
|
||||||
# 📂 S3 Lists Manager
|
# 📂 S3 Lists Manager
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -167,12 +167,13 @@
|
|||||||
.navbar-nav .dropdown-menu {
|
.navbar-nav .dropdown-menu {
|
||||||
position: static;
|
position: static;
|
||||||
float: none;
|
float: none;
|
||||||
width: auto;
|
width: 100%;
|
||||||
margin-top: 0;
|
margin-top: 0.25rem;
|
||||||
background-color: transparent;
|
background-color: var(--tblr-bg-surface);
|
||||||
border: 0;
|
border: 1px solid var(--tblr-border-color);
|
||||||
box-shadow: none;
|
box-shadow: 0 0.25rem 0.5rem rgba(0,0,0,0.08);
|
||||||
}
|
}
|
||||||
|
.navbar-nav .dropdown-item { padding: 0.5rem 0.75rem; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom animations */
|
/* Custom animations */
|
||||||
|
|||||||
+15
-1
@@ -75,6 +75,7 @@ import {
|
|||||||
useLocation,
|
useLocation,
|
||||||
Navigate
|
Navigate
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
|
import { IconMenu2 } from '@tabler/icons-react';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -208,6 +209,19 @@ function MainLayout() {
|
|||||||
<span className="fw-bold">S3 Lists Manager</span>
|
<span className="fw-bold">S3 Lists Manager</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопка бургера для мобильных */}
|
||||||
|
<button
|
||||||
|
className="navbar-toggler d-md-none"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbar-menu"
|
||||||
|
aria-controls="navbar-menu"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<IconMenu2 />
|
||||||
|
</button>
|
||||||
|
|
||||||
{/* Основное меню */}
|
{/* Основное меню */}
|
||||||
<div className="collapse navbar-collapse" id="navbar-menu">
|
<div className="collapse navbar-collapse" id="navbar-menu">
|
||||||
@@ -264,7 +278,7 @@ function MainLayout() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Правая часть навбара: язык и тема */}
|
{/* Правая часть навбара: язык и тема */}
|
||||||
<div className="navbar-nav flex-row order-md-last">
|
<div className="navbar-nav flex-row order-md-last ms-2">
|
||||||
<div className="nav-item me-2">
|
<div className="nav-item me-2">
|
||||||
<select
|
<select
|
||||||
className="form-select form-select-sm"
|
className="form-select form-select-sm"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function PageHeaderActions({
|
|||||||
onBackgroundUpdate,
|
onBackgroundUpdate,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="header-actions">
|
<div className="header-actions" role="toolbar" aria-label="Действия страницы">
|
||||||
<div className="btn-group">
|
<div className="btn-group">
|
||||||
{onOnlineUpdate && (
|
{onOnlineUpdate && (
|
||||||
<button className="btn btn-outline-warning" type="button" onClick={onOnlineUpdate} title="Online-обновление BGP (WebSocket)">
|
<button className="btn btn-outline-warning" type="button" onClick={onOnlineUpdate} title="Online-обновление BGP (WebSocket)">
|
||||||
|
|||||||
Reference in New Issue
Block a user