Reality123b commited on
Commit
945d04a
·
verified ·
1 Parent(s): 7fc235e

Create server.js

Browse files
Files changed (1) hide show
  1. server.js +412 -0
server.js ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express');
2
+ const sqlite3 = require('sqlite3').verbose();
3
+ const bcrypt = require('bcryptjs');
4
+ const jwt = require('jsonwebtoken');
5
+ const multer = require('multer');
6
+ const cors = require('cors');
7
+ const helmet = require('helmet');
8
+ const rateLimit = require('express-rate-limit');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const archiver = require('archiver');
12
+ const { parse } = require('csv-parse');
13
+
14
+ const app = express();
15
+ const PORT = process.env.PORT || 3000;
16
+ const JWT_SECRET = process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production';
17
+ const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'DataMaster2024!';
18
+
19
+ // Middleware
20
+ app.use(helmet());
21
+ app.use(cors());
22
+ app.use(express.json({ limit: '50mb' }));
23
+ app.use(express.urlencoded({ extended: true, limit: '50mb' }));
24
+
25
+ // Rate limiting
26
+ const limiter = rateLimit({
27
+ windowMs: 15 * 60 * 1000, // 15 minutes
28
+ max: 100 // limit each IP to 100 requests per windowMs
29
+ });
30
+ app.use(limiter);
31
+
32
+ // File upload configuration
33
+ const storage = multer.diskStorage({
34
+ destination: (req, file, cb) => {
35
+ const uploadPath = path.join(__dirname, 'datasets');
36
+ if (!fs.existsSync(uploadPath)) {
37
+ fs.mkdirSync(uploadPath, { recursive: true });
38
+ }
39
+ cb(null, uploadPath);
40
+ },
41
+ filename: (req, file, cb) => {
42
+ cb(null, `${Date.now()}-${file.originalname}`);
43
+ }
44
+ });
45
+
46
+ const upload = multer({
47
+ storage: storage,
48
+ limits: { fileSize: 100 * 1024 * 1024 } // 100MB limit
49
+ });
50
+
51
+ // Initialize databases
52
+ const userDb = new sqlite3.Database('./data/users.db');
53
+ const datasetDb = new sqlite3.Database('./data/datasets.db');
54
+
55
+ // Create tables
56
+ userDb.serialize(() => {
57
+ userDb.run(`CREATE TABLE IF NOT EXISTS users (
58
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
59
+ username TEXT UNIQUE NOT NULL,
60
+ password TEXT NOT NULL,
61
+ role TEXT DEFAULT 'user',
62
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
63
+ )`);
64
+ });
65
+
66
+ datasetDb.serialize(() => {
67
+ datasetDb.run(`CREATE TABLE IF NOT EXISTS datasets (
68
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
69
+ name TEXT UNIQUE NOT NULL,
70
+ description TEXT,
71
+ file_path TEXT,
72
+ file_type TEXT,
73
+ size INTEGER,
74
+ created_by TEXT,
75
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
76
+ download_count INTEGER DEFAULT 0
77
+ )`);
78
+ });
79
+
80
+ // Authentication middleware
81
+ const authenticateToken = (req, res, next) => {
82
+ const authHeader = req.headers['authorization'];
83
+ const token = authHeader && authHeader.split(' ')[1];
84
+
85
+ if (!token) {
86
+ return res.status(401).json({ error: 'Access token required' });
87
+ }
88
+
89
+ jwt.verify(token, JWT_SECRET, (err, user) => {
90
+ if (err) {
91
+ return res.status(403).json({ error: 'Invalid token' });
92
+ }
93
+ req.user = user;
94
+ next();
95
+ });
96
+ };
97
+
98
+ // Routes
99
+
100
+ // Health check
101
+ app.get('/health', (req, res) => {
102
+ res.json({ status: 'OK', timestamp: new Date().toISOString() });
103
+ });
104
+
105
+ // User registration
106
+ app.post('/api/register', async (req, res) => {
107
+ try {
108
+ const { username, password } = req.body;
109
+
110
+ if (!username || !password) {
111
+ return res.status(400).json({ error: 'Username and password required' });
112
+ }
113
+
114
+ const hashedPassword = await bcrypt.hash(password, 10);
115
+
116
+ userDb.run(
117
+ 'INSERT INTO users (username, password) VALUES (?, ?)',
118
+ [username, hashedPassword],
119
+ function(err) {
120
+ if (err) {
121
+ if (err.message.includes('UNIQUE constraint failed')) {
122
+ return res.status(409).json({ error: 'Username already exists' });
123
+ }
124
+ return res.status(500).json({ error: 'Database error' });
125
+ }
126
+
127
+ const token = jwt.sign({ id: this.lastID, username }, JWT_SECRET, { expiresIn: '24h' });
128
+ res.status(201).json({
129
+ message: 'User created successfully',
130
+ token,
131
+ user: { id: this.lastID, username }
132
+ });
133
+ }
134
+ );
135
+ } catch (error) {
136
+ res.status(500).json({ error: 'Server error' });
137
+ }
138
+ });
139
+
140
+ // User login
141
+ app.post('/api/login', (req, res) => {
142
+ const { username, password } = req.body;
143
+
144
+ if (!username || !password) {
145
+ return res.status(400).json({ error: 'Username and password required' });
146
+ }
147
+
148
+ userDb.get(
149
+ 'SELECT * FROM users WHERE username = ?',
150
+ [username],
151
+ async (err, user) => {
152
+ if (err) {
153
+ return res.status(500).json({ error: 'Database error' });
154
+ }
155
+
156
+ if (!user) {
157
+ return res.status(401).json({ error: 'Invalid credentials' });
158
+ }
159
+
160
+ const validPassword = await bcrypt.compare(password, user.password);
161
+ if (!validPassword) {
162
+ return res.status(401).json({ error: 'Invalid credentials' });
163
+ }
164
+
165
+ const token = jwt.sign(
166
+ { id: user.id, username: user.username, role: user.role },
167
+ JWT_SECRET,
168
+ { expiresIn: '24h' }
169
+ );
170
+
171
+ res.json({
172
+ message: 'Login successful',
173
+ token,
174
+ user: { id: user.id, username: user.username, role: user.role }
175
+ });
176
+ }
177
+ );
178
+ });
179
+
180
+ // Get all datasets
181
+ app.get('/api/datasets', authenticateToken, (req, res) => {
182
+ datasetDb.all(
183
+ 'SELECT id, name, description, file_type, size, created_by, created_at, download_count FROM datasets ORDER BY created_at DESC',
184
+ (err, datasets) => {
185
+ if (err) {
186
+ return res.status(500).json({ error: 'Database error' });
187
+ }
188
+ res.json(datasets);
189
+ }
190
+ );
191
+ });
192
+
193
+ // Upload dataset
194
+ app.post('/api/datasets/upload', authenticateToken, upload.single('file'), (req, res) => {
195
+ if (!req.file) {
196
+ return res.status(400).json({ error: 'No file uploaded' });
197
+ }
198
+
199
+ const { name, description } = req.body;
200
+ if (!name) {
201
+ return res.status(400).json({ error: 'Dataset name required' });
202
+ }
203
+
204
+ datasetDb.run(
205
+ 'INSERT INTO datasets (name, description, file_path, file_type, size, created_by) VALUES (?, ?, ?, ?, ?, ?)',
206
+ [name, description || '', req.file.path, req.file.mimetype, req.file.size, req.user.username],
207
+ function(err) {
208
+ if (err) {
209
+ if (err.message.includes('UNIQUE constraint failed')) {
210
+ return res.status(409).json({ error: 'Dataset name already exists' });
211
+ }
212
+ return res.status(500).json({ error: 'Database error' });
213
+ }
214
+
215
+ res.status(201).json({
216
+ id: this.lastID,
217
+ name,
218
+ description,
219
+ file_type: req.file.mimetype,
220
+ size: req.file.size,
221
+ message: 'Dataset uploaded successfully'
222
+ });
223
+ }
224
+ );
225
+ });
226
+
227
+ // Download dataset
228
+ app.get('/api/datasets/:id/download', authenticateToken, (req, res) => {
229
+ const datasetId = req.params.id;
230
+
231
+ datasetDb.get(
232
+ 'SELECT * FROM datasets WHERE id = ?',
233
+ [datasetId],
234
+ (err, dataset) => {
235
+ if (err) {
236
+ return res.status(500).json({ error: 'Database error' });
237
+ }
238
+
239
+ if (!dataset) {
240
+ return res.status(404).json({ error: 'Dataset not found' });
241
+ }
242
+
243
+ if (!fs.existsSync(dataset.file_path)) {
244
+ return res.status(404).json({ error: 'File not found on server' });
245
+ }
246
+
247
+ // Update download count
248
+ datasetDb.run(
249
+ 'UPDATE datasets SET download_count = download_count + 1 WHERE id = ?',
250
+ [datasetId]
251
+ );
252
+
253
+ res.download(dataset.file_path, dataset.name);
254
+ }
255
+ );
256
+ });
257
+
258
+ // Create custom dataset from JSON
259
+ app.post('/api/datasets/create', authenticateToken, (req, res) => {
260
+ const { name, description, data } = req.body;
261
+
262
+ if (!name || !data) {
263
+ return res.status(400).json({ error: 'Name and data required' });
264
+ }
265
+
266
+ const fileName = `${Date.now()}-${name.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
267
+ const filePath = path.join(__dirname, 'datasets', fileName);
268
+
269
+ fs.writeFile(filePath, JSON.stringify(data, null, 2), (err) => {
270
+ if (err) {
271
+ return res.status(500).json({ error: 'Failed to create file' });
272
+ }
273
+
274
+ const fileSize = fs.statSync(filePath).size;
275
+
276
+ datasetDb.run(
277
+ 'INSERT INTO datasets (name, description, file_path, file_type, size, created_by) VALUES (?, ?, ?, ?, ?, ?)',
278
+ [name, description || '', filePath, 'application/json', fileSize, req.user.username],
279
+ function(err) {
280
+ if (err) {
281
+ fs.unlinkSync(filePath); // Clean up file if database insert fails
282
+ if (err.message.includes('UNIQUE constraint failed')) {
283
+ return res.status(409).json({ error: 'Dataset name already exists' });
284
+ }
285
+ return res.status(500).json({ error: 'Database error' });
286
+ }
287
+
288
+ res.status(201).json({
289
+ id: this.lastID,
290
+ name,
291
+ description,
292
+ file_type: 'application/json',
293
+ size: fileSize,
294
+ message: 'Dataset created successfully'
295
+ });
296
+ }
297
+ );
298
+ });
299
+ });
300
+
301
+ // Delete dataset
302
+ app.delete('/api/datasets/:id', authenticateToken, (req, res) => {
303
+ const datasetId = req.params.id;
304
+
305
+ datasetDb.get(
306
+ 'SELECT * FROM datasets WHERE id = ?',
307
+ [datasetId],
308
+ (err, dataset) => {
309
+ if (err) {
310
+ return res.status(500).json({ error: 'Database error' });
311
+ }
312
+
313
+ if (!dataset) {
314
+ return res.status(404).json({ error: 'Dataset not found' });
315
+ }
316
+
317
+ // Check if user owns the dataset or is admin
318
+ if (dataset.created_by !== req.user.username && req.user.role !== 'admin') {
319
+ return res.status(403).json({ error: 'Not authorized to delete this dataset' });
320
+ }
321
+
322
+ // Delete file
323
+ if (fs.existsSync(dataset.file_path)) {
324
+ fs.unlinkSync(dataset.file_path);
325
+ }
326
+
327
+ // Delete from database
328
+ datasetDb.run(
329
+ 'DELETE FROM datasets WHERE id = ?',
330
+ [datasetId],
331
+ (err) => {
332
+ if (err) {
333
+ return res.status(500).json({ error: 'Database error' });
334
+ }
335
+ res.json({ message: 'Dataset deleted successfully' });
336
+ }
337
+ );
338
+ }
339
+ );
340
+ });
341
+
342
+ // Get dataset info
343
+ app.get('/api/datasets/:id', authenticateToken, (req, res) => {
344
+ const datasetId = req.params.id;
345
+
346
+ datasetDb.get(
347
+ 'SELECT * FROM datasets WHERE id = ?',
348
+ [datasetId],
349
+ (err, dataset) => {
350
+ if (err) {
351
+ return res.status(500).json({ error: 'Database error' });
352
+ }
353
+
354
+ if (!dataset) {
355
+ return res.status(404).json({ error: 'Dataset not found' });
356
+ }
357
+
358
+ res.json(dataset);
359
+ }
360
+ );
361
+ });
362
+
363
+ // Bulk download (zip multiple datasets)
364
+ app.post('/api/datasets/bulk-download', authenticateToken, (req, res) => {
365
+ const { datasetIds } = req.body;
366
+
367
+ if (!datasetIds || !Array.isArray(datasetIds)) {
368
+ return res.status(400).json({ error: 'Dataset IDs array required' });
369
+ }
370
+
371
+ const placeholders = datasetIds.map(() => '?').join(',');
372
+
373
+ datasetDb.all(
374
+ `SELECT * FROM datasets WHERE id IN (${placeholders})`,
375
+ datasetIds,
376
+ (err, datasets) => {
377
+ if (err) {
378
+ return res.status(500).json({ error: 'Database error' });
379
+ }
380
+
381
+ if (datasets.length === 0) {
382
+ return res.status(404).json({ error: 'No datasets found' });
383
+ }
384
+
385
+ const archive = archiver('zip', { zlib: { level: 9 } });
386
+
387
+ res.attachment('datasets.zip');
388
+ archive.pipe(res);
389
+
390
+ datasets.forEach(dataset => {
391
+ if (fs.existsSync(dataset.file_path)) {
392
+ archive.file(dataset.file_path, { name: dataset.name });
393
+ }
394
+ });
395
+
396
+ archive.finalize();
397
+ }
398
+ );
399
+ });
400
+
401
+ // Error handling middleware
402
+ app.use((err, req, res, next) => {
403
+ console.error(err.stack);
404
+ res.status(500).json({ error: 'Something went wrong!' });
405
+ });
406
+
407
+ // Start server
408
+ app.listen(PORT, () => {
409
+ console.log(`Server running on port ${PORT}`);
410
+ console.log(`Admin password: ${ADMIN_PASSWORD}`);
411
+ console.log(`JWT Secret: ${JWT_SECRET}`);
412
+ });