Files
apf_portal/apps/portal-bff/src/config/check-database-url.ts
T
julien c3c15585ff
CI / check (push) Successful in 1m43s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 1m33s
CI / a11y (push) Successful in 42s
CI / perf (push) Successful in 2m21s
style(portal-bff): apply prettier to check-database-url.ts (#71)
## Summary

Cosmetic-only follow-up to #70. Collapses one over-cautious multi-line `throw new Error(...)` into a single line that fits within the project's 100-char `printWidth`.

The reformat was produced by lint-staged **during** the amend that landed #70, but applied to the working tree only — the modification never made it back into the staged content of the amend. The merged commit therefore carries the un-prettified version.

Spotted locally with `pnpm exec prettier --check apps/portal-bff/src/config/check-database-url.ts` failing. Left unchecked, the next PR's `check` job would break at `format:check` for unrelated reasons.

## Test plan

- [ ] CI green on this PR (`format:check` clean).
- [ ] No behavioural change — only whitespace.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #71
2026-05-09 22:45:10 +02:00

53 lines
2.1 KiB
TypeScript

/**
* Sanity-check `DATABASE_URL` early in bootstrap so a malformed value
* fails fast with a clear, actionable message instead of an opaque
* Prisma error deep into the request lifecycle.
*
* The most common malformation is a literal special character in
* POSTGRES_PASSWORD that the contributor forgot to URL-encode (`@`,
* `#`, `:`, `/`, `?`, `%`, `&`, `=`, `+`, `;` all require it). This
* is the same family of bug that bit pgweb in #63 — pgweb's compose
* was switched to discrete CLI flags to bypass URL parsing entirely,
* but Prisma requires a URL string, so we have no equivalent escape
* hatch here. Documentation + early validation are the v1 fix.
*/
const URL_ENCODE_HINT =
'If POSTGRES_PASSWORD contains special characters (@ # : / ? % & = + ;), ' +
'they must be URL-encoded in DATABASE_URL — e.g., "@" → "%40", "#" → "%23". ' +
'See apps/portal-bff/.env.example for the canonical form.';
export function assertDatabaseUrl(): void {
const raw = process.env['DATABASE_URL'];
if (!raw) {
throw new Error(
'DATABASE_URL is not set. Copy apps/portal-bff/.env.example to ' +
'apps/portal-bff/.env and adjust.',
);
}
let parsed: URL;
try {
parsed = new URL(raw);
} catch {
throw new Error(`DATABASE_URL is not a valid URL. ${URL_ENCODE_HINT}`);
}
if (parsed.protocol !== 'postgresql:' && parsed.protocol !== 'postgres:') {
throw new Error(`DATABASE_URL must use the "postgresql://" scheme; got "${parsed.protocol}".`);
}
// Heuristic — multiple "@" before the path almost always means the
// password contains a literal "@" that should have been encoded.
// Splitting on the parsed pathname isolates the userinfo+host
// segment without depending on scheme-specific quirks.
const beforePath = raw.split(parsed.pathname || '/')[0] ?? raw;
const atCount = (beforePath.match(/@/g) ?? []).length;
if (atCount > 1) {
throw new Error(
`DATABASE_URL has ${atCount} "@" before the database path — your password almost ` +
`certainly needs URL-encoding. ${URL_ENCODE_HINT}`,
);
}
}