Архитектура
Simmetric Chat - это модульный TypeScript-монорепозиторий с 5 пакетами плюс десктопное приложение Tauri. Руководящий принцип - разделение границ: каждый пакет несёт отдельную ответственность и общается с другими только через хорошо определённые интерфейс…
Simmetric Chat - это модульный TypeScript-монорепозиторий с 5 пакетами плюс десктопное приложение Tauri. Руководящий принцип - разделение границ: каждый пакет несёт отдельную ответственность и общается с другими только через хорошо определённые интерфейсы (HTTP или пакет shared).
Общий обзор
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ frontend │ │ widget │ │ src-tauri │
│ React 19 │ │ Preact IIFE │ │ Desktop app │
│ Vite :5173 │ │ Express:3211│ │ │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ /api (JWT) │ /api/internal/widget (X-Api-Key) │
│ │ │
▼ ▼ │
┌────────────────────────────────────┐ │
│ server │◄──── Tauri usa il ────┘
│ Express :3000 (RBAC, agent, RAG) │ server via HTTP
└──────┬──────────────────┬──────────┘
│ HTTP (COLLECTOR_SECRET) │ HTTP (status callback)
▼ ▼
┌──────────────┐ ┌──────────────┐
│ collector │ │ PostgreSQL │
│ Express:3210│ │ (Prisma) │
│ parse/embed │ └──────────────┘
└──────────────┘
│
▼
┌──────────────────────────┐
│ Vector DB (LanceDB/ │
│ Qdrant) + LLM (Ollama/ │
│ OpenAI/Anthropic) │
└──────────────────────────┘5 пакетов
shared - листовой узел
Типы, Zod-схемы и константы. Ноль бизнес-логики, единственная runtime-зависимость - zod. Его импортируют server, collector, frontend. Никогда не импортируй из других пакетов: направление всегда * → shared. Это гарантирует порядок сборки Turborepo и предотвращает циклы.
src/types/- интерфейсы сущностей (User, Workspace, Chat, Document, Provider, Widget, BackupDestination, Archive, OcrJob, SynthesisRun, ...).src/schemas/- Zod (auth, chat, document, widget, mcpConnection, license, backup, archive, wiki, ocr, synthesis, provider). Одна схема на домен, вывод типа черезz.infer.src/constants/-permissions.ts(31 разрешение RBAC),license.ts(11 enterprise-флагов функций; 8 commodity-флагов всегда активны в Community).
server - сердце (Express :3000)
REST API + RBAC + оркестратор агента. См. packages/server/CLAUDE.md.
src/routes/- один файл на домен (auth, users, roles, projects, workspaces, documents, agent, chat, providers, apiKeys, eventLogs, analytics, backups, backupJobs, backupDestinations, webhooks, push, license, sso, templates, settings, mcpConnections, marketplace, widgets, internalWidget, health, ocr, archives, wiki, synthesis).src/middleware/-auth.ts(JWT + API key),rbac.ts(разрешения + защита от IDOR),rateLimit.ts(200/мин общий, 10/мин auth, 10/мин probe - отдельного чат-лимитера нет: агент ограничивает конкурентность/бюджет/тайм-аут внутри себя),license.ts(feature flag gating),widgetCors.ts(динамический CORS по origin).src/services/- authService, providerService (разрешение модели + capability), hybridSearchService (RRF), ftsService (tsvector), encryptionService (AES-256-GCM), backupSchedulerService (Bree), eventLogService (+ отправка webhook), dlpFilter и т.д.src/agent/-orchestrator.ts(цикл ReAct),builtinSkills.ts(rag_search, workspace_memory, document_temp_process),llmStreaming.ts(Ollama/OpenAI/Anthropic),mcpClient.ts(исходящие MCP-подключения),mcpServer.ts(RAG как MCP-tool для IDE).src/config/env.ts- Zod-схема для всех env-переменных;process.exit(1)при невалидных значениях.
collector - микросервис ингестии (Express :3210)
Общается с сервером только по HTTP (секрет COLLECTOR_SECRET). Не импортирует Prisma и сервисы сервера: критическая граница для air-gap развёртывания на раздельных узлах.
src/routes/ingest.ts- загрузка (multer), query, youtube, delete.src/services/- parser (PDF/DOCX/PPTX/XLSX/TXT/CSV/YouTube), chunker (RecursiveCharacterTextSplitter), embeddings (локальный Xenova / OpenAI), vectorStore (LanceDB / Qdrant).- Поток: server → collector
POST /api/ingest/upload→ parse → chunk → embed → store → обратный вызов collectorPUT /api/documents/:id/status→ сервер обновляет DB.
widget - встраиваемый чат (Express :3211)
Express CommonJS + Preact IIFE bundle. Аутентифицируется на сервере через X-Api-Key.
src/index.ts- фабрикаcreateApp(); статика/widget/app.js+ loader routes + API.src/routes/- chat (SSE-прокси), session, config, loader, lead.src/widget/- компоненты Preact (12 компонентов) в sandboxed iframe.- Прозрачный SSE-прокси: ретрансляция сырых байтов server→iframe, ноль парсинга.
frontend - SPA (Vite + React 19 :5173)
Никакого Next.js. Прокси /api → server.
- Трёхуровневое управление состоянием: TanStack Query (REST/CRUD), React Context (UI-состояние), fetchEventSource + useState/useRef (SSE, НЕ TanStack Query).
src/queries/(22 хука),src/contexts/,src/hooks/useChat.ts(стриминг).- i18n: 8 языков (en, it, ru, de, fr, es, zh, pt).
src-tauri - десктопное приложение
Обёртка Tauri, упаковывающая фронтенд как десктопное приложение; общается с сервером по HTTP (тот же API). Рассчитана на air-gap развёртывание на машинах без браузера.
Основные Data Flow
Chat streaming (внутренний пользователь)
Frontend useChat → POST /api/workspaces/:id/chat/stream (SSE)
→ requireWorkspaceAccess (RBAC/IDOR)
→ agent orchestrator (ReAct loop)
├─ resolveSkillsForChat (builtin + MCP pinned)
├─ rag_search (vector + tsvector → RRF merge) se serve
├─ llmStreaming (provider risolto: per-chat → workspace → global → ENV)
└─ tool execution (sandboxed)
→ SSE: token | status | citations | done(modelUsed,providerUsed,mcpSources) | errorРазрешение модели: per-chat override → workspace default → global default → ENV. Graceful fallback на 3 уровнях, если модель недоступна.
Ингестия документов
Frontend dropzone → POST /api/documents (server, multer)
→ server salva Document(status=pending) + forwarda file a collector
→ collector: parse → chunk → embed → store(vector) → ritorna chunks con text
→ collector callback PUT /api/documents/:id/status { status, chunkCount }
→ server scrive document_chunks + popola searchVector (tsvector) lato DB
→ frontend polla stato fino a completedWidget chat (анонимный посетитель)
Pagina host → <script src="/widget/:id.js"> (loader)
→ iframe sandboxed → Preact app
→ POST /api/sessions (widget) → server crea WidgetSession (256-bit token, 24h)
→ GET /api/config/:widgetId (branding, trigger, lead)
→ user messaggio → POST /api/chat/:widgetId/stream (widget, x-session-token)
→ sessionMiddleware valida → pre-search RAG (whitelist workspaces, IDOR-safe)
→ proxy a server /api/workspaces/:id/chat/stream (X-Api-Key)
→ relay SSE byte verbatim → Preact parse token/citations/done/errorГраницы и архитектурные правила
- `shared` - лист - никаких импортов в другие пакеты, даже
import type. - Server ↔ collector = только HTTP - никаких runtime-импортов между пакетами; у collector нет прямого доступа к DB. Это позволяет масштабировать collector на отдельные узлы.
- Widget = отдельный сервис - не делит код с React-фронтендом; использует Preact для маленького bundle в sandboxed iframe.
- Prisma singleton - всегда импортируй из
src/utils/prisma, никогдаnew PrismaClient(). - Универсальный soft delete - удаляемые сущности используют
deletedAt; все запросы фильтруютwhere: { deletedAt: null }. - Config resolution -
ALWAYS_READONLY(JWT_SECRET, DATABASE_URL, порты, URL):ENV > Default. Все остальные ключи:DB > ENV > Default(изменяются из UI).
Расширение архитектуры
- Новая сущность/endpoint → добавь Zod-схему в
shared, модель Prisma, маршрут вserver/src/routes/, хук TanStack вfrontend/src/queries/. - Новый agent skill → зарегистрируй в
server/src/agent/builtinSkills.ts(sandboxed, без произвольного выполнения кода). - Новый язык → добавь
frontend/src/i18n/<lang>/translation.json+ обновиi18n-check.cjs(см. 10 - Development). - Новый LLM provider → расширь
providerTypeSchemaвshared+ handler вserver/src/agent/llmStreaming.ts+providerService.
Cross-link
- Детали env/config: 03 - Configuration
- Endpoint по доменам: 04 - API Reference
- Внутренности функций: 05 - Feature Guide