/** * Environment access, in one place so nothing else has to guess. */ /** * The origin the device and the browser reach us on. * * Prefers what the request actually arrived as, because the panel has to be * handed a URL it can fetch — a mismatch here shows up as a blank screen in a * shop window, which is the most expensive place to debug. Falls back to the * configured domain for calls that have no request, such as background jobs. */ export function publicBaseUrl(request?: Request): string { if (request) { const host = request.headers.get('x-forwarded-host') ?? request.headers.get('host'); if (host) { const proto = request.headers.get('x-forwarded-proto') ?? new URL(request.url).protocol.replace(':', ''); return `${proto}://${host}`; } } const configured = process.env.NEXTAUTH_URL; if (configured) { return configured.replace(/\/$/, ''); } const domain = process.env.APP_DOMAIN; if (domain) { return domain.startsWith('http') ? domain.replace(/\/$/, '') : `https://${domain}`; } return 'http://localhost:3000'; } /** * Whether the panel may talk to us over plain HTTP. * * Off by default. It exists because these ESP32 firmwares sometimes fail on a * certificate chain, and a shop with a blank window needs a way out that does * not involve reflashing. The device token is separate and revocable precisely * so this switch stays survivable. */ export function deviceAllowsHttp(): boolean { return process.env.DEVICE_ALLOW_HTTP === 'true'; }