Назад в архив
III. Платформа, DevOps и безопасностьвитринасолоклиент анонимизирован

Admin v2 — Edge-RBAC + STRIDE Hardening

Production-grade мультитенантная админ-панель (Restaurant chain (UZ) + Restaurant chain (UZ)) на Next.js 16 / React 19 поверх legacy-бэкенда на Laravel, с сильным акцентом на безопасность: edge-RBAC, TOTP MFA, audit log, строгий CSP.

Статус
активный
Период
2026-05-20 → 2026-05-21
AI-сессий
Стек
Языки
TypeScript
Фреймворки · Инфра
Next.js 16 (App RouterTurbopack)React 19better-authdrizzle-ormTailwind CSS v4shadcn/uiTanStack Query/Tablenext-safe-actionzod v4
§01

Обзор

  • Что это: усиленная по безопасности админ-панель для двух брендов из единой кодовой базы — брендинг и базовый API выбираются в рантайме через env TENANT. Управляет каталогом (продукты, модификаторы, категории, меню), правилами продаж и скидок, локациями (города/организации/терминалы), маркетингом (Telegram-рассылки, шаблоны, SMS, новости, слайдеры) и системными настройками (роли, языки, статусы заказов, push-события). Фронтенд — это обёртка над существующим Laravel API (авторизация по OTP на номер телефона, /api/v2/*).
  • Тип / статус / роль: web-app · active (свежий, активная разработка) · solo (Davron Yuldashev).
  • Окно активности: 2026-05-20 → 2026-05-21. Очень молодой: bootstrap-скаффолд + один большой feat-коммит (237 файлов, +27 468 строк) в первый день, затем security-фикс на следующий.
§02

Стек

  • Языки: TypeScript (strict + noUncheckedIndexedAccess).
  • Фреймворки/библиотеки: Next.js 16 (App Router, Turbopack), React 19.2, better-auth 1.6 (адаптер Drizzle, хранилище сессий на SQLite), drizzle-orm + better-sqlite3, TanStack Query v5 + TanStack Table v8, next-safe-action v8 (типобезопасные server actions), react-hook-form + резолверы zod v4, nuqs (состояние в URL), shadcn/ui (new-york) + Tailwind v4 (CSS-first, без tailwind.config.js), lucide-react, sonner, @t3-oss/env-nextjs (валидация env), hashids, input-otp, react-google-recaptcha-v3.
  • Инфра/деплой: Bun (lockfile bun.lock, скрипты bun dev/build), ESLint flat config + eslint-plugin-security. CI/хостинг в репозитории не зафиксированы. @upstash/ratelimit + @upstash/redis подключены, но помечены как заглушка (rate limiting ещё не активен).
  • Данные: SQLite (local.db) — только для сессий/MFA/audit better-auth; бизнес-данные живут в Laravel. Миграции через Drizzle-kit.
  • Заметный тулинг: bun smoke — кастомный smoke-тест Laravel-клиента через --conditions react-server. «Забаненные зависимости» из README: react-quill (XSS), next-auth (заменён на better-auth), axios (только нативный fetch).
§03

Что реализовано

Таймлайн (git log, 3 коммита):

  • Initial bootstrap — скаффолд Next 16 + shadcn + better-auth (61 файл, +5154). Автор bootstrap@admin-v2-build.local (автоматический инструмент скаффолдинга).
  • `dfc8884` feat: auth + 22 dashboard pages + broadcasts parity — (2026-05-20, Davron Yuldashev). Основной массив: 237 файлов, +27 468 / −181. Именно здесь построено всё ядро: OTP-логин через Laravel, TOTP MFA, типобезопасный Laravel-клиент с zod-схемами по ~30 ресурсам, 22+ CRUD-страницы дашборда и фича broadcasts (Telegram-рассылки) с полным жизненным циклом.
  • `28d31e3` fix(proxy): close cookie-presence auth bypass — (2026-05-21, Davron Yuldashev). Security-фикс по итогам самоаудита (finding C1): подделанная сессионная кука с мусорным payload возвращала 200 на защищённых маршрутах, потому что middleware «пропускал» запросы при getCookieCache → null. Исправлено: кука присутствует + парсинг провалился ⇒ редирект на /login + удаление обеих кук.
  • Объём: 3 коммита за 2 дня; рывок «от нуля до продукта» за один заход, затем точечная работа по безопасности. Заброшенным не выглядит — свежий, активный.
§04

Технические челленджи

Всё подтверждено по диффам/файлам:

  • Edge-RBAC без обращения к БД в middleware (proxy.ts): роли проверяются прямо в Edge-рантайме чтением подписанного кэша куки better-auth (getCookieCache, HMAC над BETTER_AUTH_SECRET) — без обращения к SQLite. Реализовано: генерация CSP-nonce на каждый запрос, абсолютный лимит сессии в 12 часов (session.createdAt + 12h), admin-only префиксы (/users, /configs, /admin), нормализация roles как из массива, так и из JSON-строки. → Демонстрирует понимание ограничений Edge-рантайма и инженерии авторизации.
  • Закрытие auth bypass (коммит 28d31e3): классическая ошибка «кука есть ⇒ пропускаем», найденная самостоятельным STRIDE-аудитом и исправленная; проверки RBAC и лимита сессии вынесены из ветки if (cache) после раннего return, поскольку cache теперь гарантированно non-null. → Настоящий security-майндсет, а не галочка.
  • TOTP MFA с шифрованием секрета at-rest (lib/auth/mfa.ts): генерирует 160-битный base32-секрет (RFC 6238), шифрует его через AES-GCM с помощью symmetricEncrypt (ключ MFA_ENCRYPTION_KEY ⇒ fallback BETTER_AUTH_SECRET), выдаёт otpauth:// URI, верифицирует с окном ±1 (терпимо к сдвигу ±30 с), строго нормализует код. → Знание криптографии и стандартов TOTP.
  • Brute-force lockout на уровне БД (lib/auth/lockout.ts): 5 неудачных попыток TOTP за 15 минут ⇒ блокировка на 15 минут; состояние хранится в строке user (mfa_failed_attempts/mfa_locked_until), а не в памяти — переживает рестарты и консистентно между воркерами. Атомарный инкремент через sql\${col} + 1\` с .returning()`. Публичные ошибки намеренно обобщённые. → Rate limiting production-уровня.
  • Типобезопасный Laravel-клиент (lib/api/laravel.ts, 405 строк): единая точка входа laravelFetch — централизует структурированное JSON-логирование (никогда не логирует токен/тело), проброс bearer, тенантный заголовок X-Project-Code, авто-Idempotency-Key (UUID) на всех мутациях, объединение AbortSignal + таймаут 15 с, маппинг статуса в типизированную ошибку (LaravelUnauthorized/Forbidden/Validation/...) и опциональную zod-валидацию ответа. Ленивый динамический импорт стека сессий, чтобы вызовы skipAuth не подгружали БД. → Отличная архитектура интеграционного слоя.
  • Особенности интеграции с legacy-Laravel (lib/auth/laravel.ts, lib/api/decode.ts): странный OTP-контракт воспроизведён «как есть» (/api/keldi → base64 CSRF, /api/send_otp с заголовком wtf = recaptcha, /api/auth_otp → base64 JSON или буквальный "false"). Хелперы декодируют дважды закодированные base64-конверты. → Умение приручить чужой нестандартный API, не меняя его.
  • Фабрика server actions (lib/forms/serverAction.ts): defineCrudAction({input, method, path, output, revalidate}) кодифицирует повторяющийся CRUD-паттерн поверх next-safe-action; ошибки Laravel 422 транслируются обратно в ошибки полей формы через returnValidationErrors; handleServerError сворачивает любые не-Laravel ошибки в обобщённую строку (без утечки стектрейсов). path может быть функцией от провалидированного входа. → DRY + типобезопасность как системное решение.
  • Обход для multipart (broadcasts/actions.ts): поскольку laravelFetch работает только с JSON, загрузка изображений в рассылках идёт прямым fetch с FormData, намеренно без установки Content-Type (чтобы fetch сам выставил multipart-boundary), при этом переиспользуя те же заголовки auth/tenant. Валидация типа/размера на клиенте (≤10 МБ). Жизненный цикл рассылки (schedule/pause/resume/cancel/cancel-and-delete/reset-to-draft/duplicate) + живая статистика из Redis через polling.
§05

AI-разработка

  • Найдено сессий: 0 — директории ~/.claude/projects/*admin_v2* нет.
  • Что делалось с AI: оба пользовательских коммита несут Co-Authored-By: Claude Opus 4.7, то есть AI использовался при разработке, но локальные логи сессий Claude Code для этого репозитория не сохранились (возможно, запуск шёл из другого пути/инструмента). Сам bootstrap-коммит — это автоматический агент скаффолдинга (bootstrap@admin-v2-build.local), на что также намекает AGENTS.md с правилами для агентов под Next 16.
  • Паттерны AI-воркфлоу: агентная настройка видна косвенно (AGENTS.md, упоминание «auth-builder finalizes schema» в README, отсылки к «security audit 2026-05-21» с кодами findings) — структурированная работа с AI-агентами и самостоятельным code review/аудитом. Прямых следов нет.
§06

Достижения и метрики

  • Масштаб: 22+ страницы дашборда (broadcasts, broadcast-templates, catalog, categories, cities, configs, delivery-pricings, events-system, langs, menu-items, menu-types, mobile-push-events, modifiers, news, order-statuses, organizations, products, roles, sales, sales-rules, sliders, sms-templates, terminals).
  • ~30 zod-схем ресурсов в lib/api/schemas/** (catalog/sales/locations/people/marketing/settings).
  • +27 468 строк в главном feat-коммите за один день.
  • Безопасность: строгий CSP с инфраструктурой nonce на каждый запрос, полный набор security-заголовков (HSTS preload, COOP/CORP, X-Frame DENY, Permissions-Policy), STRIDE threat model в ARCHITECTURE.md (§6), append-only audit log, ключи идемпотентности на мутациях.
  • Мультитенантность: единая кодовая база → 2 бренда (Restaurant chain (UZ)/Restaurant chain (UZ)) через env TENANT.
§07

Контрибьюторы

git shortlog · все ветки

  1. Dave932
  2. bootstrap1
2 контрибьютора3 коммитов всего
Сейчас

Открыт к ролям Senior / Staff инженера и избранному фрилансу — продакшн-AI, платформы и full-stack разработка.

Связаться