Skip to content
File

Blob: src/worker/api/config.ts

typescript55 lines
1import type { AppContext } from "@/worker/hono";
2import { HttpError } from "@/worker/http";
3 
4export type SocialProvider = "github" | "google";
5 
6interface ClientConfigResponse {
7 turnstileSiteKey: string;
8 issuer: string;
9 socialProviders: SocialProvider[];
10 operatorName: string;
11 operatorContactEmail: string;
12}
13 
14// /api/config gates the public Turnstile site key the React app needs to
15// render the widget. A missing TURNSTILE_SITE_KEY is a deployment error,
16// not a runtime feature toggle — the verifier already fails closed on a
17// missing TURNSTILE_SECRET_KEY (services/turnstile.ts), and surfacing 503
18// here lets the sign-in / invite-accept pages show an actionable error
19// instead of an indefinite "Loading verification..." state.
20//
21// Also advertises the OIDC issuer (so the landing-page metadata reflects
22// the actual deployment instead of hardcoding a host), the list of
23// configured social providers (so the UI only renders buttons that can
24// successfully complete the flow), and the operator identity rendered on
25// /privacy and /terms (so the same React build can serve different
26// deployments without hardcoding a brand name). Providers require BOTH
27// client id AND client secret to count as configured; a half-configured
28// provider would fail at the IdP round-trip.
29export const handleConfig = (c: AppContext): Response => {
30 const siteKey = c.env.TURNSTILE_SITE_KEY?.trim();
31 if (!siteKey) {
32 throw new HttpError(503, "turnstile_unavailable", "Human verification is not configured on this server.");
33 }
34 const operatorName = c.env.OPERATOR_NAME?.trim();
35 const operatorContactEmail = c.env.OPERATOR_CONTACT_EMAIL?.trim();
36 if (!operatorName || !operatorContactEmail) {
37 throw new HttpError(503, "operator_unconfigured", "Operator identity is not configured on this server.");
38 }
39 const socialProviders: SocialProvider[] = [];
40 if (c.env.GITHUB_OAUTH_CLIENT_ID?.trim() && c.env.GITHUB_OAUTH_CLIENT_SECRET?.trim()) {
41 socialProviders.push("github");
42 }
43 if (c.env.GOOGLE_OAUTH_CLIENT_ID?.trim() && c.env.GOOGLE_OAUTH_CLIENT_SECRET?.trim()) {
44 socialProviders.push("google");
45 }
46 const response: ClientConfigResponse = {
47 turnstileSiteKey: siteKey,
48 issuer: c.var.issuer,
49 socialProviders,
50 operatorName,
51 operatorContactEmail,
52 };
53 return c.json(response);
54};