Spaces:
Paused
Paused
| // server.js | |
| const express = require('express'); | |
| const path = require('path'); | |
| const rateLimit = require('express-rate-limit'); | |
| const auth = require('./auth'); | |
| const app = express(); | |
| const PORT = process.env.PORT || 7860; | |
| // --- REQUEST LOGGER (MORGAN-LIKE) --- | |
| app.use((req, res, next) => { | |
| const start = Date.now(); | |
| const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress; | |
| res.on('finish', () => { | |
| const dur = Date.now() - start; | |
| const log = { ts: new Date().toISOString(), level: 'INFO', type: 'HTTP', method: req.method, url: req.originalUrl, status: res.statusCode, durationMs: dur, ip, ua: req.headers['user-agent']?.substring(0,50) }; | |
| // Colorize status for console readability | |
| const color = res.statusCode >= 500 ? '\x1b[31m' : res.statusCode >= 400 ? '\x1b[33m' : '\x1b[32m'; | |
| console.log(`${color}${log.method} ${log.url} ${log.status}\x1b[0m ${log.durationMs}ms | IP: ${log.ip}`); | |
| // Structured JSON for HF Logs parsing | |
| console.log(JSON.stringify(log)); | |
| }); | |
| next(); | |
| }); | |
| app.use(express.json({ limit: '10mb' })); | |
| app.use(express.static(path.join(__dirname, 'public'))); | |
| app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 300, message: 'Trop de requêtes', standardHeaders: true, legacyHeaders: false })); | |
| const authMiddleware = (req, res, next) => { | |
| const h = req.headers.authorization; | |
| if (!h?.startsWith('Bearer ')) return res.status(401).json({ error: 'Token manquant' }); | |
| const payload = auth.verifyToken(h.split(' ')[1]); | |
| if (!payload) return res.status(401).json({ error: 'Token invalide/expiré' }); | |
| req.user = payload; next(); | |
| }; | |
| // PUBLIC | |
| app.get('/api/captcha', (req, res) => res.json(auth.generateCaptcha())); | |
| app.get('/api/tos/version', (req, res) => res.json({ version: auth.TOS_VERSION })); | |
| app.post('/api/register', (req, res) => { try { res.json(auth.registerUser(req.body.email, req.body.password, req.body.captchaToken, req.body.captchaAnswer, req.body.acceptTos)); } catch (e) { res.status(400).json({ error: e.message }); } }); | |
| app.post('/api/login', (req, res) => { try { const r = auth.loginUser(req.body.email, req.body.password); if (r.tosRequired) return res.status(403).json({ tosRequired: true, tempUser: r.tempUser }); res.json(r); } catch (e) { res.status(401).json({ error: e.message }); } }); | |
| app.post('/api/tos/accept', (req, res) => { try { res.json(auth.acceptTos(req.body.userId)); } catch (e) { res.status(400).json({ error: e.message }); } }); | |
| // PROTECTED | |
| app.get('/api/me', authMiddleware, (req, res) => { const s = auth.getUserStats(req.user.uid); const g = auth.getGlobalUsed(); res.json({ user: req.user, stats: s, globalUsed: g, globalLimit: auth.GLOBAL_QUOTA_BYTES }); }); | |
| app.get('/api/databases', authMiddleware, (req, res) => res.json(auth.listDatabases(req.user.uid))); | |
| app.post('/api/databases', authMiddleware, (req, res) => { try { res.status(201).json(auth.createDatabase(req.user.uid, req.body.name, req.body.captchaToken, req.body.captchaAnswer)); } catch (e) { res.status(400).json({ error: e.message }); } }); | |
| app.delete('/api/databases/:id', authMiddleware, (req, res) => { auth.deleteDatabase(req.user.uid, req.params.id); res.json({ success: true }); }); | |
| app.post('/api/databases/:id/query', authMiddleware, (req, res) => { const { sql, params } = req.body; if (!sql) return res.status(400).json({ error: 'SQL requis' }); if (/^\s*(PRAGMA|ATTACH|DETACH|VACUUM|BACKUP)/i.test(sql)) return res.status(403).json({ error: 'Commande interdite' }); res.json(auth.executeSql(req.user.uid, req.params.id, sql, params || [])); }); | |
| app.get('/api/databases/:id/download', authMiddleware, (req, res) => { const fp = auth.getDatabaseFilePath(req.user.uid, req.params.id); if (!fp || !require('fs').existsSync(fp)) return res.status(404).send('Not found'); res.download(fp, `db_${req.params.id}.sqlite`); }); | |
| app.get('*', (req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html'))); | |
| app.listen(PORT, () => console.log(`\x1b[32m🚀 Server ready on port ${PORT} | Data: /data\x1b[0m`)); |