-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
179 lines (162 loc) · 5.4 KB
/
server.js
File metadata and controls
179 lines (162 loc) · 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import express from 'express';
import multer from 'multer';
import { DocuRAG } from 'docurag';
import cors from 'cors';
import path from 'path';
import { fileURLToPath } from 'url';
// Get current directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Initialize express app with security defaults
const app = express();
// Middleware setup with security headers
app.use(cors());
app.use(express.json()); // Add this BEFORE routes
app.use(express.static(path.join(__dirname, 'public')));
// Initialize DocuRAG instance with production configuration
// Ensure these URLs are configured via environment variables in production
const docuRAG = new DocuRAG({
qdrantUrl: 'http://localhost:6333',
llmUrl: 'http://localhost:11434'
});
// Configure multer for secure PDF upload
// Limit file size to 10MB and only allow PDFs
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 10 * 1024 * 1024, // 10MB limit
files: 1 // Only allow one file per request
},
fileFilter: (req, file, cb) => {
if (file.mimetype === 'application/pdf') {
cb(null, true);
} else {
cb(new Error('Only PDF files are allowed'));
}
}
});
// Routes
app.post('/upload', upload.single('pdf'), async (req, res) => {
try {
// Validate file presence
if (!req.file) {
return res.status(400).json({
success: false,
error: 'No PDF file uploaded'
});
}
// Extract file data and process PDF
const { buffer, originalname } = req.file;
const result = await docuRAG.processPDFBuffer(buffer, originalname);
// Validate processing result
if (!result) {
throw new Error('PDF processing failed');
}
// Return success response
res.json({
success: true,
message: 'PDF processed successfully'
});
} catch (error) {
// Log error for monitoring but send sanitized message to client
console.error('PDF processing error:', error);
res.status(500).json({
success: false,
error: 'Failed to process PDF file'
});
}
});
// Chat endpoint with Server-Sent Events (SSE)
// Handles real-time chat interactions with the processed PDF
app.post('/chat', async (req, res) => {
try {
// Validate request body
if (!req.body || typeof req.body !== 'object') {
return res.status(400).json({
success: false,
error: 'Invalid request body'
});
}
const { message } = req.body;
// Validate message
if (!message || typeof message !== 'string') {
return res.status(400).json({
success: false,
error: 'Message is required and must be a string'
});
}
// Configure SSE headers
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Stream chat response with error handling
await docuRAG.chat(message, {
onData: (data) => {
try {
res.write(`data: ${JSON.stringify(data)}\n\n`);
} catch (error) {
console.error('Stream write error:', error);
res.write(`data: ${JSON.stringify({
success: false,
error: 'Error streaming response'
})}\n\n`);
res.end();
}
},
onEnd: () => {
try {
res.write('data: [DONE]\n\n');
res.end();
} catch (error) {
console.error('Stream end error:', error);
}
},
onError: (error) => {
console.error('Chat processing error:', error);
res.write(`data: ${JSON.stringify({
success: false,
error: 'Failed to process chat message'
})}\n\n`);
res.end();
}
});
} catch (error) {
console.error('Chat endpoint error:', error);
if (!res.headersSent) {
res.status(500).json({
success: false,
error: 'Failed to process chat message'
});
}
}
});
// Cleanup endpoint
// Handles cleanup of processed documents and associated resources
app.post('/cleanup', async (req, res) => {
try {
await docuRAG.cleanup();
res.json({
success: true,
message: 'Resources cleaned up successfully'
});
} catch (error) {
console.error('Cleanup error:', error);
res.status(500).json({
success: false,
error: 'Failed to cleanup resources'
});
}
});
// Start server with proper error handling
const PORT = process.env.PORT || 3000;
const server = app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});
// Handle server shutdown gracefully
process.on('SIGTERM', () => {
console.log('Received SIGTERM. Performing graceful shutdown...');
server.close(async () => {
await docuRAG.cleanup();
process.exit(0);
});
});