72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
/**
|
|
* Environment-variable validation. Called at module load time by routes
|
|
* that need real Civi credentials. In stub mode, no env is required.
|
|
*
|
|
* Centralizing here means we can:
|
|
* - Fail fast at boot in production if a required var is missing
|
|
* - Avoid leaking secrets to error responses (we redact in error paths)
|
|
* - Reuse the "is stub mode?" check in one place
|
|
*/
|
|
|
|
export interface AppEnv {
|
|
isStub: boolean;
|
|
isProduction: boolean;
|
|
civiBaseUrl?: string;
|
|
civiApiKey?: string;
|
|
civiSiteKey?: string;
|
|
basicAuthUser?: string;
|
|
basicAuthPass?: string;
|
|
/**
|
|
* Optional admin token to gate /api/health in production. If unset in prod,
|
|
* /api/health is disabled entirely (returns 404). Set this to a long random
|
|
* string and pass via `?token=<>` to the health endpoint to read diagnostics.
|
|
*/
|
|
healthToken?: string;
|
|
/**
|
|
* Same-origin host this app runs at. Used to build `frame-ancestors 'self'`
|
|
* and any absolute self-links. Defaults to inferring from the request.
|
|
*/
|
|
publicOrigin?: string;
|
|
}
|
|
|
|
let cached: AppEnv | undefined;
|
|
|
|
export function appEnv(): AppEnv {
|
|
if (cached) return cached;
|
|
const e: AppEnv = {
|
|
isStub: !(
|
|
process.env.CIVI_BASE_URL &&
|
|
process.env.CIVI_API_KEY &&
|
|
process.env.CIVI_SITE_KEY
|
|
),
|
|
isProduction: process.env.NODE_ENV === "production",
|
|
civiBaseUrl: process.env.CIVI_BASE_URL,
|
|
civiApiKey: process.env.CIVI_API_KEY,
|
|
civiSiteKey: process.env.CIVI_SITE_KEY,
|
|
basicAuthUser: process.env.CIVI_HTTP_AUTH_USER,
|
|
basicAuthPass: process.env.CIVI_HTTP_AUTH_PASS,
|
|
healthToken: process.env.HEALTH_TOKEN,
|
|
publicOrigin: process.env.PUBLIC_ORIGIN,
|
|
};
|
|
// In production, refuse to start in stub mode — that's almost certainly a
|
|
// misconfiguration. Better to crash loudly than silently serve stub data.
|
|
if (e.isProduction && e.isStub) {
|
|
throw new Error(
|
|
"Refusing to run in production without CIVI_BASE_URL, CIVI_API_KEY, and CIVI_SITE_KEY set. " +
|
|
"Either provide all three, or set NODE_ENV != 'production'.",
|
|
);
|
|
}
|
|
cached = e;
|
|
return e;
|
|
}
|
|
|
|
/** Strip secrets from any string before logging or echoing in responses. */
|
|
export function redact(s: string): string {
|
|
let out = s;
|
|
const e = appEnv();
|
|
for (const v of [e.civiApiKey, e.civiSiteKey, e.basicAuthPass, e.healthToken]) {
|
|
if (v && v.length >= 6) out = out.split(v).join("«redacted»");
|
|
}
|
|
return out;
|
|
}
|