RelayServer / server.js
Reality123b's picture
Update server.js
1b0fc70 verified
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, () => {});