globalThis.process ??= {};
globalThis.process.env ??= {};
import { n as defineMiddleware, t as sequence } from "./chunks/sequence_DQuqZSiB.mjs";
import { C as parseMaintenance, g as getSetting, m as SETTINGS_KEYS } from "./chunks/crypto_CY2zBtG3.mjs";
import { t as enforceAdminRateLimit } from "./chunks/rate-limit_CABs9jHB.mjs";
import { n as getFullDb } from "./chunks/db_BQ9NAt8x.mjs";
import { a as isSiteConfigured, n as bootGateDecision } from "./chunks/install_Bh1p1P7H.mjs";
import { i as buildServiceTokenResolver, r as buildAuthEngine } from "./chunks/admin-engine_Cqzo5vQB.mjs";
import { s as resolveSession } from "./chunks/auth_B0DWM0G6.mjs";
import { env } from "cloudflare:workers";
//#region src/lib/boot-gate.ts
var configuredMemo = false;
async function runBootGate(ctx, deps) {
	let configured = configuredMemo;
	if (!configured) try {
		if (isSiteConfigured(await deps.loadSiteConfigured())) {
			configuredMemo = true;
			configured = true;
		}
	} catch {
		configured = true;
	}
	const redirect = bootGateDecision(ctx.url.pathname, configured);
	if (redirect) return Response.redirect(new URL(redirect, ctx.url), 302);
	return null;
}
//#endregion
//#region ../../packages/util/dist/maintenance.js
/** The operator floor — always part of the effective allow-set, never removable by config. */
var OPERATOR_ROLES = ["admin", "owner"];
var DEFAULT_RETRY_AFTER_SEC = 3600;
function maintenanceGuard(cfg, principalRoles) {
	if (!cfg.enabled) return { blocked: false };
	const allowRoles = Array.isArray(cfg.allowRoles) ? cfg.allowRoles : [];
	const allow = /* @__PURE__ */ new Set([...allowRoles, ...OPERATOR_ROLES]);
	if ((Array.isArray(principalRoles) ? principalRoles : []).some((r) => allow.has(r))) return { blocked: false };
	const retryAfter = cfg.retryAfterSec ?? DEFAULT_RETRY_AFTER_SEC;
	return {
		blocked: true,
		status: 503,
		headers: { "Retry-After": String(retryAfter) },
		message: cfg.message
	};
}
//#endregion
//#region src/lib/maintenance.ts
/** The themed 503 page the middleware rewrites to. Exempt from the gate (else infinite loop). */
var MAINTENANCE_RENDER_PATH = "/maintenance";
var EXEMPT_PREFIXES = ["/admin", "/api"];
function isMaintenanceExempt(pathname) {
	if (pathname === "/maintenance") return true;
	return EXEMPT_PREFIXES.some((p) => pathname === p || pathname.startsWith(`${p}/`));
}
/**
* Pure gate decision: exempt paths pass; otherwise defer to the module's `maintenanceGuard`
* (operator floor + array-coercion live there). Surfaces retryAfterSec for the 503 header.
*/
function evaluateMaintenance(cfg, roles, pathname) {
	if (!cfg.enabled) return { blocked: false };
	if (isMaintenanceExempt(pathname)) return { blocked: false };
	const verdict = maintenanceGuard(cfg, roles);
	if (!verdict.blocked) return { blocked: false };
	const retry = verdict.headers?.["Retry-After"];
	return {
		blocked: true,
		retryAfterSec: retry ? Number(retry) : void 0
	};
}
/**
* The full gate: returns a themed 503 Response when the request must be blocked, else null
* (caller proceeds with next()). Exempt paths short-circuit BEFORE loadConfig (no DB read);
* roles resolve only when enabled. On block, rewrites to the themed render target and re-wraps
* it as 503 + Retry-After + noindex, copying any Set-Cookie the page set. (Session-refresh cookies
* rotated onto context.cookies during resolveRoles are applied to this Response by Astro.)
*/
async function runMaintenanceGate(ctx, deps) {
	const pathname = ctx.url.pathname;
	if (isMaintenanceExempt(pathname)) return null;
	const cfg = await deps.loadConfig();
	if (!cfg.enabled) return null;
	const decision = evaluateMaintenance(cfg, await deps.resolveRoles(), pathname);
	if (!decision.blocked) return null;
	const rendered = await ctx.rewrite(MAINTENANCE_RENDER_PATH);
	const headers = new Headers();
	const ct = rendered.headers.get("content-type");
	if (ct) headers.set("content-type", ct);
	const setCookie = rendered.headers.get("set-cookie");
	if (setCookie) headers.set("set-cookie", setCookie);
	headers.set("Retry-After", String(decision.retryAfterSec ?? 3600));
	headers.set("X-Robots-Tag", "noindex");
	return new Response(rendered.body, {
		status: 503,
		headers
	});
}
//#endregion
//#region src/middleware.ts
var onRequest$1 = defineMiddleware(async (context, next) => {
	if (context.url.pathname === "/cron-arm") return next();
	const cfEnv = env;
	if (cfEnv) {
		const limited = await enforceAdminRateLimit(cfEnv, context.request);
		if (limited) return limited;
	}
	if (cfEnv?.DATABASE_URL) {
		const bootBlocked = await runBootGate({ url: context.url }, { loadSiteConfigured: () => getSetting(getFullDb(cfEnv).db, SETTINGS_KEYS.siteConfigured) });
		if (bootBlocked) return bootBlocked;
	}
	if (cfEnv?.DATABASE_URL) {
		const blocked = await runMaintenanceGate({
			url: context.url,
			rewrite: (path) => context.rewrite(path)
		}, {
			loadConfig: async () => {
				try {
					return parseMaintenance(await getSetting(getFullDb(cfEnv).db, SETTINGS_KEYS.maintenance));
				} catch {
					return { enabled: false };
				}
			},
			resolveRoles: async () => {
				try {
					const { principal } = await resolveSession(context.request, buildAuthEngine(cfEnv), context.cookies, buildServiceTokenResolver(cfEnv));
					return principal.roles ?? [];
				} catch {
					return null;
				}
			}
		});
		if (blocked) return blocked;
	}
	return next();
});
//#endregion
//#region \0virtual:astro:middleware
var onRequest = sequence(onRequest$1);
//#endregion
export { onRequest };
