[bug][ai-chat][server] POST /ai-chat/bound-chat роняет 500 (Postgres 22P02): клиент шлёт slugId страницы в UUID-колонку — привязка чата к документу молча ломается #312
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Симптом
При открытии страницы
POST /api/ai-chat/bound-chatпадает с 500:"i82qXsivsx"— это slugId страницы (10-символьный nanoid), а не UUID.Причина (цепочка)
Клиент отправляет в
bound-chatslugId под именемpageId, сервер кладёт его в запрос по UUID-колонке:apps/client/src/features/ai-chat/hooks/use-open-ai-chat.ts:30— берёт slug из URL и, вводя в заблуждение именем, зовёт егоpageId:extractPageSlugId(apps/client/src/lib/utils.tsx:14-23) для не-UUID возвращает хвост после последнего дефиса — короткий slugId.apps/client/src/features/ai-chat/services/ai-chat-service.ts:49-53—getBoundChat(pageId)→POST /ai-chat/bound-chat { pageId: <slugId> }.apps/server/src/core/ai-chat/dto/ai-chat.dto.ts:41-44—BoundChatDto.pageIdвалидируется только как@IsString(), без UUID-проверки, поэтому slugId проходит.apps/server/src/core/ai-chat/ai-chat.controller.ts:79-91—boundChat→aiChatRepo.findLatestByPage(user.id, workspace.id, dto.pageId).apps/server/src/database/repos/ai-chat/ai-chat.repo.ts:91—.where('pageId', '=', pageId)по UUID-колонкеpageId→ Postgres22P02→ 500.Impact
use-open-ai-chat.ts:46-48,catch { resolved = null }), поэтому при открытии страницы вместо «последнего чата этой страницы» открывается новый. Пользователь тихо теряет привязку чата к документу.Severity: medium (не крашит UX, но фича сломана и логи засоряются 500-ками).
Фикс
Резолвить входящий id в реальный UUID страницы на сервере, т.к.
PageRepo.findById(apps/server/src/database/repos/page/page.repo.ts:65, веткаisValidUUIDна стр. 117-121) уже принимает и UUID, и slugId:Дополнительно:
pageId→slugIdвuse-open-ai-chat.ts/getBoundChat, чтобы убрать семантическую путаницу (в поле лежит slugId).@IsUUID()в DTO — недостаточно: это лишь превратит 500 в 400, но привязку чата всё равно сломает (клиент шлёт slugId). Нужен резолв slug→id.Acceptance / DoD
POST /ai-chat/bound-chatсо slug-URL возвращает200 { chatId }и корректно находит последний чат страницы; 500/22P02в логах нет.bound-chat(и на отсутствие/чужой воркспейс →{ chatId: null }, без утечки).Окружение
POST /api/ai-chat/bound-chat