feat: add case creation endpoint and auto-title from first message

POST /api/cases lets the frontend actually create a new dava dosyası
(no working "Yeni Dosya" flow existed before). chatMessage now
generates a short title from a case's first message (like Claude/
ChatGPT auto-titling) and renames the case, returned as `newTitle`.
This commit is contained in:
mstfyldz
2026-08-09 10:34:55 +03:00
parent 7b354d4f96
commit 0e40aaef96
3 changed files with 75 additions and 2 deletions
+53 -1
View File
@@ -2,6 +2,40 @@ import { Response } from 'express';
import { supabase } from '../lib/supabase';
import { AuthenticatedRequest } from '../middleware/auth';
// Bir dosyanın ilk mesajından kısa bir başlık üretir (Claude/ChatGPT'nin yaptığı gibi).
// Başarısız olursa null döner — çağıran taraf bu durumda "Yeni Sohbet" başlığını korur,
// asıl sohbet cevabını etkilemez.
async function generateCaseTitle(firstMessage: string): Promise<string | null> {
try {
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: 'Kullanıcının hukuki sorusunu en fazla 5 kelimelik, kısa ve açıklayıcı bir Türkçe başlığa çevir. Sadece başlığı yaz; tırnak işareti, noktalama veya başka açıklama ekleme.'
},
{ role: 'user', content: firstMessage.slice(0, 2000) },
],
think: false,
stream: false,
}),
});
if (!response.ok) return null;
const data = await response.json();
const raw = (data.message?.content || '').toString().trim().replace(/^["'“”]+|["'“”]+$/g, '');
if (!raw) return null;
return raw.slice(0, 80);
} catch (err) {
console.error('Generate Case Title Error:', err);
return null;
}
}
export const deleteChatHistory = async (req: AuthenticatedRequest, res: Response) => {
try {
const { caseId } = req.params;
@@ -139,7 +173,25 @@ export const chatMessage = async (req: AuthenticatedRequest, res: Response) => {
.select().single();
if (aiMsgError) throw aiMsgError;
res.json({ message: aiMessage });
// Bu dosyanın ilk mesajıysa (kullanıcının bu isteklen önce hiç mesajı yoktu),
// ilk mesajdan kısa bir başlık üretip dosyayı yeniden adlandır.
let newTitle: string | null = null;
const isFirstMessage = !pastMessages || pastMessages.length <= 1;
if (isFirstMessage) {
newTitle = await generateCaseTitle(content);
if (newTitle) {
const { error: titleError } = await supabase
.from('cases')
.update({ title: newTitle })
.eq('id', caseId);
if (titleError) {
console.error('Case Title Update Error:', titleError);
newTitle = null;
}
}
}
res.json({ message: aiMessage, newTitle });
} catch (error: any) {
console.error('Chat Error:', error);
res.status(500).json({ error: error.message || 'Internal Server Error' });