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.
4.0 KiB
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 уже есть: фронт зовёт его при загрузке, чтобы понять,
жив ли сохранённый токен.
Как добавить свой раздел
- Роут на бэкенде — в
server.tsпод/api/…. - Вкладку на фронте — папка в
src/tabs/, реестр подхватит её сам. Подробности и форматmetadata.json— в src/tabs/README.md.