|
| 1 | +import { defineEnvVars } from '@sveltejs/kit/env'; |
| 2 | +import { building } from '$app/env'; |
| 3 | + |
| 4 | +// In no-backend (VERCEL=1) mode there is no database, no login and no writing, |
| 5 | +// so neither secret is needed. A validator only receives its own value, but this |
| 6 | +// file is evaluated when the server starts and can read the environment directly. |
| 7 | +const has_backend = () => !globalThis.process?.env?.VERCEL; |
| 8 | + |
| 9 | +// Variables are validated twice: once while the app is built, and again when it |
| 10 | +// starts. Deploys build without secrets — the Dockerfile runs `pnpm build` long |
| 11 | +// before the server's .env exists — so required variables are only enforced at |
| 12 | +// startup, never during the build. |
| 13 | +function required_with_backend(hint: string) { |
| 14 | + return { |
| 15 | + '~standard': { |
| 16 | + version: 1 as const, |
| 17 | + vendor: 'editable', |
| 18 | + types: undefined as unknown as { input: string | undefined; output: string }, |
| 19 | + validate: (value: unknown) => { |
| 20 | + if (!building && has_backend() && !value) { |
| 21 | + return { issues: [{ message: `Value is missing. ${hint}` }] }; |
| 22 | + } |
| 23 | + |
| 24 | + return { value: (value ?? '') as string }; |
| 25 | + } |
| 26 | + } |
| 27 | + }; |
| 28 | +} |
| 29 | + |
| 30 | +// Variables that may legitimately be absent need a validator saying so, |
| 31 | +// otherwise SvelteKit treats them as required non-empty strings. |
| 32 | +function optional<T>(parse: (value: string | undefined) => T) { |
| 33 | + return { |
| 34 | + '~standard': { |
| 35 | + version: 1 as const, |
| 36 | + vendor: 'editable', |
| 37 | + // Type-only carrier that SvelteKit reads to infer the exported type. |
| 38 | + // Standard Schema never touches it at runtime. |
| 39 | + types: undefined as unknown as { input: string | undefined; output: T }, |
| 40 | + validate: (value: unknown) => ({ value: parse(value as string | undefined) }) |
| 41 | + } |
| 42 | + }; |
| 43 | +} |
| 44 | + |
| 45 | +const where = |
| 46 | + 'Set it in .env locally, via `fly secrets set` on Fly, or in the server .env on a VPS.'; |
| 47 | + |
| 48 | +export const variables = defineEnvVars({ |
| 49 | + ADMIN_PASSWORD: { |
| 50 | + description: |
| 51 | + 'Password for the admin login. Required whenever the backend is enabled — the app refuses to start without it. Unused in VERCEL=1 mode.', |
| 52 | + schema: required_with_backend(where) |
| 53 | + }, |
| 54 | + ORIGIN: { |
| 55 | + description: |
| 56 | + 'Public origin of the deployment, e.g. https://my-site.example.com. Must match the URL used in the browser exactly, or write requests fail with 403. Required whenever the backend is enabled.', |
| 57 | + schema: required_with_backend(where) |
| 58 | + }, |
| 59 | + VERCEL: { |
| 60 | + description: |
| 61 | + 'Set by Vercel. Its absence is what puts the app in backend mode, so it must stay optional.', |
| 62 | + schema: optional((value) => value) |
| 63 | + }, |
| 64 | + NODE_ENV: { |
| 65 | + description: 'Used to decide whether session cookies are marked secure.', |
| 66 | + schema: optional((value) => value) |
| 67 | + } |
| 68 | +}); |
0 commit comments