# 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 название и версия панели ``` ## Запуск в деве Два терминала: ```bash 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` на бэкенд, поэтому фронт всегда ходит по относительным путям и порт нигде не зашит. ## Прод ```bash 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](frontend/src/tabs/README.md).