Files
maeneko 66ae59e1d0 Add the admin panel scaffold
A self-contained template: an Express backend with login, JWT, rate limiting
and SPA serving, and a React 18 + Vite shell with the same tab registry and
warm Material 3 design system the panel uses. No domain logic — routes and tabs
are meant to be hung off it.

node_modules and the frontend build stay out through the existing ignores.
2026-09-10 00:24:18 +03:00
..
2026-09-10 00:24:18 +03:00
2026-09-10 00:24:18 +03:00
2026-09-10 00:24:18 +03:00

Admin — каркас админ-панели

Самодостаточный шаблон админки: бэкенд с авторизацией и фронтенд-оболочка с системой вкладок. Предметной логики нет — это основа, поверх которой навешиваются свои роуты и вкладки.

admin/
  backend/     Express: /login, /logout, JWT, rate-limit, отдача SPA
    server.ts
  frontend/    React 18 + Vite: логин, тема, drawer/сайдбар, реестр вкладок
    src/App.tsx          оболочка
    src/App.css          дизайн-система (Material 3, тёплая палитра)
    src/tabs/            вкладки — см. src/tabs/README.md
    src/components/      общие компоненты и иконки (+ карта ICONS)
    src/lib/shared.ts    общие типы/утилиты + PageProps
    src/lib/branding.ts  название и версия панели

Запуск в деве

Два терминала:

cd admin/backend  && npm install && ADMIN_PASS=admin npm start   # :8080
cd admin/frontend && npm install && npm run dev                  # :5173

Открыть http://localhost:5173 — логин admin / admin. Vite проксирует /api, /login, /logout на бэкенд, поэтому фронт всегда ходит по относительным путям и порт нигде не зашит.

Прод

cd admin/frontend && npm run build     # → frontend/dist
cd admin/backend  && npm start         # раздаёт dist и API с одного порта

Переменные окружения (backend)

Переменная По умолчанию Смысл
PORT 8080 Порт бэкенда
ADMIN_USER admin Логин в панель
ADMIN_PASS — (пусто ⇒ вход отключён) Пароль; без него /login всегда отвечает 401
JWT_SECRET случайный при старте Подпись токенов; без него сессии рвутся при рестарте процесса
STATIC_DIR ../frontend/dist Каталог со сборкой фронта

Авторизация

POST /login (логин + пароль) → HS256 JWT на 24 часа, подписанный JWT_SECRET через встроенный crypto — без сторонних JWT-библиотек. Токен хранится в localStorage и уходит в заголовке Authorization: Bearer. POST /logout добавляет подпись токена в in-memory список отозванных — токен умирает сразу. Перебор пароля ограничен: 5 неудачных попыток с одного IP за 15 минут → 429.

Всё под /api закрыто мидлварой requireAuth — свои роуты добавляй ниже строки app.use("/api", requireAuth) в server.ts, и они получают авторизацию даром. GET /api/session уже есть: фронт зовёт его при загрузке, чтобы понять, жив ли сохранённый токен.

Как добавить свой раздел

  1. Роут на бэкенде — в server.ts под /api/….
  2. Вкладку на фронте — папка в src/tabs/, реестр подхватит её сам. Подробности и формат metadata.json — в src/tabs/README.md.