auth-yes/server/middleware.ts
google-labs-jules[bot] 88821b80af feat: Phase 3 Monolith decomposition of server/main.ts
- Extracts Auth, Registration, and Passkey routes into `server/routes/auth.ts`.
- Extracts all Admin API endpoints into `server/routes/admin.ts`.
- Extracts RPC Connect setup and mTLS listener into `server/rpc.ts`.
- Extracts global rate limiters and IP helpers into `server/middleware.ts`.
- Reduces `server/main.ts` purely to an entrypoint mounting orchestrator.
- Ensures all existing tests and quality gates pass with zero regressions.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-26 03:23:20 +00:00

63 lines
1.9 KiB
TypeScript

import type { Context } from "jsr:@hono/hono@4";
import { rateLimitWrapper } from "./ratelimit.ts";
import { getAuthenticatedUser } from "./auth-session.ts";
// Middleware helper to get IP address
export function getClientIp(c: Context): string {
// Check for X-Real-IP first
const realIp = c.req.header("x-real-ip");
if (realIp) {
return realIp.trim();
}
// Fallback to X-Forwarded-For, bounded to prevent memory exhaustion
let forwardedFor = c.req.header("x-forwarded-for");
if (forwardedFor) {
// Truncate to a max of 256 characters
if (forwardedFor.length > 256) {
forwardedFor = forwardedFor.substring(0, 256);
}
const parts = forwardedFor.split(",");
// Extract the last untrusted hop (right-most IP)
return parts[parts.length - 1].trim();
}
// Fallback (might not be accurate behind proxy without X-Forwarded-For)
return "unknown-ip";
}
export const publicRateLimiter = async (
c: Context,
next: () => Promise<void>,
) => {
const ip = getClientIp(c);
const key = `ratelimit:public:${ip}`;
const allowed = await rateLimitWrapper.checkRateLimit(key, 10, 60000);
if (!allowed) {
return c.json({ error: "Too Many Requests" }, 429);
}
await next();
};
export const adminRateLimiter = async (
c: Context,
next: () => Promise<void>,
) => {
const auth = await getAuthenticatedUser(c);
if (!auth) {
return c.json({ error: "Missing or invalid session" }, 401);
}
const key = `ratelimit:admin:${auth.userId}`;
const allowed = await rateLimitWrapper.checkRateLimit(key, 60, 60000);
if (!allowed) {
return c.json({ error: "Too Many Requests" }, 429);
}
// Stash userId in context so downstream routes can use it
c.set("userId", auth.userId);
await next();
};
// Also apply requireAdmin logic on the admin route by chaining it or exporting it.
// We'll export requireAdmin here or use it directly in the admin routes.