Free-DB-Host / server.js
NathMen12's picture
Update server.js
cb71b43 verified
Raw
History Blame Contribute Delete
4.02 kB
// 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`));