Initial commit: UOMysticmoon backend (Express + MariaDB + JWT)
- Layered API (router -> controller -> model -> db), serverlinkr pattern - Public / auth / admin route groups; posts, wiki, settings, users, activity models - JWT httpOnly-cookie auth (Secure auto-detected: LAN HTTP + Pangolin HTTPS) - Site LIVE/MAINTENANCE mode with admin preview bypass - Dual file+console logging (info/warn/error/debug) + HTTP access logs - Docker Compose (app + MariaDB), schema.sql + seed, .env.example - Verified end-to-end against MariaDB (27/27 smoke checks) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
35
server/src/middleware/rateLimit.js
Normal file
35
server/src/middleware/rateLimit.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const rateLimit = require('express-rate-limit')
|
||||
|
||||
const log = require('../utils/logger')('ratelimit')
|
||||
|
||||
function makeLimiter({ windowMs, max, label, message }) {
|
||||
return rateLimit({
|
||||
windowMs,
|
||||
max,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: { message },
|
||||
handler: (req, res, next, options) => {
|
||||
log.warn(`${label} rate limit exceeded`, { ip: req.ip, path: req.originalUrl })
|
||||
res.status(options.statusCode).json(options.message)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Brute-force protection on login.
|
||||
const loginLimiter = makeLimiter({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 10,
|
||||
label: 'login',
|
||||
message: 'Too many login attempts. Please try again later.',
|
||||
})
|
||||
|
||||
// Throttle the public contact form.
|
||||
const contactLimiter = makeLimiter({
|
||||
windowMs: 60 * 60 * 1000,
|
||||
max: 5,
|
||||
label: 'contact',
|
||||
message: 'Too many messages sent. Please try again later.',
|
||||
})
|
||||
|
||||
module.exports = { loginLimiter, contactLimiter }
|
||||
Reference in New Issue
Block a user