UNPKG

mnglab

Version:

Dead simple REST micro-framework with modular routing, API key, rate limiting, full HTTP response handling, dev tools, JSON DB, file helpers, endpoint stats, and professional developer experience.

129 lines (113 loc) 3.87 kB
const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); const { Low, JSONFile } = require('lowdb'); const path = require('path'); const fs = require('fs'); const chalk = require('chalk'); const app = express(); app.use(cors()); app.use(helmet()); app.use(express.json()); // Rate limiter const limiter = rateLimit({ windowMs: 60_000, max: 100 }); app.use(limiter); // Load API Keys const apiKeyPath = path.join(__dirname, 'apikey.json'); let API_KEYS = []; if (fs.existsSync(apiKeyPath)) { API_KEYS = JSON.parse(fs.readFileSync(apiKeyPath)); } function apiKeyGuard(req, res, next) { if (!API_KEYS.length) return next(); const key = req.headers['x-api-key']; if (!API_KEYS.includes(key)) { return res.status(401).json({ success: false, message: 'Invalid API key' }); } next(); } app.use(apiKeyGuard); // In-memory route counter let routeCount = 0; function register(method, path, opts, handler) { if (typeof opts === 'function') { handler = opts; opts = {}; } const validation = Array.isArray(opts?.body) ? opts.body : null; app[method](path, async (req, res, next) => { if (validation) { const missing = validation.filter((f) => req.body?.[f] === undefined); if (missing.length) { return res.status(400).json({ success: false, message: `Missing fields: ${missing.join(', ')}` }); } } try { await handler(req, res, next); } catch (err) { res.status(500).json({ success: false, message: 'Internal Server Error', error: err.message }); } }); routeCount++; } // Route helpers const route = { get: (path, opts, fn) => register('get', path, opts, fn), post: (path, opts, fn) => register('post', path, opts, fn), put: (path, opts, fn) => register('put', path, opts, fn), delete: (path, opts, fn) => register('delete', path, opts, fn) }; // Load DB const dbFile = path.join(process.cwd(), 'db.json'); if (!fs.existsSync(dbFile)) fs.writeFileSync(dbFile, JSON.stringify({})); const adapter = new JSONFile(dbFile); const db = new Low(adapter); // Utility Tools const tools = { log: (...args) => console.log(chalk.green('[LOG]'), ...args), error: (...args) => console.log(chalk.red('[ERR]'), ...args), readFile: (file) => fs.readFileSync(path.join(__dirname, file), 'utf-8'), writeFile: (file, content) => fs.writeFileSync(path.join(__dirname, file), content), appendFile: (file, content) => fs.appendFileSync(path.join(__dirname, file), content), deleteFile: (file) => fs.unlinkSync(path.join(__dirname, file)), timestamp: () => new Date().toISOString(), jsonResponse: (res, success, message, data = null) => { res.json({ success, message, data }); }, countRoutes: () => routeCount, }; // Dynamic route loader const routesDir = path.join(__dirname, 'routes'); if (fs.existsSync(routesDir)) { const files = fs.readdirSync(routesDir).filter(f => f.endsWith('.js')); files.forEach(file => { const registerRoute = require(path.join(routesDir, file)); registerRoute(route, db, tools); }); } // Home route route.get('/', (req, res) => { tools.jsonResponse(res, true, 'MNGBotz API Ready', { routes: tools.countRoutes(), timestamp: tools.timestamp() }); }); // 404 Middleware app.use((req, res) => { res.status(404).json({ success: false, message: 'Endpoint not found' }); }); // Error Middleware app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ success: false, message: 'Something went wrong', error: err.message }); }); // Start Server const PORT = process.env.PORT || 3000; (async () => { await db.read(); app.listen(PORT, () => { console.log(chalk.blue(`✅ Server running on port ${PORT}`)); console.log(chalk.magenta(`📡 ${routeCount} routes registered.`)); }); })();