import { createClient } from "@libsql/client/web"; import * as BunnySDK from "@bunny.net/edgescript-sdk"; import process from "node:process"; // --------------------------------------------------------------------------- // Database client // --------------------------------------------------------------------------- const db = createClient({ url: process.env.BUNNY_DATABASE_URL!, authToken: process.env.BUNNY_DATABASE_AUTH_TOKEN!, }); // --------------------------------------------------------------------------- // In-memory cache (per edge node, 60s TTL) // --------------------------------------------------------------------------- interface CacheEntry { blocked: boolean; expiresAt: number; } const cache = new Map(); const CACHE_TTL_MS = 60_000; const CACHE_MAX_SIZE = 50_000; function cacheGet(ip: string): boolean | null { const entry = cache.get(ip); if (!entry) return null; if (entry.expiresAt <= Date.now()) { cache.delete(ip); return null; } return entry.blocked; } function cacheSet(ip: string, blocked: boolean): void { if (cache.size >= CACHE_MAX_SIZE) { // Evict expired entries first const now = Date.now(); for (const [key, val] of cache) { if (val.expiresAt <= now) cache.delete(key); } // If still too large, clear entirely if (cache.size >= CACHE_MAX_SIZE) cache.clear(); } cache.set(ip, { blocked, expiresAt: Date.now() + CACHE_TTL_MS }); } // --------------------------------------------------------------------------- // Blocklist lookup // --------------------------------------------------------------------------- async function isBlocked(ip: string): Promise { const cached = cacheGet(ip); if (cached !== null) return cached; try { const result = await db.execute({ sql: "SELECT 1 FROM blocklist WHERE ip = ? AND (expires_at IS NULL OR expires_at > datetime('now'))", args: [ip], }); const blocked = result.rows.length > 0; cacheSet(ip, blocked); return blocked; } catch (err) { // Fail-open: on DB error, allow the request through console.error("Blocklist lookup failed, allowing request:", err); return false; } } // --------------------------------------------------------------------------- // Middleware // --------------------------------------------------------------------------- BunnySDK.net.http .servePullZone() .onOriginRequest(async (ctx) => { const ip = ctx.request.headers.get("X-Real-Ip"); if (!ip) { return ctx.request; } if (await isBlocked(ip)) { return new Response("Forbidden", { status: 403 }); } return ctx.request; });