Reality123b/RelayServer
0
1const fs = require('fs');2const path = require('path');3['data', 'datasets'].forEach(dir => {4 const fullPath = path.join(__dirname, dir);5 if (!fs.existsSync(fullPath)) {6 fs.mkdirSync(fullPath, { recursive: true });7 }8});9const express = require('express');10const sqlite3 = require('sqlite3').verbose();11const bcrypt = require('bcryptjs');12const jwt = require('jsonwebtoken');13const multer = require('multer');14const cors = require('cors');15const helmet = require('helmet');16const rateLimit = require('express-rate-limit');17const archiver = require('archiver');18const { parse } = require('csv-parse');19const app = express();20const PORT = process.env.PORT || 3000;21const JWT_SECRET = process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production';22const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'DataMaster2024!';23app.use(helmet());24app.use(cors());25app.use(express.json({ limit: '50mb' }));26app.use(express.urlencoded({ extended: true, limit: '50mb' }));27const limiter = rateLimit({28 windowMs: 15 * 60 * 1000,29 max: 10030});31app.use(limiter);32const storage = multer.diskStorage({33 destination: (req, file, cb) => {34 const uploadPath = path.join(__dirname, 'datasets');35 if (!fs.existsSync(uploadPath)) {36 fs.mkdirSync(uploadPath, { recursive: true });37 }38 cb(null, uploadPath);39 },40 filename: (req, file, cb) => {41 cb(null, `${Date.now()}-${file.originalname}`);42 }43});44const upload = multer({45 storage: storage,46 limits: { fileSize: 100 * 1024 * 1024 }47});48const userDb = new sqlite3.Database('./data/users.db');49const datasetDb = new sqlite3.Database('./data/datasets.db');50userDb.serialize(() => {51 userDb.run(`CREATE TABLE IF NOT EXISTS users (52 id INTEGER PRIMARY KEY AUTOINCREMENT,53 username TEXT UNIQUE NOT NULL,54 password TEXT NOT NULL,55 role TEXT DEFAULT 'user',56 created_at DATETIME DEFAULT CURRENT_TIMESTAMP57 )`);58});59datasetDb.serialize(() => {60 datasetDb.run(`CREATE TABLE IF NOT EXISTS datasets (61 id INTEGER PRIMARY KEY AUTOINCREMENT,62 name TEXT UNIQUE NOT NULL,63 description TEXT,64 file_path TEXT,65 file_type TEXT,66 size INTEGER,67 created_by TEXT,68 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,69 download_count INTEGER DEFAULT 070 )`);71});72const authenticateToken = (req, res, next) => {73 const authHeader = req.headers['authorization'];74 const token = authHeader && authHeader.split(' ')[1];75 if (!token) {76 return res.status(401).json({ error: 'Access token required' });77 }78 jwt.verify(token, JWT_SECRET, (err, user) => {79 if (err) {80 return res.status(403).json({ error: 'Invalid token' });81 }82 req.user = user;83 next();84 });85};86app.get('/health', (req, res) => {87 res.json({ status: 'OK', timestamp: new Date().toISOString() });88});89app.post('/api/register', async (req, res) => {90 try {91 const { username, password } = req.body;92 if (!username || !password) {93 return res.status(400).json({ error: 'Username and password required' });94 }95 const hashedPassword = await bcrypt.hash(password, 10);96 userDb.run(97 'INSERT INTO users (username, password) VALUES (?, ?)',98 [username, hashedPassword],99 function(err) {100 if (err) {101 if (err.message.includes('UNIQUE constraint failed')) {102 return res.status(409).json({ error: 'Username already exists' });103 }104 return res.status(500).json({ error: 'Database error' });105 }106 const token = jwt.sign({ id: this.lastID, username }, JWT_SECRET, { expiresIn: '24h' });107 res.status(201).json({108 message: 'User created successfully',109 token,110 user: { id: this.lastID, username }111 });112 }113 );114 } catch (error) {115 res.status(500).json({ error: 'Server error' });116 }117});118app.post('/api/login', (req, res) => {119 const { username, password } = req.body;120 if (!username || !password) {121 return res.status(400).json({ error: 'Username and password required' });122 }123 userDb.get(124 'SELECT * FROM users WHERE username = ?',125 [username],126 async (err, user) => {127 if (err) {128 return res.status(500).json({ error: 'Database error' });129 }130 if (!user) {131 return res.status(401).json({ error: 'Invalid credentials' });132 }133 const validPassword = await bcrypt.compare(password, user.password);134 if (!validPassword) {135 return res.status(401).json({ error: 'Invalid credentials' });136 }137 const token = jwt.sign(138 { id: user.id, username: user.username, role: user.role },139 JWT_SECRET,140 { expiresIn: '24h' }141 );142 res.json({143 message: 'Login successful',144 token,145 user: { id: user.id, username: user.username, role: user.role }146 });147 }148 );149});150app.get('/api/datasets', authenticateToken, (req, res) => {151 datasetDb.all(152 'SELECT id, name, description, file_type, size, created_by, created_at, download_count FROM datasets ORDER BY created_at DESC',153 (err, datasets) => {154 if (err) {155 return res.status(500).json({ error: 'Database error' });156 }157 res.json(datasets);158 }159 );160});161app.post('/api/datasets/upload', authenticateToken, upload.single('file'), (req, res) => {162 if (!req.file) {163 return res.status(400).json({ error: 'No file uploaded' });164 }165 const { name, description } = req.body;166 if (!name) {167 return res.status(400).json({ error: 'Dataset name required' });168 }169 datasetDb.run(170 'INSERT INTO datasets (name, description, file_path, file_type, size, created_by) VALUES (?, ?, ?, ?, ?, ?)',171 [name, description || '', req.file.path, req.file.mimetype, req.file.size, req.user.username],172 function(err) {173 if (err) {174 if (err.message.includes('UNIQUE constraint failed')) {175 return res.status(409).json({ error: 'Dataset name already exists' });176 }177 return res.status(500).json({ error: 'Database error' });178 }179 res.status(201).json({180 id: this.lastID,181 name,182 description,183 file_type: req.file.mimetype,184 size: req.file.size,185 message: 'Dataset uploaded successfully'186 });187 }188 );189});190app.get('/api/datasets/:id/download', authenticateToken, (req, res) => {191 const datasetId = req.params.id;192 datasetDb.get(193 'SELECT * FROM datasets WHERE id = ?',194 [datasetId],195 (err, dataset) => {196 if (err) {197 return res.status(500).json({ error: 'Database error' });198 }199 if (!dataset) {200 return res.status(404).json({ error: 'Dataset not found' });201 }202 if (!fs.existsSync(dataset.file_path)) {203 return res.status(404).json({ error: 'File not found on server' });204 }205 datasetDb.run(206 'UPDATE datasets SET download_count = download_count + 1 WHERE id = ?',207 [datasetId]208 );209 res.download(dataset.file_path, dataset.name);210 }211 );212});213app.post('/api/datasets/create', authenticateToken, (req, res) => {214 const { name, description, data } = req.body;215 if (!name || !data) {216 return res.status(400).json({ error: 'Name and data required' });217 }218 const fileName = `${Date.now()}-${name.replace(/[^a-zA-Z0-9]/g, '_')}.json`;219 const filePath = path.join(__dirname, 'datasets', fileName);220 fs.writeFile(filePath, JSON.stringify(data, null, 2), (err) => {221 if (err) {222 return res.status(500).json({ error: 'Failed to create file' });223 }224 const fileSize = fs.statSync(filePath).size;225 datasetDb.run(226 'INSERT INTO datasets (name, description, file_path, file_type, size, created_by) VALUES (?, ?, ?, ?, ?, ?)',227 [name, description || '', filePath, 'application/json', fileSize, req.user.username],228 function(err) {229 if (err) {230 fs.unlinkSync(filePath);231 if (err.message.includes('UNIQUE constraint failed')) {232 return res.status(409).json({ error: 'Dataset name already exists' });233 }234 return res.status(500).json({ error: 'Database error' });235 }236 res.status(201).json({237 id: this.lastID,238 name,239 description,240 file_type: 'application/json',241 size: fileSize,242 message: 'Dataset created successfully'243 });244 }245 );246 });247});248app.delete('/api/datasets/:id', authenticateToken, (req, res) => {249 const datasetId = req.params.id;250 datasetDb.get(251 'SELECT * FROM datasets WHERE id = ?',252 [datasetId],253 (err, dataset) => {254 if (err) {255 return res.status(500).json({ error: 'Database error' });256 }257 if (!dataset) {258 return res.status(404).json({ error: 'Dataset not found' });259 }260 if (dataset.created_by !== req.user.username && req.user.role !== 'admin') {261 return res.status(403).json({ error: 'Not authorized to delete this dataset' });262 }263 if (fs.existsSync(dataset.file_path)) {264 fs.unlinkSync(dataset.file_path);265 }266 datasetDb.run(267 'DELETE FROM datasets WHERE id = ?',268 [datasetId],269 (err) => {270 if (err) {271 return res.status(500).json({ error: 'Database error' });272 }273 res.json({ message: 'Dataset deleted successfully' });274 }275 );276 }277 );278});279app.get('/api/datasets/:id', authenticateToken, (req, res) => {280 const datasetId = req.params.id;281 datasetDb.get(282 'SELECT * FROM datasets WHERE id = ?',283 [datasetId],284 (err, dataset) => {285 if (err) {286 return res.status(500).json({ error: 'Database error' });287 }288 if (!dataset) {289 return res.status(404).json({ error: 'Dataset not found' });290 }291 res.json(dataset);292 }293 );294});295app.post('/api/datasets/bulk-download', authenticateToken, (req, res) => {296 const { datasetIds } = req.body;297 if (!datasetIds || !Array.isArray(datasetIds)) {298 return res.status(400).json({ error: 'Dataset IDs array required' });299 }300 const placeholders = datasetIds.map(() => '?').join(',');301 datasetDb.all(302 `SELECT * FROM datasets WHERE id IN (${placeholders})`,303 datasetIds,304 (err, datasets) => {305 if (err) {306 return res.status(500).json({ error: 'Database error' });307 }308 if (datasets.length === 0) {309 return res.status(404).json({ error: 'No datasets found' });310 }311 const archive = archiver('zip', { zlib: { level: 9 } });312 res.attachment('datasets.zip');313 archive.pipe(res);314 datasets.forEach(dataset => {315 if (fs.existsSync(dataset.file_path)) {316 archive.file(dataset.file_path, { name: dataset.name });317 }318 });319 archive.finalize();320 }321 );322});323app.use((err, req, res, next) => {324 res.status(500).json({ error: 'Something went wrong!' });325});326app.listen(PORT, () => {});