feat: add case-level AI summary endpoint

POST /api/cases/:caseId/summarize combines extracted text from all of
a case's documents and runs the existing AI analysis prompt, storing
the result as a case-level analysis (document_id: null). Powers the
"Özet Hazırla" button on the case detail screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mstfyldz
2026-08-09 16:14:41 +03:00
co-authored by Claude Sonnet 5
parent 5ddf6ce0cf
commit 9f5c5c1e45
3 changed files with 52 additions and 4 deletions
+46
View File
@@ -1,6 +1,7 @@
import { Response } from 'express';
import { supabase } from '../lib/supabase';
import { AuthenticatedRequest } from '../middleware/auth';
import { runDocumentAnalysis } from './document.controller';
export const createCase = async (req: AuthenticatedRequest, res: Response) => {
try {
@@ -26,6 +27,51 @@ export const createCase = async (req: AuthenticatedRequest, res: Response) => {
}
};
// Dava dosyasındaki TÜM belgelerin çıkarılmış metnini birleştirip tek bir AI
// özeti üretir (Dosya Ayrıntı ekranındaki "Özet Hazırla" butonu). uploadDocument'ın
// aksine tek bir belgeye değil, dosyanın o anki tüm belgelerine bakıyor.
export const summarizeCase = async (req: AuthenticatedRequest, res: Response) => {
try {
const { caseId } = req.params;
if (!caseId) {
return res.status(400).json({ error: 'Geçersiz caseId' });
}
const { data: docs, error: docsError } = await supabase
.from('documents')
.select('filename, extracted_text')
.eq('case_id', caseId)
.order('uploaded_at', { ascending: true });
if (docsError) throw docsError;
const withText = (docs || []).filter((d) => d.extracted_text && String(d.extracted_text).trim());
if (withText.length === 0) {
return res.status(400).json({ error: 'Bu dosyada metni çıkarılmış belge yok, özet hazırlanamıyor.' });
}
// Tüm belgeleri modele gönderiyoruz; runDocumentAnalysis toplamı ~15000 karaktere
// kırpıyor, o yüzden belge sayısına göre kabaca dengeli bir pay ayırıyoruz.
const perDocLimit = Math.max(1000, Math.floor(15000 / withText.length));
const combinedText = withText
.map((d) => `--- ${d.filename} ---\n${String(d.extracted_text).slice(0, perDocLimit)}`)
.join('\n\n');
const { summaryJson, aiModel } = await runDocumentAnalysis(combinedText);
const { data: analysisRecord, error: analysisError } = await supabase
.from('analyses')
.insert([{ case_id: caseId, document_id: null, summary_json: summaryJson, model_used: aiModel }])
.select()
.single();
if (analysisError) throw analysisError;
res.json({ analysis: analysisRecord });
} catch (error: any) {
console.error('Summarize Case Error:', error);
res.status(500).json({ error: error.message || 'Internal Server Error' });
}
};
export const deleteCase = async (req: AuthenticatedRequest, res: Response) => {
try {
const { caseId } = req.params;