Spaces:
Runtime error
Runtime error
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, () => {}); |