import { NextResponse } from 'next/server'; import { getAdminAuth, getAdminDb } from '@/lib/firebase/admin'; import { createKnowledgeItem } from '@/lib/server/knowledge'; import type { KnowledgeSourceMeta } from '@/lib/types/knowledge'; // import { chunkDocument } from '@/lib/utils/document-chunker'; // Not needed - Extractor AI handles chunking import { getStorage } from 'firebase-admin/storage'; export const maxDuration = 60; export async function POST( request: Request, context: { params: Promise<{ projectId: string }> | { projectId: string } } ) { try { // Verify auth const authHeader = request.headers.get('Authorization'); if (!authHeader?.startsWith('Bearer ')) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const idToken = authHeader.split('Bearer ')[1]; const adminAuth = getAdminAuth(); let userId: string; try { const decodedToken = await adminAuth.verifyIdToken(idToken); userId = decodedToken.uid; } catch (error) { return NextResponse.json({ error: 'Invalid token' }, { status: 401 }); } // Handle async params in Next.js 16 const params = 'then' in context.params ? await context.params : context.params; const projectId = params.projectId; if (!projectId) { return NextResponse.json({ error: 'Missing projectId' }, { status: 400 }); } // Parse multipart form data const formData = await request.formData(); const file = formData.get('file') as File; if (!file) { return NextResponse.json({ error: 'No file provided' }, { status: 400 }); } const adminDb = getAdminDb(); const projectSnap = await adminDb.collection('projects').doc(projectId).get(); if (!projectSnap.exists) { return NextResponse.json({ error: 'Project not found' }, { status: 404 }); } console.log(`[upload-document] Uploading ${file.name}, size=${file.size}`); // Read file content const arrayBuffer = await file.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); const content = buffer.toString('utf-8'); // Upload original file to Firebase Storage const storage = getStorage(); const bucket = storage.bucket(); const storagePath = `projects/${projectId}/documents/${Date.now()}_${file.name}`; const fileRef = bucket.file(storagePath); await fileRef.save(buffer, { metadata: { contentType: file.type, metadata: { uploadedBy: userId, projectId, originalFilename: file.name, uploadedAt: new Date().toISOString(), }, }, }); // Make file publicly accessible (or use signed URLs if you want private) await fileRef.makePublic(); const publicUrl = `https://storage.googleapis.com/${bucket.name}/${storagePath}`; console.log(`[upload-document] File saved to Storage: ${publicUrl}`); // Store whole document as single knowledge_item (no chunking) // Extractor AI will collaboratively chunk important sections later const sourceMeta: KnowledgeSourceMeta = { origin: 'other', url: publicUrl, filename: file.name, createdAtOriginal: new Date().toISOString(), importance: 'primary', tags: ['document', 'uploaded', 'pending_extraction'], }; const knowledgeItem = await createKnowledgeItem({ projectId, sourceType: 'imported_document', title: file.name, content: content, sourceMeta, }); console.log(`[upload-document] Stored whole document as knowledge_item: ${knowledgeItem.id}`); const knowledgeItemIds = [knowledgeItem.id]; // Create a summary record in contextSources for UI display const contextSourcesRef = adminDb.collection('projects').doc(projectId).collection('contextSources'); await contextSourcesRef.add({ type: 'document', name: file.name, summary: `Document (${content.length} characters) - pending extraction`, url: publicUrl, connectedAt: new Date(), metadata: { totalChars: content.length, fileSize: file.size, mimeType: file.type, storagePath, knowledgeItemId: knowledgeItem.id, uploadedBy: userId, status: 'pending_extraction', }, }); return NextResponse.json({ success: true, filename: file.name, url: publicUrl, knowledgeItemId: knowledgeItem.id, status: 'stored', message: 'Document stored. Extractor AI will review and chunk important sections.', }); } catch (error) { console.error('[upload-document] Failed to upload document', error); return NextResponse.json( { error: 'Failed to upload document', details: error instanceof Error ? error.message : String(error), }, { status: 500 }, ); } }