Архитектура Worker24
Worker24 — веб-приложение для изучения испанского языка, ориентированное на русскоязычных пользователей. Пользователи просматривают каталог учебных карточек, изучают фразы в интерактивном режиме, отслеживают прогресс и создают/редактируют карточки с помощью AI-ассистента.
Документ описывает текущую реализацию: стек, структуру репозитория, потоки данных, модель данных, аутентификацию, AI-интеграции и деплой.
Содержание
Section titled “Содержание”- Обзор системы
- Технологический стек
- Структура репозитория
- Архитектура фронтенда
- Архитектура бэкенда
- Модель данных
- Аутентификация и авторизация
- Каталог карточек и гостевой режим
- AI-редактор карточек
- Синхронизация прогресса и настроек
- Внешние интеграции
- Инфраструктура и деплой
- Режимы разработки
- Ограничения и архитектурные решения
1. Обзор системы
Section titled “1. Обзор системы”Высокоуровневая схема
Section titled “Высокоуровневая схема”flowchart TB
subgraph Client["Браузер (React SPA)"]
UI[Страницы и виджеты]
State[Zustand + TanStack Query]
AuthClient[Supabase Auth Client]
UI --> State
State --> AuthClient
end
subgraph Railway["Railway"]
API[Hono API :3001]
Static[Статика Vite :PORT]
end
subgraph Supabase["Supabase (внешний)"]
PG[(PostgreSQL)]
Auth[Auth + JWKS]
Storage[Storage Buckets]
end
subgraph OpenAI["OpenAI API"]
Chat[gpt-4o-mini]
Image[gpt-image-1]
STT[gpt-4o-transcribe]
end
Client -->|REST + SSE| API
Client -->|JWT login| Auth
API -->|Prisma| PG
API -->|Admin API| Auth
API -->|Upload| Storage
API --> Chat
API --> Image
API --> STT
Static --> Client
Основные пользовательские сценарии
Section titled “Основные пользовательские сценарии”| Сценарий | Маршрут | Описание |
|---|---|---|
| Каталог | / |
Бесконечная прокрутка карточек, фильтры, поиск, прогресс-бар |
| Изучение | /card/:id |
Оверлей с разбором фразы, примерами, практикой и AI-чатом |
| Создание | /create |
Новая карточка через AI-чат |
| Вход | /login |
Регистрация и авторизация |
| Настройки | /settings |
Тема, язык интерфейса, аккаунт |
Два пакета, один продукт
Section titled “Два пакета, один продукт”Репозиторий не является npm-workspace монорепо. Корневой package.json оркестрирует фронтенд и бэкенд через префиксные скрипты (npm run server:dev, npm run dev:all).
graph LR
Root["/ (worker24)"] --> FE["src/ — React SPA"]
Root --> BE["server/ — Hono API"]
Root --> Docs["docs/"]
BE --> Prisma["server/prisma/"]
2. Технологический стек
Section titled “2. Технологический стек”Фронтенд
Section titled “Фронтенд”| Категория | Технология | Назначение |
|---|---|---|
| UI | React 19 | Компонентная модель |
| Сборка | Vite 8 | Dev-сервер, HMR, production build |
| Маршрутизация | React Router 7 | SPA-роутинг |
| Серверное состояние | TanStack Query 5 | Кэш карточек, infinite scroll |
| Клиентское состояние | Zustand 5 (+ persist) | Auth, прогресс, настройки |
| Стили | Tailwind CSS 4 + shadcn/ui | UI-компоненты |
| i18n | Собственный реестр | ru / en / es |
| AI-чат | Vercel AI SDK (@ai-sdk/react) |
Стриминг чата редактора |
| Auth | @supabase/supabase-js |
Сессии, JWT |
| Тесты | Vitest + Testing Library | Unit-тесты |
Бэкенд
Section titled “Бэкенд”| Категория | Технология | Назначение |
|---|---|---|
| HTTP | Hono 4 | REST API, CORS, middleware |
| Runtime | Node.js 20+ | @hono/node-server |
| ORM | Prisma 6 | Схема, миграции, запросы |
| Auth verify | jose | JWT через Supabase JWKS |
| AI | Vercel AI SDK + @ai-sdk/openai |
Чат, tools, изображения, STT |
| Dev tooling | Mastra | Агент для локальной разработки (не prod) |
Инфраструктура
Section titled “Инфраструктура”| Компонент | Провайдер |
|---|---|
| База данных | Supabase PostgreSQL |
| Аутентификация | Supabase Auth |
| Файлы | Supabase Storage |
| Хостинг API + SPA | Railway (2 сервиса) |
| LLM / STT / Image | OpenAI |
3. Структура репозитория
Section titled “3. Структура репозитория”WORKER24/├── src/ # Фронтенд (React SPA)│ ├── app/ # Bootstrap: router, providers, auth/theme/locale sync│ ├── pages/ # Страницы маршрутов│ ├── widgets/ # Составные UI-блоки (TopBar, FilterBar, …)│ ├── features/ # Фичи: auth, card-creator, progress, settings, …│ ├── entities/ # Домен «карточка»: api, hooks, model, ui, lib│ ├── shared/ # api client, i18n, theme, config, ui│ ├── components/ # shadcn/ui + json-render renderer│ ├── lib/ # json-render catalog/registry│ └── data/ # Mock-данные каталога (только mock-режим)│├── server/ # Бэкенд API│ ├── src/│ │ ├── index.ts # Точка входа Hono│ │ ├── routes/ # REST-модули│ │ ├── middleware/ # Auth, resolveCatalogUser│ │ ├── lib/ # Auth, Prisma, catalog, AI, storage│ │ └── mastra/ # Mastra-агент (dev)│ └── prisma/ # schema, migrations, seed│├── docs/ # Документация (этот файл, RAILWAY.md)├── dist/ # Vite build output├── index.html # SPA shell├── vite.config.ts # Proxy /api → localhost:3001├── railway.toml # Конфиг фронтенд-сервиса└── package.json # Корневые скриптыСлои фронтенда (Feature-Sliced Design)
Section titled “Слои фронтенда (Feature-Sliced Design)”flowchart TB
subgraph Pages["pages/"]
CatalogPage
StudyPage
CreatorPage
end
subgraph Widgets["widgets/"]
TopBar
FilterBar
CatalogTopActions
end
subgraph Features["features/"]
Auth
CardCreator
Progress
Settings
CardFilter
StudyMode
end
subgraph Entities["entities/"]
Card["card (api, hooks, model, ui)"]
end
subgraph Shared["shared/"]
ApiClient
I18n
Theme
Config
end
Pages --> Widgets
Pages --> Features
Widgets --> Features
Features --> Entities
Features --> Shared
Entities --> Shared
Правило зависимостей: слой может импортировать только из слоёв ниже себя (pages → widgets → features → entities → shared).
4. Архитектура фронтенда
Section titled “4. Архитектура фронтенда”Точка входа и bootstrap
Section titled “Точка входа и bootstrap”sequenceDiagram
participant main as main.tsx
participant App as App.tsx
participant Providers as AppProviders
participant Router as RouterProvider
main->>App: render
App->>Providers: QueryClientProvider
App->>App: ThemeSync
App->>App: LocaleSync
App->>App: AuthBootstrap
App->>Router: маршруты
Файлы:
src/main.tsx— монтирование Reactsrc/app/App.tsx— композиция провайдеровsrc/app/router.tsx— маршрутыsrc/app/AuthBootstrap.tsx— инициализация Supabase-сессии
Маршрутизация
Section titled “Маршрутизация”| Path | Компонент | Особенности |
|---|---|---|
/ |
CatalogPage |
Infinite scroll, фильтры, восстановление скролла |
/card/:id |
StudyPage |
Оверлей CardEditorOverlay, AI-чат в режиме edit |
/create |
CreatorPage |
AI-чат в режиме create |
/login |
LoginPage |
signIn / signUp |
/settings |
SettingsPage |
Тема, locale, аккаунт |
Управление состоянием
Section titled “Управление состоянием”flowchart LR
subgraph ServerState["Серверное состояние"]
TQ[TanStack Query]
Cards["cardApi.getAll / getById / getTags"]
TQ --> Cards
end
subgraph ClientState["Клиентское состояние (Zustand)"]
AuthStore["authStore — token, user"]
ProgressStore["progressStore — completedCardIds"]
SettingsStore["settingsStore — theme, locale"]
end
subgraph Ephemeral["Эфемерное (React state)"]
CreatorDraft["creatorDraft — черновик AI"]
ChatMessages["messages — история чата"]
end
AuthStore -->|Bearer JWT| Cards
ProgressStore -->|sync| API
SettingsStore -->|sync| API
| Concern | Хранилище | Персистентность |
|---|---|---|
| Сессия | authStore |
В памяти (+ Supabase session) |
| Каталог карточек | TanStack Query | In-memory cache |
| Прогресс | progressStore |
localStorage + API sync |
| Настройки | settingsStore |
localStorage + API sync |
| Черновик редактора | React useState |
Только в сессии |
Repository pattern для карточек
Section titled “Repository pattern для карточек”src/entities/card/api/cardApi.ts выбирает реализацию по режиму:
const repository = env.useMockData ? mockCardRepository : httpCardRepository;- Mock: данные из
src/data/(in-memory) - Live: HTTP-запросы через
src/shared/api/client.tsсAuthorization: Bearer
API-клиент
Section titled “API-клиент”src/shared/api/client.ts — обёртка над fetch:
- Базовый URL из
VITE_API_URL - Автоматическая подстановка JWT из
authStore - Единая обработка ошибок
Конфигурация окружения
Section titled “Конфигурация окружения”src/shared/config/env.ts:
| Переменная | Значение по умолчанию | Эффект |
|---|---|---|
VITE_USE_MOCK_DATA |
true (если не "false") |
Mock-режим |
VITE_API_URL |
"" |
URL бэкенда |
VITE_SUPABASE_URL |
"" |
Supabase project URL |
VITE_SUPABASE_ANON_KEY |
"" |
Публичный anon key |
isApiEnabled = useMockData === false && apiUrl задан
5. Архитектура бэкенда
Section titled “5. Архитектура бэкенда”Точка входа
Section titled “Точка входа”server/src/index.ts — единый Hono-приложение:
flowchart TB
Index["index.ts"] --> CORS
CORS --> Health["GET /health"]
CORS --> Auth["/auth"]
CORS --> Cards["/cards"]
CORS --> Creator["/creator"]
CORS --> Progress["/users/me/progress"]
CORS --> Profile["/users/me/profile"]
CORS --> Settings["/users/me/settings"]
Index --> Startup["ensureGuestCatalog()"]
При старте асинхронно вызывается ensureGuestCatalog() — гарантирует наличие системного гостевого каталога.
API-эндпоинты
Section titled “API-эндпоинты”| Mount | Файл | Auth | Описание |
|---|---|---|---|
GET /health |
index.ts |
— | Health check |
POST /auth/register |
routes/auth.ts |
— | Регистрация через Supabase Admin |
GET /cards |
routes/cards.ts |
Опционально | Список карточек (пагинация, поиск, теги) |
GET /cards/:id |
routes/cards.ts |
Опционально | Одна карточка |
GET /cards/tags |
routes/cards.ts |
Опционально | Теги и счётчики |
GET /cards/:id/practice-choices |
routes/cards.ts |
Опционально | Варианты для практики |
POST /creator/chat |
routes/creator.ts |
— | AI-чат (SSE stream) |
POST /creator/transcribe |
routes/creator.ts |
— | Голос → текст |
GET /users/me/progress |
routes/progress.ts |
Обязательно | Список завершённых карточек |
POST /users/me/progress/:cardId |
routes/progress.ts |
Обязательно | Отметить карточку |
PUT /users/me/progress |
routes/progress.ts |
Обязательно | Bulk sync |
GET /users/me/profile |
routes/profile.ts |
Обязательно | Профиль |
PUT /users/me/profile |
routes/profile.ts |
Обязательно | Обновление + аватар |
GET /users/me/settings |
routes/settings.ts |
Обязательно | Настройки |
PUT /users/me/settings |
routes/settings.ts |
Обязательно | Обновление настроек |
Middleware
Section titled “Middleware”flowchart TD
Request["HTTP Request"] --> MW{Middleware}
MW -->|"/cards/*"| ResolveCatalog["resolveCatalogUser"]
ResolveCatalog -->|Bearer JWT valid| AuthUser["userId = claims.sub"]
ResolveCatalog -->|No token / invalid| GuestUser["userId = GUEST_USER_ID"]
MW -->|"/users/me/*"| RequireAuth["requireAuth"]
RequireAuth -->|JWT valid| AuthUser2["userId = claims.sub"]
RequireAuth -->|Invalid| Error401["401 Unauthorized"]
MW -->|"/creator/*"| NoAuth["Без auth middleware"]
resolveCatalogUser (server/src/middleware/resolveCatalogUser.ts):
- С JWT → верификация +
ensureUserProfile+ личный каталог - Без JWT → гостевой
userId
requireAuth (server/src/middleware/requireAuth.ts):
- Строгая проверка JWT для персональных данных
Слой lib/
Section titled “Слой lib/”| Модуль | Назначение |
|---|---|
auth.ts |
JWT verify (JWKS), upsert профиля, клонирование каталога |
prisma.ts |
Singleton Prisma Client |
supabase.ts |
Admin client для регистрации и Storage |
guestUser.ts |
Системный гостевой пользователь |
catalogSeeder.ts |
Seed и clone каталога |
catalog/ |
Базовые карточки + generated topics (~2350 шт.) |
cardQueries.ts |
Поиск (pg_trgm), фильтры, пагинация |
creatorSystemPrompt.ts |
System prompt для AI |
creatorDraftSchema.ts |
Zod-схема патча черновика |
generateCardImage.ts |
OpenAI Images API |
creatorImageStorage.ts |
Upload в Supabase Storage |
avatarImageStorage.ts |
Upload аватаров |
transcribeAudio.ts |
OpenAI STT |
corsOrigins.ts |
CORS whitelist |
6. Модель данных
Section titled “6. Модель данных”ER-диаграмма
Section titled “ER-диаграмма”erDiagram
User ||--o{ Card : owns
User ||--o{ Tag : owns
User ||--o{ UserProgress : tracks
User ||--o| UserSettings : has
Card ||--o{ CardTag : has
Tag ||--o{ CardTag : labels
Card ||--o{ UserProgress : completed_in
User {
string id PK "Supabase UUID"
string email UK
string name
string avatar_url
datetime created_at
}
Card {
int id PK
string user_id FK
string title
string subtitle
string level
string time
string category
string phrase
string translation
text note
string image_url
datetime created_at
}
Tag {
int id PK
string user_id FK
string name
}
CardTag {
int card_id PK,FK
int tag_id PK,FK
}
UserProgress {
string user_id PK,FK
int card_id PK,FK
datetime completed_at
}
UserSettings {
string user_id PK,FK
string theme
string locale
}
Схема: server/prisma/schema.prisma
Ключевые инварианты
Section titled “Ключевые инварианты”User.id= UUID из Supabase Auth (не auto-increment)- Карточки и теги привязаны к
userId— у каждого пользователя свой каталог - Пароли не хранятся в Prisma (удалены в миграции
20250623120000) - Поиск — GIN-индексы
pg_trgmнаtitle,phrase,translation(миграция20250625140000)
Подключение к БД
Section titled “Подключение к БД”| URL | Порт | Назначение |
|---|---|---|
DATABASE_URL |
6543 (transaction pooler) | Runtime API |
DIRECT_URL |
5432 (session pooler) | Prisma migrations |
RLS-политики в репозитории не определены — авторизация на уровне приложения (фильтрация по userId в запросах).
7. Аутентификация и авторизация
Section titled “7. Аутентификация и авторизация”Поток регистрации
Section titled “Поток регистрации”sequenceDiagram
participant Browser
participant Supabase as Supabase Auth
participant API as Hono API
participant DB as PostgreSQL
Browser->>API: POST /auth/register {email, password, name}
API->>Supabase: admin.createUser()
API->>DB: ensureUserProfile(sub, email)
API->>DB: ensureUserCatalogFromGuest(guest → new user)
API-->>Browser: { ok: true }
Browser->>Supabase: signInWithPassword()
Supabase-->>Browser: access_token (JWT)
Browser->>API: GET /cards (Authorization: Bearer)
API->>API: verifySupabaseToken(JWKS)
API->>DB: cards WHERE user_id = sub
Поток входа
Section titled “Поток входа”sequenceDiagram
participant Browser
participant Supabase as Supabase Auth
participant API as Hono API
Browser->>Supabase: signInWithPassword()
Supabase-->>Browser: session.access_token
Browser->>Browser: authStore.set(token, user)
Browser->>API: GET /users/me/profile
API->>API: JWT verify + ensureUserProfile
API-->>Browser: { user }
Browser->>Browser: mergeAndSyncProgress()
Browser->>Browser: mergeAndSyncSettings()
Верификация JWT на сервере
Section titled “Верификация JWT на сервере”server/src/lib/auth.ts:
- Загрузка JWKS с
{SUPABASE_URL}/auth/v1/.well-known/jwks.json - Проверка через
jose.jwtVerify(issuer + audience"authenticated") - Upsert
User+UserSettingsв Prisma - При первом входе — клонирование гостевого каталога
Модель доступа
Section titled “Модель доступа”| Ресурс | Гость | Авторизованный |
|---|---|---|
Каталог /cards |
Общий гостевой каталог | Личная копия каталога |
| Прогресс | Только localStorage | localStorage + сервер |
| Настройки | Только localStorage | localStorage + сервер |
| Профиль / аватар | — | Только свой |
Creator /creator |
Доступен всем | Доступен всем |
Секреты
Section titled “Секреты”| Переменная | Где | Экспозиция |
|---|---|---|
VITE_SUPABASE_ANON_KEY |
Frontend build | Публичная (by design) |
SUPABASE_SERVICE_ROLE_KEY |
Server only | Никогда на клиент |
OPENAI_API_KEY |
Server only | Только серверные вызовы |
DATABASE_URL |
Server only | Prisma connection |
8. Каталог карточек и гостевой режим
Section titled “8. Каталог карточек и гостевой режим”Гостевой пользователь
Section titled “Гостевой пользователь”Системный пользователь без Supabase-аккаунта:
GUEST_USER_ID = "00000000-0000-4000-8000-000000000001"Файл: server/src/lib/guestUser.ts
flowchart LR
Seed["catalogSeeder"] --> GuestCatalog["Гостевой каталог<br/>~2350 карточек"]
GuestCatalog --> GuestUser["GUEST_USER_ID"]
GuestUser -->|Без JWT| Anonymous["Анонимный браузер"]
GuestUser -->|При регистрации| Clone["cloneUserCatalog"]
Clone --> NewUser["Новый пользователь"]
Источники данных каталога
Section titled “Источники данных каталога”| Расположение | Когда используется |
|---|---|
server/src/lib/catalog/ |
Production API, seed, Railway |
src/data/ |
Mock-режим фронтенда |
Данные дублируются намеренно: API-сервис на Railway собирается независимо от фронтенда (docs/RAILWAY.md).
Состав каталога:
base-cards.ts— базовые карточки с Unsplash-изображениямиgenerated-topics.ts— сгенерированные темыbuildCardCatalog.ts— сборка полного каталога
Размер по умолчанию: TOTAL_CARDS = 2350 (src/shared/config/constants.ts). Для нагрузочного тестирования: SEED_TOTAL_CARDS=15000.
Запрос каталога
Section titled “Запрос каталога”sequenceDiagram
participant Browser
participant API
participant DB
Browser->>API: GET /cards?q=...&tags=...&page=1&limit=25
API->>API: resolveCatalogUser → userId
API->>DB: card.findMany(WHERE user_id, ILIKE search, tags)
DB-->>API: cards + total
API-->>Browser: { items, total, page, limit }
Поиск: ILIKE + pg_trgm GIN-индексы (server/src/lib/cardQueries.ts).
9. AI-редактор карточек
Section titled “9. AI-редактор карточек”Редактор карточек — центральная AI-фича. Поддерживает:
- Текстовый чат с ассистентом (русский UI, испанский контент)
- Tool calls для обновления черновика
- Генерацию иллюстраций
- Голосовой ввод (STT)
Важно: черновики AI не сохраняются в базу данных. Сохранение новых карточек через API пока не реализовано.
Поток AI-чата
Section titled “Поток AI-чата”sequenceDiagram
participant UI as CreatorChat
participant Hook as useCardEditorChat
participant API as POST /creator/chat
participant OpenAI
participant Storage as Supabase Storage
UI->>Hook: handleSubmit(message)
Hook->>API: { messages, draft, mode }
API->>OpenAI: streamText(gpt-4o-mini, tools)
alt updateCardDraft tool
OpenAI-->>API: tool call
API-->>Hook: SSE data-draft { patch }
Hook->>Hook: mergeCreatorDraft()
end
alt generateCardImage tool
OpenAI-->>API: tool call
API-->>Hook: SSE data-image-status: generating
API->>OpenAI: Images API
API->>Storage: upload (или data URL)
API-->>Hook: SSE data-image + data-draft { image }
API-->>Hook: SSE data-image-status: idle
end
OpenAI-->>Hook: text stream (assistant message)
Инструменты (tools)
Section titled “Инструменты (tools)”| Tool | Схема | Действие |
|---|---|---|
updateCardDraft |
creatorDraftPatchSchema |
Патч текстовых полей → data-draft SSE |
generateCardImage |
generateCardImageInputSchema |
OpenAI image → Storage → data-image SSE |
Файлы:
server/src/routes/creator.ts— эндпоинтыserver/src/lib/creatorSystemPrompt.ts— system promptsrc/features/card-creator/lib/useCardEditorChat.ts— фронтенд-хукsrc/features/card-creator/lib/mergeCreatorDraft.ts— слияние патчей
Голосовой ввод
Section titled “Голосовой ввод”sequenceDiagram
participant Browser
participant API as POST /creator/transcribe
participant OpenAI
Browser->>Browser: MediaRecorder → audio blob
Browser->>API: multipart/form-data (audio file)
API->>OpenAI: transcription (gpt-4o-transcribe)
OpenAI-->>API: text
API-->>Browser: { text }
Browser->>Browser: append to chat input
Лимит: 25 MB (MAX_TRANSCRIBE_BYTES).
Mastra (dev-only)
Section titled “Mastra (dev-only)”flowchart LR
subgraph Production["Production path"]
HonoCreator["Hono /creator/chat"]
AISDK["Vercel AI SDK"]
HonoCreator --> AISDK
end
subgraph DevOnly["Dev tooling"]
MastraServer["mastra dev :4111"]
CardAgent["cardCreatorAgent"]
MastraServer --> CardAgent
end
HonoCreator -.->|не связан| MastraServer
Mastra-агент (server/src/mastra/) не подключён к production-роутам. Запуск: npm run mastra:dev.
json-render (заготовка)
Section titled “json-render (заготовка)”Инфраструктура для AI-генерируемого UI (@json-render/*):
src/lib/json-render/catalog.tssrc/lib/json-render/registry.tsxsrc/components/json-render/GenerativeUiRenderer.tsx
GenerativeUiRenderer не используется ни на одной странице — подготовленная, но неактивная возможность.
10. Синхронизация прогресса и настроек
Section titled “10. Синхронизация прогресса и настроек”Прогресс
Section titled “Прогресс”stateDiagram-v2
[*] --> LocalOnly: Гость / mock
[*] --> LocalAndRemote: Авторизованный
LocalOnly: Zustand persist (localStorage)
LocalAndRemote: localStorage + API
LocalAndRemote --> MergeOnLogin: mergeAndSyncProgress()
MergeOnLogin: union(local, remote) → PUT /users/me/progress
LocalAndRemote --> MarkComplete: POST /users/me/progress/:cardId
Файлы:
src/features/progress/model/progressStore.tssrc/features/progress/lib/progressSync.tsserver/src/routes/progress.ts
Настройки (theme, locale)
Section titled “Настройки (theme, locale)”Аналогичная схема:
- Локально в Zustand +
localStorage(worker24-settings) - При логине:
mergeAndSyncSettings()— загрузка с сервера - При изменении:
syncSettingsToServer()→PUT /users/me/settings
Темы определены в src/shared/theme/definitions.ts. Locale: ru (default), en, es.
11. Внешние интеграции
Section titled “11. Внешние интеграции”flowchart TB
subgraph Worker24
FE[Frontend]
API[API]
end
subgraph Supabase
Auth[Auth]
PG[(PostgreSQL)]
Storage[Storage]
end
subgraph OpenAI
ChatAPI[Chat Completions]
ImageAPI[Images API]
STTAPI[Transcription]
end
subgraph External
Unsplash[Unsplash URLs]
end
FE -->|signIn/signUp| Auth
API -->|Admin API| Auth
API -->|Prisma| PG
API -->|Upload| Storage
API --> ChatAPI
API --> ImageAPI
API --> STTAPI
Seed[Seed data] -.->|image URLs| Unsplash
| Сервис | Использование | Ключевые файлы |
|---|---|---|
| Supabase Auth | Login, JWT, регистрация | src/shared/lib/supabase.ts, server/src/lib/supabase.ts |
| Supabase PostgreSQL | Все данные | server/prisma/schema.prisma |
| Supabase Storage | Аватары, AI-изображения | creatorImageStorage.ts, avatarImageStorage.ts |
| OpenAI Chat | AI-ассистент карточек | server/src/routes/creator.ts |
| OpenAI Images | Иллюстрации карточек | server/src/lib/generateCardImage.ts |
| OpenAI STT | Голосовой ввод | server/src/lib/transcribeAudio.ts |
| Unsplash | URL в seed-данных | server/src/lib/catalog/base-cards.ts |
Вебхуки, платёжные системы, email-сервисы и аналитика не используются.
12. Инфраструктура и деплой
Section titled “12. Инфраструктура и деплой”Railway: два сервиса
Section titled “Railway: два сервиса”flowchart TB
subgraph Railway
subgraph APIService["API Service (server/)"]
Build1["npm install && npm run build"]
Start1["prisma migrate deploy && node dist/index.js"]
Health1["GET /health"]
end
subgraph WebService["Frontend Service (root)"]
Build2["npm install && npm run build"]
Start2["npx serve dist -s -l $PORT"]
Health2["GET /"]
end
end
subgraph External
Supabase[(Supabase)]
OpenAI[OpenAI]
end
User((Пользователь)) --> WebService
WebService -->|VITE_API_URL| APIService
APIService --> Supabase
APIService --> OpenAI
Конфигурация:
server/railway.toml— APIrailway.toml— Frontenddocs/RAILWAY.md— пошаговая инструкция
Переменные окружения
Section titled “Переменные окружения”Frontend (bake-time, Vite):
| Переменная | Обязательна (prod) |
|---|---|
VITE_USE_MOCK_DATA |
false |
VITE_API_URL |
URL API-сервиса |
VITE_SUPABASE_URL |
Да |
VITE_SUPABASE_ANON_KEY |
Да |
Backend (server/.env.example):
| Переменная | Обязательна |
|---|---|
DATABASE_URL |
Да |
DIRECT_URL |
Да |
SUPABASE_URL |
Да |
SUPABASE_SERVICE_ROLE_KEY |
Да |
OPENAI_API_KEY |
Да (для creator) |
CORS_ORIGIN |
Да (prod) |
CREATOR_IMAGE_BUCKET |
Опционально |
AVATAR_IMAGE_BUCKET |
Опционально |
Локальная разработка
Section titled “Локальная разработка”# Оба сервиса параллельноnpm run dev:all
# Или раздельно:cd server && npm run dev # API :3001npm run dev # Vite :5173, proxy /api → :3001
# Первичная настройка БДnpm run db:setupVite proxy (vite.config.ts):
/api/* → http://localhost:3001/* (rewrite убирает /api)Автоматических пайплайнов (.github/workflows) в репозитории нет. Тестирование — локально через npm test (Vitest).
13. Режимы разработки
Section titled “13. Режимы разработки”flowchart TD
Env["VITE_USE_MOCK_DATA"]
Env -->|не равно false| Mock["Mock Mode"]
Env -->|false и задан API URL| Live["Live Mode"]
Mock --> MockRepo["mockCardRepository"]
Mock --> MockData["Локальные данные src/data"]
Mock --> NoAPI["Без API и Supabase"]
Live --> HttpRepo["httpCardRepository"]
Live --> API["Hono API"]
Live --> SupabaseAuth["Supabase Auth"]
| Аспект | Mock Mode | Live Mode |
|---|---|---|
| Каталог | src/data/ in-memory |
API + PostgreSQL |
| Auth | Опционально (Supabase) | Supabase + JWT |
| Прогресс | Только localStorage | localStorage + sync |
| AI Creator | Работает (нужен API) | Полный стек |
| Запуск | Только npm run dev |
npm run dev:all + .env |
14. Ограничения и архитектурные решения
Section titled “14. Ограничения и архитектурные решения”Принятые решения
Section titled “Принятые решения”| Решение | Обоснование |
|---|---|
| Supabase Auth + Prisma ORM | Auth делегирован Supabase; данные через Prisma без RLS |
| Гостевой каталог | Полный UX без регистрации; auth даёт личную копию |
| Per-user catalog clone | Изоляция данных; каждый пользователь владеет своими карточками |
| Дублирование seed-данных | Независимая сборка API на Railway |
| Vercel AI SDK (не Mastra) в prod | Прямой контроль над стримингом и tools в Hono |
| Zustand persist + API sync | Офлайн-прогресс для гостей; merge при логине |
Текущие ограничения
Section titled “Текущие ограничения”| Ограничение | Статус |
|---|---|
| AI-черновики не сохраняются в БД | Нет POST /cards |
| json-render не используется в UI | Инфраструктура готова |
| Mastra не в production | Только mastra dev |
| Нет CI/CD | Ручной деплой на Railway |
| Нет backend-тестов | Только frontend Vitest |
| Cross-package import в seed | seedConfig.ts импортирует TOTAL_CARDS из src/ |
Диаграмма зрелости компонентов
Section titled “Диаграмма зрелости компонентов”quadrantChart
title Зрелость компонентов
x-axis Низкая интеграция --> Высокая интеграция
y-axis MVP --> Production-ready
quadrant-1 Зрелые core-фичи
quadrant-2 Требуют доработки
quadrant-3 Эксперименты
quadrant-4 Инфраструктура
Каталог: [0.85, 0.9]
Auth: [0.8, 0.85]
Прогресс: [0.75, 0.8]
AI Creator: [0.7, 0.75]
Настройки: [0.7, 0.8]
json-render: [0.1, 0.2]
Mastra: [0.15, 0.25]
CI/CD: [0.2, 0.3]
Save Card API: [0.0, 0.1]
Связанные документы
Section titled “Связанные документы”- Деплой на Railway
- Prisma-схема:
server/prisma/schema.prisma - Примеры env:
.env.example,server/.env.example
Документ актуален для версии 0.1.0. При изменении архитектуры обновляйте соответствующие разделы и диаграммы.