From cf3437c70c359b780da560ab04055022d81ea580 Mon Sep 17 00:00:00 2001 From: shats Date: Sun, 26 Apr 2026 01:45:43 +0700 Subject: [PATCH] Update Docker documentation and README for SQLite storage and environment variables - Enhanced Docker.md with detailed instructions on building and running Docker images, including environment variable requirements and tag usage. - Updated README.md to reflect the current architecture using SQLite for data storage and clarified environment variable settings for backend and frontend operations. - Removed references to AWS S3, emphasizing local storage for MikroTik backups and SQLite database management. Made-with: Cursor --- DOCKER.md | 308 +++++++++++++++++++-------------------------- README.md | 370 +++++++++++------------------------------------------- 2 files changed, 204 insertions(+), 474 deletions(-) diff --git a/DOCKER.md b/DOCKER.md index 3916fcf..249988b 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -1,118 +1,142 @@ # Docker Images -Этот проект предоставляет Docker образы для RouterOS Manager с двумя различными версиями интерфейса. +Образ собирается из [`Dockerfile.fast`](Dockerfile.fast) (многостадийная сборка: Vite frontend + `npm ci` backend, рантайм **distroless** Node 20). В Gitea Actions используется тот же файл (см. [`.gitea/workflows/docker-publish.yml`](.gitea/workflows/docker-publish.yml)). -## 🏷️ Доступные теги +## Теги в реестре `git.shts.su` -### Основная версия (main branch) -- `git.shts.su/[repository]:latest` - Стабильная версия с базовым интерфейсом -- `git.shts.su/[repository]:[commit-sha]` - Версия с конкретным коммитом +Замените `[repository]` на путь репозитория в Gitea (например `denozord/router-lists-ui`). -### Tabler версия (tabler branch) -- `git.shts.su/[repository]:tabler` - Версия с современным Tabler UI -- `git.shts.su/[repository]:tabler-[commit-sha]` - Tabler версия с конкретным коммитом +| Ветка (триггер push) | Образы в реестре | +|----------------------|-------------------| +| **main** (и v4, v3 и т.д. из workflow) | `git.shts.su/[repository]:latest` и `git.shts.su/[repository]:<полный-sha>` | +| **v5** | **только** `git.shts.su/[repository]:v5` и `git.shts.su/[repository]:v5-<полный-sha>` — тег **`latest` с ветки v5 не публикуется** | +| **tabler** | см. отдельный workflow `docker-publish-tabler.yml`, если включён | -## 🚀 Запуск +Пин на коммит: используйте тег с суффиксом `-`; «плавающий» тег ветки (`:v5` или `:latest`) всегда указывает на последнюю успешную сборку. + +## Запуск (production) + +### Общие рекомендации + +1. **Данные на хосте** — смонтируйте том для SQLite и каталог бэкапов MikroTik, иначе при удалении контейнера пропадут БД и `.rsc`. +2. **Переменные** — минимум `ENCRYPTION_KEY`; для справочников и UI — `EVOBGP_API_URL` и `EVOBGP_API_TOKEN`; при необходимости `CORS_ORIGINS`, `PORT`. +3. **Рантайм distroless** — внутри образа **нет** `/bin/bash` и пакетного менеджера; отладка через `docker logs` / метрики. Интерактивная оболочка недоступна. + +### Основная линия (`:latest`) -### Основная версия ```bash docker run -d \ - --name s3-lists-manager \ + --name router-lists-ui \ -p 3001:3001 \ - -e ENCRYPTION_KEY=your-64-hex-character-key-here \ + -e ENCRYPTION_KEY=<64-hex> \ + -e EVOBGP_API_URL=https://evobgp.example/v1 \ + -e EVOBGP_API_TOKEN= \ + -e SQLITE_PATH=/data/router-lists.db \ + -e MIKROTIK_BACKUP_DIR=/data/backups/mikrotik \ + -v router-lists-data:/data \ git.shts.su/[repository]:latest ``` -### Tabler версия +Каталог `/data` на томе должен быть доступен пользователю **nonroot** (образ distroless); при ошибках прав создайте том заранее или используйте bind-mount с `chown` под UID образа (обычно `65532` у nonroot). + +### Ветка v5 (только теги `v5` / `v5-`) + +```bash +docker pull git.shts.su/[repository]:v5 + +docker run -d \ + --name router-lists-ui-v5 \ + -p 3002:3001 \ + -e ENCRYPTION_KEY=<64-hex> \ + -e EVOBGP_API_URL=https://evobgp.example/v1 \ + -e EVOBGP_API_TOKEN= \ + -e SQLITE_PATH=/data/router-lists.db \ + -e MIKROTIK_BACKUP_DIR=/data/backups/mikrotik \ + -v router-lists-v5-data:/data \ + git.shts.su/[repository]:v5 +``` + +Чтобы зафиксировать конкретный коммит: + +```bash +docker run -d ... git.shts.su/[repository]:v5-1c6e6ab... +``` + +### Tabler-ветка (если публикуется отдельным workflow) + ```bash docker run -d \ - --name s3-lists-manager-tabler \ - -p 3001:3001 \ - -e ENCRYPTION_KEY=your-64-hex-character-key-here \ + --name router-lists-ui-tabler \ + -p 3003:3001 \ + --env-file ./backend/.env \ + -v router-lists-tabler-data:/data \ git.shts.su/[repository]:tabler ``` -## 🔄 Обновление +## Обновление образа + +### latest -### Основная версия ```bash docker pull git.shts.su/[repository]:latest -docker stop s3-lists-manager -docker rm s3-lists-manager -docker run -d \ - --name s3-lists-manager \ - -p 3001:3001 \ - -e ENCRYPTION_KEY=your-64-hex-character-key-here \ - git.shts.su/[repository]:latest +docker stop router-lists-ui && docker rm router-lists-ui +# затем снова docker run ... как выше ``` -### Tabler версия +### v5 + ```bash -docker pull git.shts.su/[repository]:tabler -docker stop s3-lists-manager-tabler -docker rm s3-lists-manager-tabler -docker run -d \ - --name s3-lists-manager-tabler \ - -p 3001:3001 \ - -e ENCRYPTION_KEY=your-64-hex-character-key-here \ - git.shts.su/[repository]:tabler +docker pull git.shts.su/[repository]:v5 +docker stop router-lists-ui-v5 && docker rm router-lists-ui-v5 +docker run -d ... git.shts.su/[repository]:v5 ``` -## 🐳 Docker Compose +## Docker Compose -Создайте файл `docker-compose.yml`: +Пример **одной** службы на `latest` с томом данных (подставьте свой `image:` и при необходимости `env_file`): ```yaml -version: '3.8' - services: - s3-lists-manager: + router-lists-ui: image: git.shts.su/[repository]:latest - container_name: s3-lists-manager + container_name: router-lists-ui ports: - "3001:3001" restart: unless-stopped environment: - - NODE_ENV=production - - ENCRYPTION_KEY=${ENCRYPTION_KEY} # Обязательно! Используйте .env файл или secrets - # Прокси EvoBGP для UI (Communities / Domains / …) — без CORS в браузере: - # - EVOBGP_API_URL=http://evobgp:8080 - # - EVOBGP_API_TOKEN=... + NODE_ENV: production + ENCRYPTION_KEY: ${ENCRYPTION_KEY} + EVOBGP_API_URL: ${EVOBGP_API_URL} + EVOBGP_API_TOKEN: ${EVOBGP_API_TOKEN} + SQLITE_PATH: /data/router-lists.db + MIKROTIK_BACKUP_DIR: /data/backups/mikrotik + volumes: + - router-lists-data:/data - s3-lists-manager-tabler: - image: git.shts.su/[repository]:tabler - container_name: s3-lists-manager-tabler - ports: - - "3002:3001" - restart: unless-stopped - environment: - - NODE_ENV=production - - ENCRYPTION_KEY=${ENCRYPTION_KEY} # Обязательно! Используйте .env файл или secrets +volumes: + router-lists-data: ``` -**Создайте файл `.env` в той же директории:** -```bash -ENCRYPTION_KEY=your-64-hex-character-key-here -``` +Отдельный сервис для **v5** — тот же фрагмент, но `image: git.shts.su/[repository]:v5`, другой `container_name` и при желании отдельный volume. -Запуск: -```bash -docker-compose up -d -``` +## Переменные окружения (контейнер) -## 🔧 Переменные окружения +| Переменная | Описание | Обязательная | +|------------|----------|----------------| +| `ENCRYPTION_KEY` | AES-256 для IPSec и паролей MikroTik (64 hex) | **Да** | +| `EVOBGP_API_URL` | Базовый URL API EvoBGP (серверный прокси `/api/evobgp` и бэкенд-справочники) | По сценарию | +| `EVOBGP_API_TOKEN` | Bearer к EvoBGP | По сценарию | +| `SQLITE_PATH` | Путь к файлу SQLite внутри контейнера (персистентный том) | Рекомендуется явно задать, напр. `/data/router-lists.db` | +| `MIKROTIK_BACKUP_DIR` | Каталог бэкапов `.rsc` на диске | Рекомендуется с томом, напр. `/data/backups/mikrotik` | +| `PORT` | Порт HTTP внутри контейнера | Нет (по умолчанию `3001`) | +| `CORS_ORIGINS` | Список Origin через запятую | Нет | +| `LOG_LEVEL` | Уровень логов pino | Нет | -| Переменная | Описание | По умолчанию | Обязательная | -|------------|----------|--------------|--------------| -| `NODE_ENV` | Окружение Node.js | `production` | Нет | -| `PORT` | Порт для запуска сервера | `3001` | Нет | -| `ENCRYPTION_KEY` | Ключ шифрования для IPSec паролей (64 hex символа) | - | **Да** | -| `EVOBGP_API_URL` | Базовый URL API EvoBGP для серверного прокси `/api/evobgp` (например `http://evobgp:8080` или `http://10.0.0.5:8080`; при необходимости к пути добавляется `/v1`) | - | Да, если используете интеграцию EvoBGP в UI | -| `EVOBGP_API_TOKEN` | Bearer-токен к API EvoBGP (только на сервере, не в браузере) | - | Да, если используете интеграцию EvoBGP в UI | +`ENCRYPTION_KEY` должен быть **один и тот же** между перезапусками, иначе не расшифровать уже сохранённые секреты. ### EvoBGP и reverse proxy (nginx) -Запросы UI идут на тот же origin: `/api/evobgp/…` обрабатывает **Node** внутри образа. Если перед приложением стоит **nginx**, нужно проксировать префикс `/api` на Node **без потери** сегмента `/api` в URI (иначе в upstream попадёт не тот путь и будет 404). +Запросы UI идут на тот же origin: `/api/evobgp/…` обрабатывает **Node** внутри образа. Если перед приложением стоит **nginx**, нужно проксировать префикс `/api` на Node **без потери** сегмента `/api` в URI. **Рекомендуемый вариант** (путь `/api/…` сохраняется): @@ -126,140 +150,64 @@ location /api/ { } ``` -**Типичная ошибка**: внутри `location /api` указать `proxy_pass http://127.0.0.1:3001/;` — nginx **срезает** `/api`, в Node приходит `/evobgp/router-lists/catalog` вместо `/api/evobgp/router-lists/catalog`. В коде добавлен запасной маршрут на `/evobgp`, но лучше поправить nginx. +**Типичная ошибка**: `proxy_pass http://127.0.0.1:3001/;` — nginx срезает `/api`. В коде есть запасной маршрут на `/evobgp`, но лучше исправить nginx. -**Проверка**: `GET http://<хост>:<порт>/api/evobgp` (без хвоста) должен вернуть JSON вида `{"ok":true,"proxy":true,"evobgpConfigured":true}` — если вместо этого HTML «404» или страница SPA, запрос **не доходит** до Node (только статика, другой порт или неверный `proxy_pass`). +**Проверка**: `GET http://<хост>:<порт>/api/evobgp` → JSON вида `{"ok":true,"proxy":true,"evobgpConfigured":true}`. -### ENCRYPTION_KEY +## Генерация `ENCRYPTION_KEY` -**Важно**: `ENCRYPTION_KEY` является обязательным параметром для работы с IPSec паролями. Этот ключ используется для шифрования/дешифрования паролей, хранящихся в S3. - -**Генерация ключа:** ```bash node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" ``` -**Пример запуска с ENCRYPTION_KEY:** +## Мониторинг + ```bash -docker run -d \ - --name s3-lists-manager \ - -p 3001:3001 \ - -e ENCRYPTION_KEY=your-64-hex-character-key-here \ - git.shts.su/[repository]:latest +docker logs -f router-lists-ui +docker stats router-lists-ui ``` -**⚠️ Внимание**: Используйте один и тот же `ENCRYPTION_KEY` для всех запусков контейнера. При изменении ключа старые зашифрованные пароли не смогут быть расшифрованы. +## Локальная сборка (как в CI) -## 📊 Мониторинг - -### Просмотр логов ```bash -# Основная версия -docker logs s3-lists-manager - -# Tabler версия -docker logs s3-lists-manager-tabler +docker build -f Dockerfile.fast -t router-lists-ui:local . +docker run --rm -p 3001:3001 --env-file ./backend/.env -v $(pwd)/data:/data \ + -e SQLITE_PATH=/data/router-lists.db \ + -e MIKROTIK_BACKUP_DIR=/data/backups/mikrotik \ + router-lists-ui:local ``` -### Статистика контейнеров +(PowerShell на Windows: вместо `$(pwd)` используйте путь к каталогу данных.) + +## CI/CD (Gitea Actions) + +- [`.gitea/workflows/docker-publish.yml`](.gitea/workflows/docker-publish.yml) — пуш в перечисленные ветки (включая **v5**): сборка и push в `git.shts.su`. +- Для **v5** в реестр уходят **только** теги `v5` и `v5-` (без `latest`). +- Остальные ветки из списка workflow — `latest` и тег по SHA. + +Шаг **Trigger container update webhook** (если задан `secrets.portainer_webhook`) выполняется после сборки; при ошибке шаг не валит job (`continue-on-error: true`). + +## Безопасность и сеть + ```bash -docker stats s3-lists-manager s3-lists-manager-tabler +docker network create router-lists-net +docker run -d --network router-lists-net ... git.shts.su/[repository]:latest ``` -## 🔍 Отладка +Ограничение ресурсов: -### Вход в контейнер ```bash -# Основная версия -docker exec -it s3-lists-manager /bin/bash - -# Tabler версия -docker exec -it s3-lists-manager-tabler /bin/bash +docker run -d --memory=512m --cpus=0.5 ... git.shts.su/[repository]:latest ``` -### Проверка процессов -```bash -docker exec s3-lists-manager ps aux -``` +## Устранение неполадок -## 🏗️ Сборка локально - -### Основная версия -```bash -git checkout main -docker build -t s3-lists-manager:local . -``` - -### Tabler версия -```bash -git checkout tabler -docker build -t s3-lists-manager-tabler:local . -``` - -## 📝 CI/CD - -### Автоматическая сборка - -- **Ветка `main`**: Автоматически создает образ с тегом `latest` -- **Ветка `tabler`**: Автоматически создает образ с тегом `tabler` - -### Workflow файлы - -- `.gitea/workflows/docker-publish.yml` - Сборка основной версии -- `.gitea/workflows/docker-publish-tabler.yml` - Сборка Tabler версии - -## 🔒 Безопасность - -### Проверка образа -```bash -docker scan git.shts.su/[repository]:latest -docker scan git.shts.su/[repository]:tabler -``` - -### Запуск в изолированной сети -```bash -docker network create s3-lists-network -docker run -d \ - --name s3-lists-manager \ - --network s3-lists-network \ - -p 3001:3001 \ - git.shts.su/[repository]:latest -``` - -## 📈 Производительность - -### Ограничение ресурсов -```bash -docker run -d \ - --name s3-lists-manager \ - --memory=512m \ - --cpus=0.5 \ - -p 3001:3001 \ - git.shts.su/[repository]:latest -``` - -### Мониторинг ресурсов -```bash -docker stats --no-stream s3-lists-manager -``` - -## 🆘 Устранение неполадок - -### Проверка статуса контейнера -```bash -docker ps -a | grep s3-lists-manager -``` - -### Перезапуск контейнера -```bash -docker restart s3-lists-manager -``` - -### Очистка неиспользуемых образов ```bash +docker ps -a --filter name=router-lists-ui +docker restart router-lists-ui docker image prune -f ``` --- -**Примечание**: Замените `[repository]` на актуальное имя вашего репозитория в Gitea. \ No newline at end of file +**Замените `[repository]`** на актуальный путь репозитория в Gitea (например `denozord/router-lists-ui`). diff --git a/README.md b/README.md index d1d3c1c..c3da03c 100644 --- a/README.md +++ b/README.md @@ -1,347 +1,129 @@ # Router Lists UI -Полноценный UI/Backend для управления списками BGP (домены, IP-диапазоны, ASNs), фильтрами и конфигурациями MikroTik на базе S3 (Yandex Object Storage). Интерфейс построен на Tabler, frontend — Vite + React, backend — Express. +UI/Backend для управления BGP-списками, фильтрами и конфигурациями MikroTik. +Текущая модель хранения: **SQLite + локальная файловая система** (для `.rsc` backup MikroTik). + +## Актуальная архитектура хранения + +- Основные данные и объекты приложения хранятся в SQLite (через `better-sqlite3`). +- MikroTik backup-файлы (`.rsc`) хранятся в локальной ФС в каталоге `MIKROTIK_BACKUP_DIR`. +- AWS SDK/S3 в runtime не используются. +- Часть имен API/сервисов (`s3*`) сохранена для обратной совместимости, но фактический backend storage — SQLite. ## Быстрый старт ### Требования + - Node.js 18+ -- S3-совместимое хранилище (Yandex Object Storage) -- Доступы AWS: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `S3_BUCKET_NAME`, `AWS_REGION` +- npm ### Запуск backend + ```powershell cd backend -npm i +npm install # создайте .env и заполните (пример ниже) npm start ``` -Сервис поднимется на `http://localhost:3001`. + +Backend поднимется на `http://localhost:3001`. ### Запуск frontend -```bash + +```powershell cd frontend -npm i +npm install 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`. +Frontend доступен на `http://localhost:5173`. -### Важные переменные окружения -- `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `S3_BUCKET_NAME` — доступ к Object Storage -- `ENCRYPTION_KEY` — ключ шифрования (64 hex символа) для IPSec и MikroTik паролей -- `CORS_ORIGINS` — список разрешённых Origin через запятую (если пусто — разрешены все) -- `LOG_LEVEL` — уровень логов pino (`info` по умолчанию) -- `PORT` — порт backend (по умолчанию 3001) -- `BGP_BACKGROUND_URL` — адрес фонового обновления BGP, например: - - `http://77.232.38.173:8080/api/update_bgp/background?api_key=...` - - Используется эндпоинтом прокси `POST /api/update-bgp/background` для обхода CORS +## Важные переменные окружения + +- `SQLITE_PATH` — путь к файлу SQLite (по умолчанию `backend/data/router-lists.db`). +- `MIKROTIK_BACKUP_DIR` — каталог для локальных `.rsc` backup-файлов MikroTik. +- `ENCRYPTION_KEY` — 64 hex-символа для шифрования секретов (IPSec/MikroTik). +- `EVOBGP_API_URL`, `EVOBGP_API_TOKEN` — если используете интеграцию EvoBGP. +- `CORS_ORIGINS`, `LOG_LEVEL`, `PORT` — эксплуатационные настройки сервиса. +- `BGP_BACKGROUND_URL` — URL фонового обновления BGP для прокси-эндпоинта `POST /api/update-bgp/background`. Пример `.env`: + ```dotenv PORT=3001 LOG_LEVEL=info -AWS_ACCESS_KEY_ID=... -AWS_SECRET_ACCESS_KEY=... -AWS_REGION=ru-central1 -S3_BUCKET_NAME=... +SQLITE_PATH=./data/router-lists.db +MIKROTIK_BACKUP_DIR=./data/backups/mikrotik +ENCRYPTION_KEY=<64-hex> CORS_ORIGINS=http://localhost:5173 -BGP_BACKGROUND_URL=http://77.232.38.173:8080/api/update_bgp/background?api_key=denozord2502 +BGP_BACKGROUND_URL=http://77.232.38.173:8080/api/update_bgp/background?api_key=... ``` ## API (backend) -Все ответы об ошибке имеют единый формат: +Формат ошибок: + ```json { "code": "E_*", "message": "...", "details": {}, "requestId": "..." } ``` -Успешные POST/PUT возвращают: + +Формат успешных `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 }` +### Примечание по legacy-терминам -- GET `/api/ip-ranges` — список `[{ ipRange, community }]` -- POST `/api/ip-ranges` — `{ ipRanges: [{ipRange, community}], etag }` +- `GET /api/s3/last-modified` и функции вида `readS3*`/`writeS3*` — это **legacy-названия**. +- Фактически эти операции работают с SQLite-backed хранилищем. -- 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) +- Данные: `/api/domains-new`, `/api/ip-ranges`, `/api/asns`, `/api/communities` +- Фильтры/конфиги: `/api/filters`, `/api/server-configs`, `/api/server-filters` +- MikroTik: `/api/mikrotik/generate`, `/api/mikrotik/generate-interfaces`, `/api/mikrotik/generate-recursive-routes`, `/api/mikrotik/test-connection`, `/api/mikrotik/apply` +- Прочее: `/api/servers`, `/api/billing`, `/api/auto-urls`, `/api/servers/availability`, `/api/locks/:resource`, `/api/history/:resource` -### Фильтры и конфигурации -- GET `/api/filters` / POST `/api/filters` — фильтры для всех серверов. -- GET `/api/filters/generate-config?format=text|json` — сгенерировать конфиг MikroTik из `filters.json`. -- POST `/api/filters/export-config` — сохранить сгенерированный конфиг в S3 (`mikrotik-frouting-config.txt`). +## Docker -- 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` — удалить конфиг и фильтры. +Полная инструкция: [DOCKER.md](DOCKER.md). -- GET `/api/server-filters/:serverId` — фильтры сервера. -- POST `/api/server-filters/:serverId` — сохранить фильтры сервера. -- POST `/api/server-filters/generate-config` — сгенерировать конфиг MikroTik на лету из переданных `{ filters, format?: 'text'|'json' }`. -- POST `/api/mikrotik/generate` — сгенерировать конфиг интерфейсов и маршрутов. Body: `{ format?: 'text'|'json', type?: 'interfaces'|'recursive'|'all', serverId?: string, config?, servers? }`. Возвращает `{ blocks }` — массив блоков с полем `code` (text) или `operations` (json). -- GET `/api/mikrotik/generate-interfaces?format=text|json&serverId=` — только интерфейсы. -- GET `/api/mikrotik/generate-recursive-routes?format=text|json&serverId=` — только рекурсивные маршруты. -- POST `/api/mikrotik/test-connection` — проверить соединение с MikroTik. Body: `{ serverId }` или `{ host, port?, user?, password }`. -- POST `/api/mikrotik/apply` — применить конфигурацию на MikroTik по API. Body: `{ serverId, type?: 'interfaces'|'recursive'|'all', dryRun?: boolean }`. Только для jumphost. +Критично для production: -### Прочее -- 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) по ключевым файлам. -- POST `/api/update-bgp/background` — прокси к фоновой задаче обновления BGP. Требует `BGP_BACKGROUND_URL` в `.env`. -- Locks: GET `/api/locks/:resource`, POST `/api/locks/:resource`, DELETE `/api/locks/:resource` (опциональные soft-locks для редактирования; доступ к S3 и долгие операции вроде speed-test блокировок не используют). -- History: GET `/api/history/:resource`, POST `/api/history/:resource/rollback`. +- Обязательно смонтировать volume для пути с `SQLITE_PATH`. +- Обязательно смонтировать volume для `MIKROTIK_BACKUP_DIR`, иначе `.rsc` backup-файлы потеряются при пересоздании контейнера. +- Использовать стабильный `ENCRYPTION_KEY` между перезапусками. -## 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 - -# 📂 RouterOS Manager - - -Веб-интерфейс для удобного управления файлами в S3-совместимом хранилище Yandex Cloud. Приложение позволяет в реальном времени просматривать, добавлять, редактировать, удалять и массово изменять записи в следующих файлах: - -- **domains.txt** - список доменов и их шлюзов -- **asns.txt** - список AS номеров и их шлюзов -- **servers.json** - список серверов с расширенной информацией (IP, DNS, страна, провайдер, тип туннеля) - -## ✨ Возможности - -- **Три режима работы:** Управление списками доменов, AS-номеров и серверов через вкладки. -- **CRUD операции:** Полный набор действий: создание, чтение, обновление и удаление записей. -- **Поиск в реальном времени:** Мгновенная фильтрация списков по мере ввода. -- **Массовая замена:** Быстрое обновление шлюзов для сотен записей в один клик. -- **Сохранение в S3:** Все изменения сохраняются непосредственно в файлах в бакете Yandex Cloud. -- **Docker-контейнеризация:** Готовый `Dockerfile` для сборки и запуска приложения в изолированном окружении. -- **CI/CD с Gitea Actions:** Автоматическая сборка и публикация Docker-образа в Gitea Registry при пуше в `main`. -- **Современный интерфейс:** Полностью интегрированный Tabler UI с официальными компонентами. - -## 🛠️ Технологический стек - -| Область | Технология | -|--------------|-----------------------------------------------------------------------------------------------------------| -| **Фронтенд** | [**React**](https://reactjs.org/) + [**Vite**](https://vitejs.dev/) | -| | [**@tabler/core**](https://tabler.io/) (UI-компоненты для Tabler версии) | -| | [**Tabler Icons**](https://tabler-icons.io/) (иконки) | -| | [**Axios**](https://axios-http.com/) (HTTP-клиент) | -| **Бэкенд** | [**Node.js**](https://nodejs.org/) + [**Express**](https://expressjs.com/) | -| | [**AWS SDK for JS**](https://aws.amazon.com/sdk-for-javascript/) (для работы с Yandex Cloud S3) | -| **CI/CD** | [**Docker**](https://www.docker.com/), [**Gitea Actions**](https://gitea.com/blog/2022/10/01/gitea-actions/) | - -## 🏗️ Архитектура - -Приложение состоит из двух основных частей: фронтенд на React и бэкенд на Node.js/Express, которые взаимодействуют через REST API. - -```mermaid -graph TD - subgraph Browser - A[React Frontend] - end - - subgraph Server - B(Node.js/Express API) - end - - subgraph Yandex Cloud - C{S3 Bucket} - D1[domains.txt] - D2[asns.txt] - D3[servers.json] - end - - A -- HTTP Requests --> B - B -- AWS SDK --> C - C --- D1 - C --- D2 -``` - -## 🚀 Установка и запуск - -### Предварительные требования - -- [Node.js](https://nodejs.org/) (v20.x или выше) -- [npm](https://www.npmjs.com/) или [yarn](https://yarnpkg.com/) -- Доступ к бакету Yandex Cloud S3 и сервисный аккаунт с правами на чтение и запись. - -### 1. Настройка бэкенда - -1. Перейдите в директорию `backend`: - ```bash - cd backend - ``` -2. Создайте файл `.env` на основе примера `.env.example`. Заполните его вашими учетными данными от Yandex Cloud S3: - ```env - # .env - S3_ACCESS_KEY_ID=ВАШ_КЛЮЧ_ДОСТУПА - S3_SECRET_ACCESS_KEY=ВАШ_СЕКРЕТНЫЙ_КЛЮЧ - S3_BUCKET_NAME=ИМЯ_ВАШЕГО_БАКЕТА - ``` -3. Установите зависимости: - ```bash - npm install - ``` - -### 2. Настройка фронтенда - -1. Перейдите в директорию `frontend`: - ```bash - cd ../frontend - ``` -2. Установите зависимости: - ```bash - npm install - ``` - -### 3. Запуск приложения - -1. **Запустите бэкенд-сервер.** В директории `backend` выполните: - ```bash - npm start - ``` - Сервер запустится на `http://localhost:3001`. - -2. **Запустите фронтенд.** В новой вкладке терминала, в директории `frontend`, выполните: - ```bash - npm run dev - ``` - Приложение будет доступно по адресу `http://localhost:5173` и будет автоматически проксировать API-запросы на бэкенд. - -## 🐳 Docker - -Приложение полностью готово к запуску в Docker с двумя вариантами интерфейса. - -### Доступные образы - -#### Основная версия (main branch) -```bash -git.shts.su/[repository]:latest -``` - -#### Tabler версия (tabler branch) -```bash -git.shts.su/[repository]:tabler -``` - -### Быстрый запуск - -#### Основная версия -```bash -docker run -d \ - --name s3-lists-manager \ - -p 3001:3001 \ - --env-file ./backend/.env \ +```powershell +docker run -d ` + --name router-lists-ui ` + -p 3001:3001 ` + --env-file ./backend/.env ` + -e SQLITE_PATH=/data/router-lists.db ` + -e MIKROTIK_BACKUP_DIR=/data/backups/mikrotik ` + -v router-lists-data:/data ` git.shts.su/[repository]:latest ``` -#### Tabler версия -```bash -docker run -d \ - --name s3-lists-manager-tabler \ - -p 3002:3001 \ - --env-file ./backend/.env \ - git.shts.su/[repository]:tabler +## Чек-лист проверки соответствия (S3 -> SQLite) + +1. В `backend/package.json` нет зависимостей AWS SDK. +2. Приложение стартует с `SQLITE_PATH` и создает/использует файл БД. +3. CRUD по основным данным (`/api/domains-new`, `/api/ip-ranges`, `/api/asns`) работает после перезапуска контейнера с тем же volume. +4. Созданный MikroTik backup появляется как локальный `.rsc` файл в каталоге `MIKROTIK_BACKUP_DIR`. +5. После перезапуска контейнера с тем же volume backup-файл остается доступным. +6. `GET /api/s3/last-modified` возвращает метаданные, но интерпретируется как legacy endpoint поверх SQLite. + +## Структура репозитория + +```text +backend/ Express API +frontend/ Vite + React UI ``` -### Локальная сборка +## Лицензия -Для сборки образа выполните команду в корневой директории проекта: -```bash -docker build -t s3-lists-manager . -``` - -### Запуск контейнера - -Для запуска контейнера необходимо передать переменные окружения. Это можно сделать с помощью флага `-e` или через `--env-file`. - -```bash -docker run --rm -p 3001:3001 --env-file ./backend/.env s3-lists-manager -``` - -После этого приложение будет доступно по адресу `http://localhost:3001`. - -### Docker Compose - -Создайте файл `docker-compose.yml`: - -```yaml -version: '3.8' - -services: - s3-lists-manager: - image: git.shts.su/[repository]:latest - container_name: s3-lists-manager - ports: - - "3001:3001" - restart: unless-stopped - env_file: - - ./backend/.env - - s3-lists-manager-tabler: - image: git.shts.su/[repository]:tabler - container_name: s3-lists-manager-tabler - ports: - - "3002:3001" - restart: unless-stopped - env_file: - - ./backend/.env -``` - -Запуск: -```bash -docker-compose up -d -``` - -Подробная документация по Docker образам доступна в файле [DOCKER.md](DOCKER.md). - -## ⚙️ API Endpoints - -| Метод | Путь | Описание | -|--------|---------------|----------------------------------| -| `GET` | `/api/domains`| Получить список всех доменов. | -| `POST` | `/api/domains`| Сохранить изменения в `domains.txt`. | -| `GET` | `/api/asns` | Получить список всех AS. | -| `POST` | `/api/asns` | Сохранить изменения в `asns.txt`. | -| `GET` | `/api/servers`| Получить список всех серверов. | -| `POST` | `/api/servers`| Сохранить изменения в `servers.json`. | \ No newline at end of file +MIT