Архитектура backend API
Архитектура backend API (@lowcode/api)
Как устроен сервер приложений: модули NestJS, слои, потоки запросов, работа с БД и интеграция с AI.
🎯 Задача страницы
Эта страница описывает архитектуру приложения @lowcode/api:
- как организованы модули и слои NestJS;
- как устроен жизненный цикл HTTP‑запроса;
- как API работает с БД через Prisma;
- как реализованы основные сценарии: проекты, версии, DSL, компиляция;
- как устроен модуль AI и интеграция с
@lowcode/ai-orchestrator; - где искать код и как добавлять новые функции.
1. Роль @lowcode/api в общей архитектуре
@lowcode/api — центральный backend‑сервис платформы. Он:
- хранит проекты и их версии в PostgreSQL;
- принимает и валидирует DSL‑структуры;
- вызывает
@lowcode/dsl-compilerдля генерации бандлов; - отдаёт данные редактору
builder-webи runtime‑хосту; - предоставляет REST API для всех ключевых операций;
- делегирует AI‑сценарии пакету
@lowcode/ai-orchestratorи проксирует вызовы к LLM‑провайдерам.
Высокоуровневые схемы:
graph TD
BW[builder-web] -->|HTTP| API[@lowcode/api]
API --> DB[(PostgreSQL)]
API --> DSL[dsl-compiler]
DSL --> BNDL[JS Bundle]
BNDL --> RH[runtime-host]
AI‑контур:
graph TD
BW[builder-web<br/>AI Assist] -->|POST /ai/assist| API_AI[API: AiController]
API_AI --> AI_ORCH[@lowcode/ai-orchestrator]
AI_ORCH --> LLM[LLM провайдеры<br/>(OpenAI / AI Tunnel / локальный)]
AI_ORCH --> DSL_OPS[AI-операции над AppSchema]
DSL_OPS --> BW
2. Структура приложения NestJS
Архитектурно @lowcode/api использует стандартный подход NestJS:
- модули (modules) — логические блоки функциональности;
- контроллеры (controllers) — HTTP‑эндпоинты;
- сервисы (services) — бизнес‑логика;
- инфраструктурный слой — Prisma, конфигурация, фильтры, пайпы.
Типичная структура каталога:
apps/api/src/
main.ts ← точка входа (NestFactory + FastifyAdapter)
app.module.ts ← корневой модуль
modules/
auth/ ← регистрация, логин, JWT-стратегии
account/ ← личный кабинет пользователя/клиента
clients/ ← управление организациями и подписками
users/ ← сервисы пользователей клиента
projects/ ← работа с проектами
project-versions/ ← работа с версиями
dsl/ ← валидация и операции с DSL
ai/ ← AI-ассистент, интеграция с ai-orchestrator
database/ ← PrismaService, PrismaModule
common/ ← общие фильтры, пайпы, декораторы
Для деталей реализации см. также:
- точка входа
apps/api/src/main.ts; - корневой модуль
apps/api/src/app.module.ts.
3. Жизненный цикл HTTP‑запроса
Упрощённый путь любого запроса:
Fastify → NestJS → Controller → Service → Prisma/DSL/AI → Response
3.1. Fastify + NestFactory
В main.ts создаётся приложение NestJS на основе Fastify:
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());
Также на этом этапе:
- настраиваются глобальные пайпы (
ValidationPipe); - включается CORS (для доступа builder-web);
- конфигурируются префиксы роутов, логгирование и пр.
3.2. Контроллер
Контроллер отвечает за приём HTTP‑запросов и маппинг DTO:
@Controller('projects')
export class ProjectsController {
constructor(private readonly service: ProjectsService) {}
@Get()
findAll() {
return this.service.findAll();
}
@Post()
create(@Body() dto: CreateProjectDto) {
return this.service.create(dto);
}
}
Особенности:
- маршруты объявляются через декораторы
@Get,@Post,@Patchи т.д.; - входные данные типизируются DTO‑классами и валидируются пайпами;
- контроллер ничего не знает о БД, только делегирует в сервис.
3.3. Сервис
Сервис инкапсулирует бизнес‑логику, работу с БД и сторонними модулями:
@Injectable()
export class ProjectsService {
constructor(private readonly prisma: PrismaService) {}
findAll() {
return this.prisma.project.findMany();
}
create(dto: CreateProjectDto) {
return this.prisma.project.create({ data: dto });
}
}
В случае AI‑сценариев сервис (AiService) дополнительно:
- адаптирует DTO из HTTP‑слоя к
AiAssistRequest; - вызывает
assist()из@lowcode/ai-orchestrator; - реализует низкоуровневый адаптер
callModel()к OpenAI‑совместимому API.
3.4. Ответ
NestJS автоматически сериализует возвращаемые объекты в JSON и отправляет их клиенту (builder‑web или runtime‑host).
4. Работа с БД: Prisma + PostgreSQL
4.1. Prisma schema
Модели данных находятся в apps/api/prisma/schema.prisma. Там описаны сущности:
Project— проект пользователя;ProjectVersion— версии проекта;- дополнительные сущности (шаблоны, пользователи и т.п. при расширении).
Пример упрощённой модели:
model Project {
id String @id @default(cuid())
name String
createdAt DateTime @default(now())
versions ProjectVersion[]
}
4.2. PrismaService и PrismaModule
Для интеграции Prisma с NestJS используется обёртка PrismaService:
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
}
Она регистрируется в PrismaModule и импортируется в AppModule.
Сервисы получают PrismaService через DI и используют его для запросов:
this.prisma.project.findMany();
4.3. Миграции и окружения
- миграции лежат в
apps/api/prisma/migrations/*; - в Docker‑окружениях они применяются автоматически при старте API;
- локально миграции выполняются через скрипты
pnpm prisma:migrate-dev/prisma:migrate-deploy.
5. Доменные модули: проекты, версии, DSL, компиляция, AI
5.1. Модуль Projects
Отвечает за:
- создание, обновление, удаление проектов;
- получение списка и деталей проекта.
Типичные эндпоинты:
GET /projects
GET /projects/:id
POST /projects
PATCH /projects/:id
DELETE /projects/:id
Реализация доменной логики и HTTP‑слоя для проектов включает:
- модуль
ProjectsModule; - контроллер
ProjectsController; - сервис
ProjectsService; - сущность
Projectи DTOCreateProjectDto/UpdateProjectDto.
5.2. Модуль ProjectVersions
Модуль версий (ProjectVersion):
- хранит снимки состояния проекта во времени;
- позволяет откатываться к предыдущим версиям;
- обеспечивает совместимость с компилятором.
Примерный набор эндпоинтов:
GET /projects/:id/versions
GET /project-versions/:versionId
POST /projects/:id/versions
Реализация:
- модуль
ProjectVersionsModule; - контроллер
ProjectVersionsController; - сервис
ProjectVersionsService; - сущность
ProjectVersionEntityи DTOCreateProjectVersionDto.
5.3. Модуль DSL
Роль модуля DSL:
- валидация DSL‑дерева, которое приходит от builder-web;
- базовые проверки структуры, типов компонентов, связей.
API‑слой предоставляет эндпоинт вида:
POST /dsl/validate
При вызове:
- принимается JSON‑описание приложения (
AppSchema); - запускаются валидаторы из
@lowcode/dsl/@lowcode/dsl-compiler; - возвращается результат валидации (ошибки, предупреждения).
Кроме того, модуль DSL может включать эндпоинты компиляции (см. ниже).
5.4. Модуль Compile (часть DSL‑модуля)
Роль:
- принимать DSL‑дерево и проходить цикл DSL → AST → Bundle;
- возвращать готовый JS‑ или HTML‑бандл редактору или runtime‑host.
Типичные эндпоинты:
POST /dsl/compile/react
POST /dsl/compile/html
Внутри сервис компиляции использует функции @lowcode/dsl-compiler:
compileDslToReact(app, options)— генерация TSX/JS бандла;compileDslToHtml(app, options)— генерация HTML‑выхода.
Модуль компиляции разделяет инфраструктуру с модулем DSL (те же DTO, валидаторы, error‑handling).
5.5. Модуль AI (AiModule)
Новый модуль, отвечающий за AI‑ассистента и интеграцию с @lowcode/ai-orchestrator.
Основные элементы:
AiController— HTTP‑слой;AiService— адаптер к@lowcode/ai-orchestratorи LLM‑провайдерам;- DTO
AiAssistDto— входной формат запроса от builder-web.
5.5.1. Эндпоинты AI
На данный момент модуль предоставляет основной эндпоинт:
POST /ai/assist
Тело запроса — AiAssistDto, по сути зеркалящий AiAssistRequest из @lowcode/shared-types:
interface AiAssistDto {
prompt: string;
appSchema: AppSchema;
projectId?: string;
pageId?: string;
hints?: AiEditorContextHints;
provider?: AiProviderType; // optional, может быть переопределён на сервере
model?: string; // модель LLM (например, "gpt-5-mini")
requestId?: string; // для трейсинга
}
Ответ — AiAssistResponse:
interface AiAssistResponse {
updatedAppSchema?: AppSchema; // новый DSL после применения AI-операций
operations?: AiOperation[]; // список операций, которые были применены
explanation?: string; // текстовое объяснение для UI
error?: AiError; // если что-то пошло не так
}
5.5.2. Поток запроса AI
-
builder-web вызывает
callAiAssist()и отправляетAiAssistRequestна/ai/assist. -
AiControllerпринимаетAiAssistDtoи передаёт его вAiService.assist(). -
AiService:- нормализует провайдера и модель;
- собирает
AiAssistRequestдля@lowcode/ai-orchestrator; - вызывает функцию
assist(input, callModel)из@lowcode/ai-orchestrator.
-
assist()внутри@lowcode/ai-orchestrator:- строит промпт и
AiModelRequest(см.buildPrompt.ts); - вызывает переданный коллбек
callModel(); - парсит ответ модели в список
AiOperation(см.parseResponse.ts); - применяет операции к
AppSchema(см.applyOperations.ts).
- строит промпт и
-
AiServiceвозвращает полученныйAiAssistResponseклиенту.
5.5.3. Адаптер callModel() и провайдеры
AiService реализует низкоуровневый адаптер к OpenAI‑совместимому API:
private async callModel(req: AiModelRequest): Promise<AiModelResponse> {
const baseUrl = this.getProviderBaseUrl(req.provider);
const apiKey = this.getProviderApiKey(req.provider);
const url = `${baseUrl}/chat/completions`;
const body = {
model: req.model,
messages: req.messages.map((m) => ({ role: m.role, content: m.content })),
max_tokens: req.maxTokens,
temperature: req.temperature,
};
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
5.6. Модуль Attachments
Модуль для работы с вложениями (файлы, изображения).
Модуль AttachmentsModule предоставляет полный CRUD для attachments с поддержкой двух типов хранения:
- link — внешние URL (например, ссылки на CDN)
- s3 — файлы, загруженные в AWS S3
5.6.1. Модель данных
model Attachment {
id String @id @default(uuid())
clientId String
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
kind AttachmentKind // 'link' или 's3'
// Для kind = 'link'
sourceUrl String?
downloadUrl String?
// Для kind = 's3'
storageKey String?
bucket String?
url String?
name String?
mimeType String?
size Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([projectId])
@@index([kind])
}
enum AttachmentKind {
link
s3
}
Важные поля:
kind— тип вложения ('link' или 's3')sourceUrl— публичная ссылка (например, короткая ссылка Яндекс.Диска)downloadUrl— прямая ссылка на скачивание (может истекать)storageKey— ключ файла в S3 bucketurl— presigned URL для скачивания S3 (временный, TTL 1 час)
5.6.2. AttachmentsService
Местоположение: apps/api/src/modules/attachments/attachments.service.ts
Основные методы:
class AttachmentsService {
// Создать link attachment
async createLinkAttachment(clientId: string, dto: CreateLinkAttachmentDto): Promise<Attachment>
// Запросить presigned URL для загрузки в S3
async requestUploadUrl(clientId: string, dto: RequestUploadUrlDto): Promise<{
uploadUrl: string;
storageKey: string;
}>
// Создать S3 attachment после загрузки
async createS3Attachment(clientId: string, dto: CreateS3AttachmentDto): Promise<Attachment>
// Получить attachment по ID (с обновлением presigned URL)
async getAttachment(clientId: string, attachmentId: string): Promise<Attachment>
// Список всех attachments проекта
async listAttachments(clientId: string, projectId: string): Promise<Attachment[]>
// Обновить attachment
async updateAttachment(clientId: string, attachmentId: string, dto: UpdateAttachmentDto): Promise<Attachment>
// Удалить attachment (с удалением из S3)
async deleteAttachment(clientId: string, attachmentId: string): Promise<void>
}
Критически важные фичи:
-
Валидация projectId — метод
ensureProject()проверяет существование проекта и доступ:private async ensureProject(clientId: string, projectId: string): Promise<void> {
const project = await this.prisma.project.findUnique({
where: { id: projectId },
select: { id: true, clientId: true },
});
if (!project) {
throw new NotFoundException(`Project with id "${projectId}" not found`);
}
if (project.clientId !== clientId) {
throw new ForbiddenException('Access denied to this project');
}
}Используется во всех методах создания и загрузки для предотвращения 500 Internal Server Error.
-
LinkNormalizer — валидация и нормализация внешних URL:
class LinkNormalizer {
normalize(url: string): string {
// Google Drive: извлекает fileId и формирует прямую ссылку
// Yandex Disk: запрашивает публичный API и берёт href
}
} -
Генерация presigned URLs — для S3 attachments генерируются временные URL (TTL 1 час):
async getAttachment(clientId: string, attachmentId: string): Promise<Attachment> {
const attachment = await this.ensureAttachment(clientId, attachmentId);
if (attachment.kind === 's3' && attachment.storageKey) {
// Генерируем свежий presigned URL
const downloadUrl = await this.storageService.getDownloadUrl(attachment.storageKey);
return { ...attachment, downloadUrl };
}
return attachment;
} -
Обновление прямых ссылок (refresh) — endpoint
/attachments/:id/refresh-url:- для s3 — обновляет
url(новый presigned URL); - для link — повторно нормализует
sourceUrl(получает новыйdownloadUrl).
- для s3 — обновляет
5.6.3. StorageService
Местоположение: apps/api/src/modules/storage/storage.service.ts
Работа с AWS S3:
class StorageService {
// Генерация presigned URL для загрузки (TTL 1 час)
async getUploadUrl(key: string, contentType: string): Promise<string>
// Генерация presigned URL для скачивания (TTL 1 час)
async getDownloadUrl(key: string): Promise<string>
// Удаление файла из S3
async deleteFile(key: string): Promise<void>
}
Конфигурация (.env):
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-key
AWS_SECRET_ACCESS_KEY=your-secret
S3_BUCKET_NAME=lowcode-attachments
5.6.4. Эндпоинты
// Список всех вложений проекта
GET /attachments?projectId=<uuid>
→ Attachment[]
// Получить вложение по ID
GET /attachments/:id
→ Attachment (с обновленным presigned URL)
// Создать link attachment
POST /attachments/link
Body: { projectId, url, name?, mimeType? }
→ Attachment
// Запросить URL для загрузки в S3
POST /attachments/upload-url
Body: { projectId, filename, contentType }
→ { uploadUrl, storageKey }
// Создать S3 attachment после загрузки
POST /attachments/s3
Body: { projectId, storageKey, name, mimeType, size }
→ Attachment
// Обновить вложение
PATCH /attachments/:id
Body: { name?, url? }
→ Attachment
// Удалить вложение
DELETE /attachments/:id
→ void
Все эндпоинты защищены JWT-авторизацией и используют project scoping.
5.6.5. Процесс загрузки S3 файла
Типичный flow для загрузки файла:
// 1. Frontend запрашивает presigned URL
const { uploadUrl, storageKey } = await requestUploadUrl({
projectId: "550e8400-...",
filename: "photo.jpg",
contentType: "image/jpeg"
});
// 2. Frontend загружает файл напрямую в S3
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': 'image/jpeg' },
body: file
});
// 3. Frontend создает запись в БД
const attachment = await createS3Attachment({
projectId: "550e8400-...",
storageKey,
name: "photo.jpg",
mimeType: "image/jpeg",
size: 1024000
});
// → { id: "att-123...", kind: "s3", downloadUrl: "https://..." }
5.6.6. Безопасность и валидация
- JWT авторизация на всех эндпоинтах
- Project scoping — пользователь видит только attachments своих проектов
- ensureProject() — проверка существования проекта и доступа (предотвращает 500 ошибки)
- ensureAttachment() — проверка существования attachment и доступа
- Presigned URLs с TTL (1 час для S3)
- Валидация URL через LinkNormalizer
- Автоматическое удаление файлов из S3 при удалении attachment
5.6.7. Тестирование
Unit tests: apps/api/tests/unit/
attachments.service.spec.ts— тесты AttachmentsServicelink-normalizer.spec.ts— тесты валидации URL
Все тесты покрывают основные сценарии и edge cases.
5.7. Клиенты и пользователи
Чтобы поддержать работу разных организаций (клиентов), в API добавлены специализированные модули:
ClientsModule— CRUD данных клиента, управление тарифом (SubscriptionTier), логом переходов (SubscriptionHistory);UsersModule— операции с пользователями внутри клиента: создание, изменение ролей (OWNER / ADMIN / MEMBER), смена пароля, отметка логинов.
Эти модули не имеют публичных контроллеров, но используются сервисами auth/account и доменных контроллеров для проверки прав. Все сущности проектов и версий теперь жёстко привязаны к clientId и фильтруются на уровне Prisma‑запросов.
5.7. Auth + Account (JWT‑контур)
AuthModule отвечает за регистрацию клиента (POST /auth/register), логин (/auth/login), обновление токенов (/auth/refresh) и logout (/auth/logout). Внутри используются:
JwtAccessStrategy(Passport +passport-jwt) — читает Bearer‑токен, проверяет подпись и возвращаетRequestUser;SessionsService— хранит refresh‑токены в таблицеuser_sessions, поддерживает ротацию и отзыв.
Все защищённые контроллеры (/projects, /projects/:id/versions, /account/*) используют JwtAuthGuard и декоратор @CurrentUser() для извлечения clientId / userId. Благодаря этому любая бизнес‑логика может быть client-scoped без ручной передачи идентификаторов.
AccountModule реализует личный кабинет: профили пользователей, смену пароля, настройки клиента и просмотр подписки. Эти эндпоинты будут использоваться builder-web, когда появится UI кабинета.
6. Доступные HTTP‑эндпоинты
Ниже собраны основные маршруты API. Все ответы возвращаются в JSON, любые запросы (кроме auth/*) требуют Bearer‑токен, полученный после логина/регистрации.
6.1. Аутентификация (/auth/*)
| Метод | Путь | Описание | Auth |
|---|---|---|---|
| POST | /auth/register | Создаёт клиента + владельца, сразу возвращает токены. | Нет |
| POST | /auth/login | Авторизация по email/паролю, выдаёт access/refresh. | Нет |
| POST | /auth/refresh | Ротация токенов по refresh‑токену. | Нет |
| POST | /auth/logout | Отзывает refresh‑сессию (refresh в теле запроса). | Нет |
6.2. Личный кабинет (/account/*)
| Метод | Путь | Описание |
|---|---|---|
| GET | /account/profile | Комбинированный профиль (пользователь + клиент). |
| PATCH | /account/profile | Обновление displayName. |
| PATCH | /account/password | Смена пароля (current/new). |
| PATCH | /account/client | Редактирование имени клиента и биллингового email. |
| GET | /account/subscription | Текущий тариф и срок подписки клиента. |
| GET | /account/subscription/history | Хронология смен тарифов. |
Все маршруты используют JwtAuthGuard и работают строго в рамках clientId из токена.
6.3. Проекты (/projects)
| Метод | Путь | Описание |
|---|---|---|
| GET | /projects | Список проектов клиента (от новых к старым). |
| GET | /projects/:id | Детали проекта, проверяется принадлежность клиенту. |
| POST | /projects | Создание проекта (name, optional description). |
| PATCH | /projects/:id | Обновление названия/описания/статуса. |
| DELETE | /projects/:id | Удаление проекта и всех его версий. |
6.4. Версии проекта (/projects/:projectId/versions)
| Метод | Путь | Описание |
|---|---|---|
| GET | /projects/:projectId/versions | Список версий (desc). |
| GET | /projects/:projectId/versions/:versionNumber | Получение версии по порядковому номеру. |
| POST | /projects/:projectId/versions | Создание новой версии (сырое DSL). |
| POST | /projects/:projectId/versions/:versionId/rollback | Обновляет currentVersionId проекта. |
| DELETE | /projects/:projectId/versions/:versionId | Удаляет сохранённую версию проекта. |
Все запросы дополнительно валидируют, что projectId принадлежит текущему клиенту.
6.5. DSL и компиляция
| Метод | Путь | Описание |
|---|---|---|
| POST | /dsl/validate | Проверка DSL‑дерева, возврат ошибок. |
| POST | /dsl/compile* | (опционально) компиляция в бандл. |
* Конкретные под‑роуты зависят от реализации (/dsl/compile/react, /dsl/compile/html и т.п.).
6.6. Вложения (/attachments)
| Метод | Путь | Описание |
|---|---|---|
| GET | /attachments?projectId=<uuid> | Список всех attachments проекта. |
| GET | /attachments/:id | Получение attachment по ID (с обновленным presigned URL). |
| POST | /attachments/link | Создание link attachment (внешний URL). |
| POST | /attachments/request-upload-url | Запрос presigned URL для загрузки в S3. |
| POST | /attachments/s3 | Создание S3 attachment после загрузки файла. |
| PATCH | /attachments/:id | Обновление attachment (name, url). |
| DELETE | /attachments/:id | Удаление attachment (и файла из S3). |
Все запросы требуют JWT-авторизации и валидируют принадлежность проекта текущему клиенту.
6.7. AI‑ассистент
| Метод | Путь | Описание |
|---|---|---|
| POST | /ai/assist | Делегирует запрос AiAssistDto в @lowcode/ai-orchestrator. |
Запросы включают DSL‑дерево + контекст редактора, ответ содержит операции и обновлённый AppSchema.
if (apiKey) {
headers.Authorization = `Bearer ${apiKey}`;
}
const response = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body) });
// ... разбор JSON и маппинг в AiModelResponse
}
Поддерживаются три типа провайдеров (AiProviderType):
direct— прямой доступ к OpenAI‑подобному API;aiTunnel— доступ через собственный AI‑прокси (AI Tunnel);local— локальная модель (например, Ollama) с OpenAI‑совместимым REST.
Конкретные URL и ключи берутся из .env (см. раздел «Настройка AI Assistant» ниже).
6. Общие компоненты: DTO, валидация, ошибки
6.1. DTO и ValidationPipe
DTO‑классы используют декораторы class-validator:
export class CreateProjectDto {
@IsString()
name: string;
}
Глобальный ValidationPipe в main.ts гарантирует, что:
- входные данные соответствуют типам;
- лишние поля отбрасываются (
whitelist: true).
То же относится и к AiAssistDto: неправильные типы будут отброшены или вызовут ошибку валидации.
6.2. Фильтры ошибок
Через NestJS ExceptionFilter можно:
- унифицировать формат ошибок;
- логировать непойманные исключения;
- возвращать понятные ответы клиенту.
Модуль AI опирается на те же механизмы: ошибки адаптера callModel() пробрасываются наверх, а @lowcode/ai-orchestrator дополнительно нормализует их в поле AiAssistResponse.error.
7. Тестирование API
@lowcode/api покрывается тестами на нескольких уровнях:
- unit‑тесты сервисов (мокаем
PrismaServiceи внешние зависимости); - integration‑тесты модулей (через
Test.createTestingModule); - e2e‑тесты в Docker‑окружении (API тестируется через HTTP как чёрный ящик).
Тесты лежат в:
apps/api/src/**/*.spec.ts
Для AI‑модуля рекомендуется:
- мокать
AiService.callModel()и проверять только glue‑логику; - либо мокать весь
AiServiceна уровне контроллера и проверять HTTP‑контракт.
8. Настройка AI Assistant в @lowcode/api
Для работы AI‑ассистента backend использует переменные окружения. Минимальный .env для apps/api может выглядеть так:
DATABASE_URL=postgresql://lowcode_user:lowcode_password@localhost:5432/lowcode
# Прямой доступ к OpenAI-подобному API
AI_DIRECT_API_KEY=sk-...
AI_DIRECT_BASE_URL=https://api.openai.com/v1/
# Доступ через AI Tunnel
AI_TUNNEL_API_KEY=sk-aitunnel-...
AI_TUNNEL_BASE_URL=https://api.aitunnel.ru/v1/
# Локальная модель (предполагаем OpenAI-совместимый эндпоинт)
AI_LOCAL_BASE_URL=http://localhost:11434/v1/
# (опционально) провайдер и модель по умолчанию для /ai/assist
AI_DEFAULT_PROVIDER=aiTunnel
AI_DEFAULT_MODEL=gpt-5-mini
Ключевые моменты:
- URL’ы не должны содержать завершающий
/— в коде они нормализуются, но лучше сразу задавать без лишних слэшей; - для
directиaiTunnelобязателен API‑ключ (AI_DIRECT_API_KEY/AI_TUNNEL_API_KEY); - для
localключ обычно не нужен; AI_DEFAULT_PROVIDERиAI_DEFAULT_MODELиспользуются как дефолтные значения, но могут быть переопределены на фронтенде.
На стороне builder-web дефолтная модель и провайдер задаются в хуке useAiAssist / AiAssistPanel и могут переопределяться через:
VITE_AI_PROVIDER=aiTunnel
VITE_AI_MODEL=gpt-5-mini
Таким образом, источник истины по модели — builder-web, а backend может лишь подставить дефолт или валидировать/ограничивать провайдеры.
9. Где искать ключевые части кода
| Компонент | Путь | Описание |
|---|---|---|
| Точка входа | apps/api/src/main.ts | NestFactory, FastifyAdapter, глобальные пайпы |
| Корневой модуль | apps/api/src/app.module.ts | Подключение доменных модулей и инфраструктуры |
| Модуль Projects | apps/api/src/modules/projects/* | CRUD операций над проектами |
| Модуль ProjectVersions | apps/api/src/modules/project-versions/* | Управление версиями проектов |
| Модуль DSL/Compile | apps/api/src/modules/dsl/* | Валидация и компиляция DSL |
| Модуль AI | apps/api/src/modules/ai/* | Эндпоинты /ai/assist, адаптер к ai-orchestrator |
| Prisma schema | apps/api/prisma/schema.prisma | Модели БД |
| Prisma migrations | apps/api/prisma/migrations/* | Миграции схемы БД |
| PrismaService/Module | apps/api/src/database/* | Интеграция Prisma с NestJS |
Пакет @lowcode/ai-orchestrator, с которым интегрируется API, находится в packages/ai-orchestrator/ и содержит:
assist/assist.ts— входную точку сценария AI‑ассистента;assist/buildPrompt.ts— логику формирования system/user‑сообщений для LLM;assist/parseResponse.ts— разбор JSON‑ответа модели вAiOperation[];assist/applyOperations.ts— применение AI‑операций кAppSchema.
10. Как расширять API безопасно
При добавлении новой функциональности рекомендуется придерживаться паттерна:
- Создать модуль:
modules/feature/feature.module.ts. - Создать сервис с бизнес‑логикой (без HTTP‑деталей).
- Создать контроллер с HTTP‑эндпоинтами.
- Описать DTO и включить их в валидацию.
- При необходимости добавить модели в
schema.prismaи миграции. - Написать unit‑/integration‑тесты.
- Зарегистрировать новый модуль в
AppModule.
Для AI‑сценариев дополнительно:
- описать контракт в
@lowcode/shared-types(DTO, типы операций); - реализовать glue‑код в
@lowcode/ai-orchestrator(промпт, парсер, применение операций); - в
@lowcode/apiограничиться тонким адаптером (AiService+callModel).
Так архитектура остаётся модульной, предсказуемой и масштабируемой, а AI‑логика — изолированной и переиспользуемой между backend и другими сервисами. // Обновить прямую ссылку на скачивание POST /attachments/:id/refresh-url → Attachment