Skip to main content

Архитектура 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 и DTO CreateProjectDto / UpdateProjectDto.

5.2. Модуль ProjectVersions

Модуль версий (ProjectVersion):

  • хранит снимки состояния проекта во времени;
  • позволяет откатываться к предыдущим версиям;
  • обеспечивает совместимость с компилятором.

Примерный набор эндпоинтов:

GET    /projects/:id/versions
GET /project-versions/:versionId
POST /projects/:id/versions

Реализация:

  • модуль ProjectVersionsModule;
  • контроллер ProjectVersionsController;
  • сервис ProjectVersionsService;
  • сущность ProjectVersionEntity и DTO CreateProjectVersionDto.

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

  1. builder-web вызывает callAiAssist() и отправляет AiAssistRequest на /ai/assist.

  2. AiController принимает AiAssistDto и передаёт его в AiService.assist().

  3. AiService:

    • нормализует провайдера и модель;
    • собирает AiAssistRequest для @lowcode/ai-orchestrator;
    • вызывает функцию assist(input, callModel) из @lowcode/ai-orchestrator.
  4. assist() внутри @lowcode/ai-orchestrator:

    • строит промпт и AiModelRequest (см. buildPrompt.ts);
    • вызывает переданный коллбек callModel();
    • парсит ответ модели в список AiOperation (см. parseResponse.ts);
    • применяет операции к AppSchema (см. applyOperations.ts).
  5. 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 bucket
  • url — 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>
}

Критически важные фичи:

  1. Валидация 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.

  2. LinkNormalizer — валидация и нормализация внешних URL:

    class LinkNormalizer {
    normalize(url: string): string {
    // Google Drive: извлекает fileId и формирует прямую ссылку
    // Yandex Disk: запрашивает публичный API и берёт href
    }
    }
  3. Генерация 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;
    }
  4. Обновление прямых ссылок (refresh) — endpoint /attachments/:id/refresh-url:

    • для s3 — обновляет url (новый presigned URL);
    • для link — повторно нормализует sourceUrl (получает новый downloadUrl).

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 — тесты AttachmentsService
  • link-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.tsNestFactory, FastifyAdapter, глобальные пайпы
Корневой модульapps/api/src/app.module.tsПодключение доменных модулей и инфраструктуры
Модуль Projectsapps/api/src/modules/projects/*CRUD операций над проектами
Модуль ProjectVersionsapps/api/src/modules/project-versions/*Управление версиями проектов
Модуль DSL/Compileapps/api/src/modules/dsl/*Валидация и компиляция DSL
Модуль AIapps/api/src/modules/ai/*Эндпоинты /ai/assist, адаптер к ai-orchestrator
Prisma schemaapps/api/prisma/schema.prismaМодели БД
Prisma migrationsapps/api/prisma/migrations/*Миграции схемы БД
PrismaService/Moduleapps/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 безопасно

При добавлении новой функциональности рекомендуется придерживаться паттерна:

  1. Создать модуль: modules/feature/feature.module.ts.
  2. Создать сервис с бизнес‑логикой (без HTTP‑деталей).
  3. Создать контроллер с HTTP‑эндпоинтами.
  4. Описать DTO и включить их в валидацию.
  5. При необходимости добавить модели в schema.prisma и миграции.
  6. Написать unit‑/integration‑тесты.
  7. Зарегистрировать новый модуль в AppModule.

Для AI‑сценариев дополнительно:

  • описать контракт в @lowcode/shared-types (DTO, типы операций);
  • реализовать glue‑код в @lowcode/ai-orchestrator (промпт, парсер, применение операций);
  • в @lowcode/api ограничиться тонким адаптером (AiService + callModel).

Так архитектура остаётся модульной, предсказуемой и масштабируемой, а AI‑логика — изолированной и переиспользуемой между backend и другими сервисами. // Обновить прямую ссылку на скачивание POST /attachments/:id/refresh-url → Attachment