From b030bdaa60f763af39cb497d5562ef26487cdb3d Mon Sep 17 00:00:00 2001 From: mstfyldz Date: Sun, 9 Aug 2026 01:52:53 +0300 Subject: [PATCH] fix: allow case-less ("general") chat without ownership check requireAuth treated every chat caseId as a real cases row and rejected "general" (the no-file chat sentinel) with 403. general chat now skips the ownership check and is answered without touching chat_messages, since that table's case_id is a NOT NULL FK to cases. --- src/controllers/chat.controller.ts | 34 ++++++++++++++++++++++++++++++ src/middleware/auth.ts | 4 +++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/controllers/chat.controller.ts b/src/controllers/chat.controller.ts index 74e86c9..319ad74 100644 --- a/src/controllers/chat.controller.ts +++ b/src/controllers/chat.controller.ts @@ -12,6 +12,40 @@ export const chatMessage = async (req: AuthenticatedRequest, res: Response) => { return res.status(400).json({ error: 'Missing caseId or content' }); } + // "general" = dosyasız/genel sohbet. Şemada chat_messages.case_id NOT NULL bir + // cases FK'si olduğundan bu moddaki mesajları veritabanına yazmıyoruz — sadece + // AI'ye iletip yanıtı doğrudan döndürüyoruz. + if (caseId === 'general') { + const generalSystemPrompt = 'Sen bir hukuk asistanısın. Belirli bir dava dosyasına bağlı olmayan genel hukuki soruları cevaplıyorsun. Emin olmadığın noktalarda bunu açıkça belirt, uydurma bilgi verme.'; + const aiModel = process.env.AI_MODEL || 'mizan-fixed'; + const runpodUrl = process.env.RUNPOD_API_BASE_URL || 'https://q190env94stwis-11434.proxy.runpod.net'; + + const response = await fetch(`${runpodUrl}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: aiModel, + messages: [ + { role: 'system', content: generalSystemPrompt }, + { role: 'user', content }, + ], + think: false, + stream: false, + }), + }); + + if (!response.ok) { + throw new Error(`AI Model request failed: ${response.statusText}`); + } + + const data = await response.json(); + const aiContent = data.message?.content || 'Yanıt alınamadı.'; + + return res.json({ + message: { id: `general-${Date.now()}`, case_id: 'general', role: 'assistant', content: aiContent }, + }); + } + const { data: userMessage, error: userMsgError } = await supabase .from('chat_messages') .insert([{ case_id: caseId, user_id: userId, role: 'user', content }]) diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index 2c592a2..264ff41 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -33,8 +33,10 @@ export const requireAuth = async (req: AuthenticatedRequest, res: Response, next req.user = user; // P0-3: Kaynak Sahipliği Doğrulama (Eğer istekte caseId veya case_id varsa) + // "general", dosyasız/genel sohbet için ayrılmış bir sentinel değer — gerçek bir + // case kaydı değil, bu yüzden sahiplik kontrolüne tabi tutulmaz. const caseId = req.params.caseId || req.body?.case_id; - if (caseId) { + if (caseId && caseId !== 'general') { const { data: caseRecord, error: caseError } = await supabase .from('cases') .select('id')