import { env } from "cloudflare:workers";
import { getRequestHeader } from "@tanstack/react-start/server";
import { HttpError } from "./errors";
import type { Viewer } from "~/lib/types";
const HTTP_TOO_MANY = 429;
type LimiterName = "AUTH_LIMITER" | "WRITE_LIMITER" | "HEAVY_LIMITER" | "GIT_LIMITER";
export function requestIp(request?: Request): string {
const header = request ? request.headers.get("cf-connecting-ip") : getRequestHeader("cf-connecting-ip");
return header ?? "unknown";
}
/**
* Local e2e runs only: RATE_LIMITS_DISABLED=true turns the limiters off, and only when PUBLIC_URL is a
* loopback address, so it can never take effect on a deployed forge.
*/
function rateLimitsDisabled(): boolean {
if (env.RATE_LIMITS_DISABLED !== "true") return false;
try {
const host = new URL(env.PUBLIC_URL).hostname;
return host === "localhost" || host === "127.0.0.1" || host === "[::1]";
} catch {
return false;
}
}
/** Uses the Workers Rate Limiting binding; throws 429 once the key exceeds its budget. */
export async function enforceRateLimit(limiter: LimiterName, key: string): Promise<void> {
if (rateLimitsDisabled()) return;
const { success } = await env[limiter].limit({ key });
if (!success) throw new HttpError(HTTP_TOO_MANY, "Rate limit exceeded; slow down and try again shortly");
}
/** Non-throwing variant for optional fan-out (e.g. webhooks): false once the key's budget is spent. */
export async function withinRateLimit(limiter: LimiterName, key: string): Promise<boolean> {
if (rateLimitsDisabled()) return true;
const { success } = await env[limiter].limit({ key });
return success;