LearnSpace: full-stack educational whiteboard platform
Node.js/Express backend + vanilla JS frontend. Features: real-time collaborative whiteboard (SSE), multi-page support, LaTeX formulas, shapes/connectors, coordinate systems, number lines, compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with rotation & resize controls, minimap navigation overlay, auto-measurements, multi-page thumbnails sidebar, PNG export, page templates. Student/teacher workflows: classes, assignments, library, dashboard. Mobile responsive. SQLite (better-sqlite3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
/* Simple in-memory rate limiter — no external dependency needed */
|
||||
|
||||
// Clean stale entries every 5 minutes across all stores
|
||||
const _allStores = new Set();
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const store of _allStores) {
|
||||
for (const [key, entry] of store) {
|
||||
if (now > entry.resetAt) store.delete(key);
|
||||
}
|
||||
}
|
||||
}, 5 * 60 * 1000).unref();
|
||||
|
||||
module.exports = function rateLimit({ windowMs = 60_000, max = 10, message = 'Too many requests, please try again later' } = {}) {
|
||||
// Skip rate limiting in test environment
|
||||
if (process.env.NODE_ENV === 'test') return (_req, _res, next) => next();
|
||||
|
||||
// Each rateLimit() call gets its own isolated store — counters don't bleed between limiters
|
||||
const store = new Map();
|
||||
_allStores.add(store);
|
||||
|
||||
return (req, res, next) => {
|
||||
const key = req.ip || req.socket?.remoteAddress || 'unknown';
|
||||
const now = Date.now();
|
||||
let entry = store.get(key);
|
||||
|
||||
if (!entry || now > entry.resetAt) {
|
||||
entry = { count: 0, resetAt: now + windowMs };
|
||||
}
|
||||
entry.count++;
|
||||
store.set(key, entry);
|
||||
|
||||
if (entry.count > max) {
|
||||
const retryAfter = Math.ceil((entry.resetAt - now) / 1000);
|
||||
res.set('Retry-After', retryAfter);
|
||||
return res.status(429).json({ error: message });
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user