Beeline

Монорепозиторий проекта: бекенд-API и фронтенд-приложения в одном репозитории под управлением pnpm workspaces.

Для жюри / быстрый запуск в Docker: docs/jury.md.

Для AI-агентов: канонический подробный документ по проекту — CLAUDE.md (стек, слои, команды, конвенции, «гочи»). Агентские скиллы/команды/субагенты — в .claude/ (см. .claude/SUMMARY.md).

Стек

  • Пакетный менеджер: pnpm (workspaces), Node >= 24
  • Бекенд (apps/api): Hono + @hono/zod-openapi, аутентификация — better-auth, логирование — pino, Scalar-доки
  • БД (packages/db): PostgreSQL + Drizzle ORM (+ drizzle-kit для миграций)
  • Фронтенд (apps/admin): Vite + React 19 + TypeScript, TailwindCSS v4, Zustand (клиентское состояние), TanStack Query (серверное), react-router-dom v7, клиент better-auth. Архитектура — Feature-Sliced Design
  • Дизайн-система (packages/ui): React-компоненты на семантических токенах; токены/тема — packages/shared/theme.css (Tailwind v4, тёмная тема по классу .dark, шрифт Onest)
  • Общий код (packages/shared): типизированный Hono-клиент (hc), конфиг better-auth, типы ресурсов, theme.css

Структура

.
├── apps/
│   ├── api/      # Бекенд на Hono (реализован частично: cities + auth)
│   └── admin/    # Админка (Vite + React SPA), структура по FSD:
│                 #   app / pages / features / entities / shared
├── packages/
│   ├── db/       # Слой БД: схемы Drizzle, миграции, подключение к Postgres
│   ├── shared/   # @beeline/shared — hc-клиент, конфиг auth, типы, theme.css
│   ├── ui/       # @beeline/ui — React-компоненты дизайн-системы
│   └── onboarding/ # @beeline/onboarding — движок туров driver.js (router-агностичный)
├── docker-compose.yml   # Локальный PostgreSQL
├── .env.example         # Шаблон общего окружения для api + db
├── CLAUDE.md            # Подробный док проекта для AI-агентов
└── pnpm-workspace.yaml

Фронтенд задуман как два независимых приложения: apps/admin (админ-панель) и будущее клиентское. Сейчас реализуется только admin; packages/ui и packages/shared — переиспользуемый кросс-аппный слой.

Требования

  • Node.js >= 24 (см. devEngines в package.json)
  • pnpm >= 11
  • Docker (для локального PostgreSQL) — либо свой инстанс Postgres

Быстрый старт

pnpm install                                 # 1. зависимости всех воркспейсов
cp .env.example .env                         # 2. общий .env для apps/api + packages/db
cp apps/admin/.env.example apps/admin/.env   # 3. .env админки (VITE_API_URL)
docker compose up -d                         # 4. локальный PostgreSQL
pnpm db:migrate                              # 5. миграции (db:generate — создать новую)
pnpm dev:api                                 # 6. API на http://localhost:3030 (tsx watch)
pnpm dev:admin                               # 7. админка на http://localhost:5173 (Vite)

