import express from 'express'; import { verifyFirebaseToken } from '../middleware/firebaseAuth'; import { documentController } from '../controllers/documentController'; import { unifiedDocumentProcessor } from '../services/unifiedDocumentProcessor'; import { logger } from '../utils/logger'; import { config } from '../config/env'; import { DocumentModel } from '../models/DocumentModel'; import { validateUUID, addCorrelationId } from '../middleware/validation'; // Extend Express Request to include user property declare global { namespace Express { interface Request { user?: import('firebase-admin').auth.DecodedIdToken; } } } const router = express.Router(); // Apply authentication and correlation ID to all routes router.use(verifyFirebaseToken); router.use(addCorrelationId); // Add logging middleware for document routes router.use((req, res, next) => { logger.debug('Document route accessed', { method: req.method, path: req.path }); next(); }); // Analytics endpoints (MUST come before ANY routes with :id parameters) router.get('/analytics', async (req, res) => { try { const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } const days = parseInt(req.query['days'] as string) || 30; // Return empty analytics data (agentic RAG analytics not fully implemented) const analytics = { totalSessions: 0, successfulSessions: 0, failedSessions: 0, avgQualityScore: 0.8, avgCompleteness: 0.9, avgProcessingTime: 0, sessionsOverTime: [], agentPerformance: [], qualityTrends: [] }; return res.json({ ...analytics, correlationId: req.correlationId || undefined }); } catch (error) { logger.error('Failed to get analytics data', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Failed to get analytics data', correlationId: req.correlationId || undefined }); } }); router.get('/processing-stats', async (req, res) => { try { const stats = await unifiedDocumentProcessor.getProcessingStats(); return res.json({ ...stats, correlationId: req.correlationId || undefined }); } catch (error) { logger.error('Failed to get processing stats', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Failed to get processing stats', correlationId: req.correlationId || undefined }); } }); // Firebase Storage direct upload routes router.post('/upload-url', documentController.getUploadUrl); router.post('/:id/confirm-upload', validateUUID('id'), documentController.confirmUpload); // Document listing route router.get('/', documentController.getDocuments); // Document-specific routes with UUID validation router.get('/:id', validateUUID('id'), documentController.getDocument); router.get('/:id/progress', validateUUID('id'), documentController.getDocumentProgress); router.delete('/:id', validateUUID('id'), documentController.deleteDocument); // CIM Review data endpoints router.post('/:id/review', validateUUID('id'), async (req, res) => { try { const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } const { id } = req.params; const reviewData = req.body; if (!reviewData) { return res.status(400).json({ error: 'Review data is required', correlationId: req.correlationId }); } // Check if document exists and user has access const document = await DocumentModel.findById(id); if (!document) { return res.status(404).json({ error: 'Document not found', correlationId: req.correlationId }); } if (document.user_id !== userId) { return res.status(403).json({ error: 'Access denied', correlationId: req.correlationId }); } // Update the document with new analysis data await DocumentModel.updateAnalysisResults(id, reviewData); logger.info('CIM Review data saved successfully', { documentId: id, userId, correlationId: req.correlationId }); return res.json({ success: true, message: 'CIM Review data saved successfully', correlationId: req.correlationId || undefined }); } catch (error) { logger.error('Failed to save CIM Review data', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Failed to save CIM Review data', correlationId: req.correlationId || undefined }); } }); router.get('/:id/review', validateUUID('id'), async (req, res) => { try { const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } const { id } = req.params; // Check if document exists and user has access const document = await DocumentModel.findById(id); if (!document) { return res.status(404).json({ error: 'Document not found', correlationId: req.correlationId }); } if (document.user_id !== userId) { return res.status(403).json({ error: 'Access denied', correlationId: req.correlationId }); } return res.json({ success: true, reviewData: document.analysis_data || {}, correlationId: req.correlationId || undefined }); } catch (error) { logger.error('Failed to get CIM Review data', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Failed to get CIM Review data', correlationId: req.correlationId || undefined }); } }); // Download endpoint (keeping this) router.get('/:id/download', validateUUID('id'), async (req, res) => { try { const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } const { id } = req.params; if (!id) { return res.status(400).json({ error: 'Document ID is required', correlationId: req.correlationId }); } const document = await DocumentModel.findById(id); if (!document) { return res.status(404).json({ error: 'Document not found', correlationId: req.correlationId }); } if (document.user_id !== userId) { return res.status(403).json({ error: 'Access denied', correlationId: req.correlationId }); } // Check if document has analysis data if (!document.analysis_data) { return res.status(404).json({ error: 'No analysis data available for download', correlationId: req.correlationId }); } // Generate PDF on-demand try { const { pdfGenerationService } = await import('../services/pdfGenerationService'); const pdfBuffer = await pdfGenerationService.generateCIMReviewPDF(document.analysis_data); if (!pdfBuffer) { return res.status(500).json({ error: 'Failed to generate PDF', correlationId: req.correlationId }); } // Generate standardized filename const companyName = document.analysis_data?.dealOverview?.targetCompanyName || 'Unknown'; const date = new Date().toISOString().split('T')[0].replace(/-/g, ''); // YYYYMMDD const sanitizedCompanyName = companyName .replace(/[^a-zA-Z0-9\s]/g, '') // Remove special characters .replace(/\s+/g, '_') // Replace spaces with underscores .toUpperCase(); const filename = `${date}_${sanitizedCompanyName}_CIM_Review.pdf`; res.setHeader('Content-Type', 'application/pdf'); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.setHeader('x-correlation-id', req.correlationId || 'unknown'); return res.send(pdfBuffer); } catch (pdfError) { logger.error('PDF generation failed', { error: pdfError, correlationId: req.correlationId }); return res.status(500).json({ error: 'PDF generation failed', correlationId: req.correlationId || undefined }); } } catch (error) { logger.error('Download document failed', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Download failed', correlationId: req.correlationId || undefined }); } }); // CSV Export endpoint router.get('/:id/export-csv', validateUUID('id'), async (req, res) => { try { const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } const { id } = req.params; if (!id) { return res.status(400).json({ error: 'Document ID is required', correlationId: req.correlationId }); } const document = await DocumentModel.findById(id); if (!document) { return res.status(404).json({ error: 'Document not found', correlationId: req.correlationId }); } if (document.user_id !== userId) { return res.status(403).json({ error: 'Access denied', correlationId: req.correlationId }); } // Check if document has analysis data if (!document.analysis_data) { return res.status(404).json({ error: 'No analysis data available for CSV export', correlationId: req.correlationId }); } // Generate CSV try { const { default: CSVExportService } = await import('../services/csvExportService'); const companyName = document.analysis_data?.dealOverview?.targetCompanyName || 'Unknown'; const csvContent = CSVExportService.generateCIMReviewCSV(document.analysis_data, companyName); const filename = CSVExportService.generateCSVFilename(companyName); res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.setHeader('x-correlation-id', req.correlationId || 'unknown'); return res.send(csvContent); } catch (csvError) { logger.error('CSV generation failed', { error: csvError, correlationId: req.correlationId }); return res.status(500).json({ error: 'CSV generation failed', correlationId: req.correlationId || undefined }); } } catch (error) { logger.error('CSV export failed', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'CSV export failed', correlationId: req.correlationId || undefined }); } }); // ONLY OPTIMIZED AGENTIC RAG PROCESSING ROUTE - All other processing routes disabled router.post('/:id/process-optimized-agentic-rag', validateUUID('id'), async (req, res) => { try { const { id } = req.params; if (!id) { return res.status(400).json({ error: 'Document ID is required', correlationId: req.correlationId }); } const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } // Check if agentic RAG is enabled if (!config.agenticRag.enabled) { return res.status(400).json({ error: 'Agentic RAG is not enabled', correlationId: req.correlationId }); } // Get document text const documentText = await documentController.getDocumentText(id); const result = await unifiedDocumentProcessor.processDocument( id, userId, documentText, { strategy: 'simple_full_document' } ); return res.json({ success: result.success, processingStrategy: result.processingStrategy, processingTime: result.processingTime, apiCalls: result.apiCalls, summary: result.summary, analysisData: result.analysisData, error: result.error, correlationId: req.correlationId || undefined }); } catch (error) { logger.error('Optimized Agentic RAG processing failed', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Optimized Agentic RAG processing failed', correlationId: req.correlationId || undefined }); } }); // Agentic RAG session routes (keeping these for monitoring) router.get('/:id/agentic-rag-sessions', validateUUID('id'), async (req, res) => { try { const { id } = req.params; if (!id) { return res.status(400).json({ error: 'Document ID is required', correlationId: req.correlationId }); } const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } // Return empty sessions array (agentic RAG sessions not fully implemented) return res.json({ sessions: [], correlationId: req.correlationId || undefined }); } catch (error) { logger.error('Failed to get agentic RAG sessions', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Failed to get agentic RAG sessions', correlationId: req.correlationId || undefined }); } }); router.get('/agentic-rag-sessions/:sessionId', validateUUID('sessionId'), async (req, res) => { try { const { sessionId } = req.params; if (!sessionId) { return res.status(400).json({ error: 'Session ID is required', correlationId: req.correlationId }); } const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } // Return 404 since agentic RAG sessions are not fully implemented return res.status(404).json({ error: 'Session not found', correlationId: req.correlationId }); } catch (error) { logger.error('Failed to get agentic RAG session details', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Failed to get agentic RAG session details', correlationId: req.correlationId || undefined }); } }); router.get('/:id/analytics', validateUUID('id'), async (req, res) => { try { const { id } = req.params; if (!id) { return res.status(400).json({ error: 'Document ID is required', correlationId: req.correlationId }); } const userId = req.user?.uid; if (!userId) { return res.status(401).json({ error: 'User not authenticated', correlationId: req.correlationId }); } // Return empty analytics data (agentic RAG analytics not fully implemented) const analytics = { documentId: id, totalSessions: 0, lastProcessed: null, avgQualityScore: 0.8, avgCompleteness: 0.9, processingHistory: [] }; return res.json({ ...analytics, correlationId: req.correlationId || undefined }); } catch (error) { logger.error('Failed to get document analytics', { error, correlationId: req.correlationId }); return res.status(500).json({ error: 'Failed to get document analytics', correlationId: req.correlationId || undefined }); } }); export default router;