import { Hono } from "hono"; import { type Context, type Next } from "hono"; const app = new Hono(); // --- In-Memory Store for Rate Limits --- // This Map will store when each user/key last accessed a rate-limited endpoint. // Key: string (e.g., 'ip_address' or 'user_id_endpoint') // Value: number (timestamp of last access in milliseconds) const rateLimitStore = new Map(); // --- Configuration --- const FIFTEEN_MINUTES_MS = 5 * 60 * 1000; // 15 minutes in milliseconds // --- Rate Limiting Middleware --- export const simpleRateLimit = async (c: Context, next: Next) => { // 1. Define a unique key for the rate limit // For simplicity, we'll use a placeholder for user identification. // In a real app: // - If unauthenticated: Use c.req.header('x-forwarded-for') or c.req.ip (if configured/available) // - If authenticated: Get user ID from c.req.user or similar after authentication middleware const userIdentifier = c.req.header("x-forwarded-for") || "anonymous_user"; // Basic IP-like identifier // You can also make the key specific to the route to have different limits per route const routeKey = `${userIdentifier}:${c.req.path}`; const now = Date.now(); const lastAccessTime = rateLimitStore.get(routeKey); if (lastAccessTime) { const timeElapsed = now - lastAccessTime; if (timeElapsed < FIFTEEN_MINUTES_MS) { // Limit exceeded const timeRemainingMs = FIFTEEN_MINUTES_MS - timeElapsed; const timeRemainingSeconds = Math.ceil(timeRemainingMs / 1000); c.status(429); // HTTP 429: Too Many Requests return c.json({ error: "Too Many Requests", message: `Please wait ${timeRemainingSeconds} seconds before trying again.`, retryAfter: timeRemainingSeconds, // Standard header for rate limiting clients }); } } // If no previous access, or the 15 minutes have passed, allow the request // and update the last access time. rateLimitStore.set(routeKey, now); // Continue to the next middleware or route handler await next(); }; // --- Apply the Middleware to Specific Routes --- app.get("/", (c) => { return c.text("Welcome! This is a public endpoint."); }); // This endpoint will be rate-limited app.get("/privileged", simpleRateLimit, (c) => { return c.text("You successfully accessed the privileged endpoint!"); }); // Another rate-limited endpoint app.post("/submit-data", simpleRateLimit, async (c) => { // In a real app, you'd process form data or JSON here return c.text("Data submitted successfully (rate-limited)."); }); // Example of an endpoint that is NOT rate-limited app.get("/health", (c) => { return c.text("Server is healthy!"); }); export default app;