API-доки (Swagger/Scalar) — http://localhost:3030/reference (нужен запущенный pnpm dev:api).

  • .env — общий файл для apps/api и packages/db (оба резолвят его из корня). Секрет: BETTER_AUTH_SECRET (>= 16 символов), например openssl rand -base64 32.
  • Порт Postgres берётся из POSTGRES_PORT в .env (на этой машине 5432 занят другим проектом — используется 5433; проверяйте docker ps перед поднятием ещё одной БД).
  • Для админки есть свой apps/admin/.env (только VITE_API_URL); шаблон — apps/admin/.env.example.
  • Origin админки (http://localhost:5173) уже прописан в ALLOWED_ORIGINS.

Памятка по .env и портам

Два разных .env (оба в .gitignore, из шаблонов — копировать один раз после клона):

Файл Кто читает Ключевые переменные Шаблон
.env (корень) apps/api, packages/db DATABASE_URL, POSTGRES_PORT, BETTER_AUTH_SECRET, BETTER_AUTH_URL, ALLOWED_ORIGINS, ADMIN_EMAIL/ADMIN_PASSWORD .env.example
apps/admin/.env apps/admin (Vite) VITE_API_URL apps/admin/.env.example
cp .env.example .env                          # корневой (api + db)
cp apps/admin/.env.example apps/admin/.env    # админка (VITE_API_URL)

Порты по умолчанию: API — 3030, админка (Vite) — 5173, Postgres — 5433 (не 5432).

Частые проблемы

  • EADDRINUSE ... port: 3030 при pnpm dev:api — порт занят, обычно второй экземпляр API (tsx watch не умер после закрытого терминала). Найти и прибить процесс:

    Get-NetTCPConnection -LocalPort 3030 -State Listen | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }

    Затем снова pnpm dev:api.

  • Запрос авторизации уходит на 5173 вместо 3030 (http://localhost:5173/auth/get-session) — нет apps/admin/.env или в нём пустой VITE_API_URL. Без переменной better-auth берёт origin страницы (5173). Лечится cp apps/admin/.env.example apps/admin/.env + перезапуск Vite (import.meta.env.VITE_* вшивается на старте, на лету не подхватывается).

  • CORS-ошибки на все запросы к API — Vite молча уехал с занятого 5173 на 5174/5175 (смотрите порт в баннере Vite). Новый origin не в ALLOWED_ORIGINS. Освободите 5173 либо добавьте порт в ALLOWED_ORIGINS (через запятую) в корневом .env и перезапустите API (tsx watch не перечитывает .env).

  • Сменили порт API — синхронно поправьте VITE_API_URL (apps/admin/.env), BETTER_AUTH_URL и ALLOWED_ORIGINS (корневой .env), иначе фронт/CORS отвалятся.

Как проверить

Бекенд:

  • API-доки (Swagger/Scalar): http://localhost:3030/reference — интерактивный UI по всем эндпоинтам, «попробовать», + отдельный источник по auth. Требуется поднятый API (pnpm dev:api).
  • http://localhost:3030/doc — сырой OpenAPI-спек (JSON), из него рисуется /reference
  • http://localhost:3030/ — корневой эндпоинт → { "status": "ok", "data": { "message": "Test API" } }
curl http://localhost:3030/cities
curl -X POST http://localhost:3030/cities -H "Content-Type: application/json" -d '{"name":"Москва"}'

Админка: http://localhost:5173 — план дня, календарь, заявки, инженеры, рабочий график, справочники (/catalogs → типы работ, навыки, оборудование, типы графиков, города — везде поиск/фильтры, создание и правка) и /ui-kit (витрина дизайн-системы, переключатель светлой/тёмной темы). Это сквозная проверка связки фронт ↔ бек (CORS + конверт { status, data, meta }).

Полезные скрипты (из корня)

Команда Что делает
pnpm dev:api Запуск API в dev-режиме
pnpm dev:admin Запуск админки в dev-режиме
pnpm db:migrate Применить миграции к БД
pnpm db:generate Сгенерировать миграцию из схем Drizzle
pnpm build Собрать все пакеты (pnpm -r build)
pnpm format Прогнать Prettier по репозиторию

Фронтенд-архитектура (FSD)

apps/admin следует Feature-Sliced Design. Слои сверху вниз: app → pages → widgets → features → entities → shared. Импорты — только вниз; слайсы одного слоя изолированы; публичный API слайса — через index.ts.

apps/admin/src/
  app/        шелл, провайдеры, роутинг (react-router-dom), стили
  pages/      cities, ui-kit — цели маршрутов
  features/   create-city, delete-city, theme-toggle — по одному действию
  entities/   city — чтение (useCities) + ключи кэша + типы
  shared/     api (client+auth), config (query-client), model (ui-store)

Роутинг живёт только в app (app/routing.tsx + app/root-layout.tsx). Sidebar из @beeline/ui остаётся router-агностичным. Подробности и правила — в CLAUDE.md.

Дизайн-система (@beeline/ui + theme.css)

  • Токены — packages/shared/theme.css (Tailwind v4 @theme), тёмная тема по классу .dark, шрифт Onest. Приложение импортит @import '@beeline/shared/theme.css' и добавляет @source для packages/ui/src + packages/shared/src.
  • Компоненты — packages/ui (@beeline/ui). В разметке только семантические утилиты (bg-canvas/bg-surface/text-fg/border-border/bg-brand), без примитивов и произвольных значений. Иконки — в одном месте (packages/ui/src/icons/).

Общий код: @beeline/shared

Source-only TS-пакет (приложения импортируют исходники напрямую):

  • Типизированный RPC-клиент Hono (hc) — createApiClient(baseUrl); типы путей/query/тела/ответов выведены из исходников бэкенда:
    import { createApiClient, unwrap } from '@beeline/shared';
    const api = createApiClient(import.meta.env.VITE_API_URL);
    const { data, meta } = await unwrap(await api.cities.$get({ query: { limit: '10' } }));
    Тип роутера отдаётся через apps/api → api/hc. unwrap() разворачивает конверт { status, data, meta? }, сужает до успешной ветки и бросает ApiError на не-2xx.
  • Типы ресурсов (City, CitiesQuery) выводятся из клиента — руками не дублируются.
  • better-auth — authClientConfig(baseURL) даёт базовый конфиг; плагины добавляет каждое приложение.

⚠️ Типы hc берутся из собранных деклараций apps/api (api/dist/hc.d.ts):

  • перед pnpm --filter admin build / typecheck соберите API: pnpm --filter api build (в pnpm build учтено — топологический порядок);
  • import type стирается, поэтому pnpm dev:admin работает без сборки API;
  • после изменения роутов — пересоберите типы: pnpm --filter api build.

Документация для агентов

  • CLAUDE.md — единый подробный док проекта (стек, слои, команды, конвенции, гочи). Держится в актуальном состоянии и обновляется в том же коммите, что и изменение структуры/команд/контрактов (см. правило в CLAUDE.md; pre-commit-хук напоминает).
  • .claude/ — субагенты (agents/), скиллы (skills/), slash-команды (commands/); индекс — .claude/SUMMARY.md.

Деплой

CI/CD в .gitlab-ci.yml (ветка main): rsync кода, pnpm install, сборка @beeline/db и api, миграции, перезапуск через PM2. Продакшен-.env — из переменной $PRODUCTION_ENV GitLab. SPA-фронт при статик-хостинге требует history-fallback на index.html.