feat: full site build — Project/Melody schema (Option A), admin CRUD, public sections, uploads, email+SMTP, internal analytics, legal pages, docs

This commit is contained in:
2026-08-05 20:53:40 +02:00
parent d87f3033c6
commit c26f41511f
122 changed files with 15068 additions and 41 deletions
+26
View File
@@ -0,0 +1,26 @@
type RateLimitEntry = { count: number; resetAt: number };
const entries = new Map<string, RateLimitEntry>();
const windowMs = 10 * 60 * 1000;
const maxRequests = 5;
export function consumeRateLimit(key: string, limit: number, durationMs: number) {
const now = Date.now();
const current = entries.get(key);
if (!current || current.resetAt <= now) {
entries.set(key, { count: 1, resetAt: now + durationMs });
return true;
}
if (current.count >= limit) return false;
current.count += 1;
return true;
}
export function consumeContactRateLimit(key: string) {
return consumeRateLimit(key, maxRequests, windowMs);
}
export function getClientIp(requestHeaders: Headers) {
const forwardedFor = requestHeaders.get("x-forwarded-for")?.split(",")[0]?.trim();
return forwardedFor || requestHeaders.get("x-real-ip")?.trim() || "unknown";
}