const fs = require('fs'); const path = require('path'); ['data', 'datasets'].forEach(dir => { const fullPath = path.join(__dirname, dir); if (!fs.existsSync(fullPath)) { fs.mkdirSync(fullPath, { recursive: true }); } }); const express = require('express'); const sqlite3 = require('sqlite3').verbose(); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const multer = require('multer'); const cors = require('cors'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); const archiver = require('archiver'); const { parse } = require('csv-parse'); const app = express(); const PORT = process.env.PORT || 3000; const JWT_SECRET = process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production'; const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'DataMaster2024!'; app.use(helmet()); app.use(cors()); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ extended: true, limit: '50mb' })); const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }); app.use(limiter); const storage = multer.diskStorage({ destination: (req, file, cb) => { const uploadPath = path.join(__dirname, 'datasets'); if (!fs.existsSync(uploadPath)) { fs.mkdirSync(uploadPath, { recursive: true }); } cb(null, uploadPath); }, filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}`); } }); const upload = multer({ storage: storage, limits: { fileSize: 100 * 1024 * 1024 } }); const userDb = new sqlite3.Database('./data/users.db'); const datasetDb = new sqlite3.Database('./data/datasets.db'); userDb.serialize(() => { userDb.run(`CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, role TEXT DEFAULT 'user', created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`); }); datasetDb.serialize(() => { datasetDb.run(`CREATE TABLE IF NOT EXISTS datasets ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, description TEXT, file_path TEXT, file_type TEXT, size INTEGER, created_by TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, download_count INTEGER DEFAULT 0 )`); }); const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Access token required' }); } jwt.verify(token, JWT_SECRET, (err, user) => { if (err) { return res.status(403).json({ error: 'Invalid token' }); } req.user = user; next(); }); }; app.get('/health', (req, res) => { res.json({ status: 'OK', timestamp: new Date().toISOString() }); }); app.post('/api/register', async (req, res) => { try { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: 'Username and password required' }); } const hashedPassword = await bcrypt.hash(password, 10); userDb.run( 'INSERT INTO users (username, password) VALUES (?, ?)', [username, hashedPassword], function(err) { if (err) { if (err.message.includes('UNIQUE constraint failed')) { return res.status(409).json({ error: 'Username already exists' }); } return res.status(500).json({ error: 'Database error' }); } const token = jwt.sign({ id: this.lastID, username }, JWT_SECRET, { expiresIn: '24h' }); res.status(201).json({ message: 'User created successfully', token, user: { id: this.lastID, username } }); } ); } catch (error) { res.status(500).json({ error: 'Server error' }); } }); app.post('/api/login', (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: 'Username and password required' }); } userDb.get( 'SELECT * FROM users WHERE username = ?', [username], async (err, user) => { if (err) { return res.status(500).json({ error: 'Database error' }); } if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); } const validPassword = await bcrypt.compare(password, user.password); if (!validPassword) { return res.status(401).json({ error: 'Invalid credentials' }); } const token = jwt.sign( { id: user.id, username: user.username, role: user.role }, JWT_SECRET, { expiresIn: '24h' } ); res.json({ message: 'Login successful', token, user: { id: user.id, username: user.username, role: user.role } }); } ); }); app.get('/api/datasets', authenticateToken, (req, res) => { datasetDb.all( 'SELECT id, name, description, file_type, size, created_by, created_at, download_count FROM datasets ORDER BY created_at DESC', (err, datasets) => { if (err) { return res.status(500).json({ error: 'Database error' }); } res.json(datasets); } ); }); app.post('/api/datasets/upload', authenticateToken, upload.single('file'), (req, res) => { if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }); } const { name, description } = req.body; if (!name) { return res.status(400).json({ error: 'Dataset name required' }); } datasetDb.run( 'INSERT INTO datasets (name, description, file_path, file_type, size, created_by) VALUES (?, ?, ?, ?, ?, ?)', [name, description || '', req.file.path, req.file.mimetype, req.file.size, req.user.username], function(err) { if (err) { if (err.message.includes('UNIQUE constraint failed')) { return res.status(409).json({ error: 'Dataset name already exists' }); } return res.status(500).json({ error: 'Database error' }); } res.status(201).json({ id: this.lastID, name, description, file_type: req.file.mimetype, size: req.file.size, message: 'Dataset uploaded successfully' }); } ); }); app.get('/api/datasets/:id/download', authenticateToken, (req, res) => { const datasetId = req.params.id; datasetDb.get( 'SELECT * FROM datasets WHERE id = ?', [datasetId], (err, dataset) => { if (err) { return res.status(500).json({ error: 'Database error' }); } if (!dataset) { return res.status(404).json({ error: 'Dataset not found' }); } if (!fs.existsSync(dataset.file_path)) { return res.status(404).json({ error: 'File not found on server' }); } datasetDb.run( 'UPDATE datasets SET download_count = download_count + 1 WHERE id = ?', [datasetId] ); res.download(dataset.file_path, dataset.name); } ); }); app.post('/api/datasets/create', authenticateToken, (req, res) => { const { name, description, data } = req.body; if (!name || !data) { return res.status(400).json({ error: 'Name and data required' }); } const fileName = `${Date.now()}-${name.replace(/[^a-zA-Z0-9]/g, '_')}.json`; const filePath = path.join(__dirname, 'datasets', fileName); fs.writeFile(filePath, JSON.stringify(data, null, 2), (err) => { if (err) { return res.status(500).json({ error: 'Failed to create file' }); } const fileSize = fs.statSync(filePath).size; datasetDb.run( 'INSERT INTO datasets (name, description, file_path, file_type, size, created_by) VALUES (?, ?, ?, ?, ?, ?)', [name, description || '', filePath, 'application/json', fileSize, req.user.username], function(err) { if (err) { fs.unlinkSync(filePath); if (err.message.includes('UNIQUE constraint failed')) { return res.status(409).json({ error: 'Dataset name already exists' }); } return res.status(500).json({ error: 'Database error' }); } res.status(201).json({ id: this.lastID, name, description, file_type: 'application/json', size: fileSize, message: 'Dataset created successfully' }); } ); }); }); app.delete('/api/datasets/:id', authenticateToken, (req, res) => { const datasetId = req.params.id; datasetDb.get( 'SELECT * FROM datasets WHERE id = ?', [datasetId], (err, dataset) => { if (err) { return res.status(500).json({ error: 'Database error' }); } if (!dataset) { return res.status(404).json({ error: 'Dataset not found' }); } if (dataset.created_by !== req.user.username && req.user.role !== 'admin') { return res.status(403).json({ error: 'Not authorized to delete this dataset' }); } if (fs.existsSync(dataset.file_path)) { fs.unlinkSync(dataset.file_path); } datasetDb.run( 'DELETE FROM datasets WHERE id = ?', [datasetId], (err) => { if (err) { return res.status(500).json({ error: 'Database error' }); } res.json({ message: 'Dataset deleted successfully' }); } ); } ); }); app.get('/api/datasets/:id', authenticateToken, (req, res) => { const datasetId = req.params.id; datasetDb.get( 'SELECT * FROM datasets WHERE id = ?', [datasetId], (err, dataset) => { if (err) { return res.status(500).json({ error: 'Database error' }); } if (!dataset) { return res.status(404).json({ error: 'Dataset not found' }); } res.json(dataset); } ); }); app.post('/api/datasets/bulk-download', authenticateToken, (req, res) => { const { datasetIds } = req.body; if (!datasetIds || !Array.isArray(datasetIds)) { return res.status(400).json({ error: 'Dataset IDs array required' }); } const placeholders = datasetIds.map(() => '?').join(','); datasetDb.all( `SELECT * FROM datasets WHERE id IN (${placeholders})`, datasetIds, (err, datasets) => { if (err) { return res.status(500).json({ error: 'Database error' }); } if (datasets.length === 0) { return res.status(404).json({ error: 'No datasets found' }); } const archive = archiver('zip', { zlib: { level: 9 } }); res.attachment('datasets.zip'); archive.pipe(res); datasets.forEach(dataset => { if (fs.existsSync(dataset.file_path)) { archive.file(dataset.file_path, { name: dataset.name }); } }); archive.finalize(); } ); }); app.use((err, req, res, next) => { res.status(500).json({ error: 'Something went wrong!' }); }); app.listen(PORT, () => {});