Skip to content

Deploying Portal

The portal is a SvelteKit app (Svelte 5) built with @sveltejs/adapter-cloudflare and deployed to Cloudflare Pages, one project per environment (digilan-portal-<env>).

Environment variables (build-time)

All portal variables are Vite variables baked into the bundle at build time. Changing one means rebuilding and redeploying.

Variable Description
VITE_NEXUS_URL Nexus base URL for the environment.
VITE_REALTIME_WS_URL wss:// URL of the realtime Worker.
VITE_ZITADEL_ISSUER https://auth.digilan-edge.com.
VITE_ZITADEL_CLIENT_ID The environment's portal client id; CI reads DEV_ZITADEL_CLIENT_ID, STG_ZITADEL_CLIENT_ID or PRD_ZITADEL_CLIENT_ID.
VITE_ZITADEL_PROJECT_ID The environment's Zitadel project id; CI reads DEV_ / STG_ / PRD_ZITADEL_PROJECT_ID.
VITE_MRWIFI_URL Monsieur Wifi app URL, for the sidebar link.
VITE_RADIUS_URL RADIUS SPA URL, for the home-page card.
VITE_ENV development, staging or production; shown in the About dialog.
VITE_NEXUS_BINARY Optional. true switches the ConnectRPC clients to binary protobuf (default JSON).

CI/CD

Only a tag deploys

The pipeline runs on a tag or a merge request. A merge request runs the checks and stops; merging to main builds and deploys nothing.

Release by tagging: npm run tag:patch (or tag:minor / tag:major) bumps package.json, commits, and pushes the commit and the tag.

Each tag builds one bundle per environment and deploys it with wrangler pages deploy .svelte-kit/cloudflare --project-name=<project>:

Env VITE_NEXUS_URL VITE_REALTIME_WS_URL Pages project Deploy job
dev https://dev.nexus.digilan-edge.com wss://dev.realtime.digilan-edge.com digilan-portal-dev deploy_dev, automatic
stg https://stg.nexus.digilan-edge.com wss://stg.realtime.digilan-edge.com digilan-portal-stg deploy_stg, manual
prod https://prod.nexus.digilan-edge.com wss://realtime.digilan-edge.com digilan-portal-prod deploy_prd, manual

Promoting to stg or prod is pressing play on the same pipeline; nothing is rebuilt. Deploys use CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID from the digilan/platform group variables.

NODE_OPTIONS: --no-network-family-autoselection is required

Node 20+ races IPv6 and IPv4 (Happy Eyeballs). Runner containers resolve IPv6 but cannot route it, and undici stalls instead of falling back. The symptoms are npm ci failing with "Exit handler never called!" and wrangler pages deploy failing with "fetch failed". The flag is set globally in variables: because every Node job needs it; do not remove it or add a Node job that overrides it.

Pages projects and Terraform

Terraform (infra/terraform/modules/dns) owns each Pages project's existence, name, custom domain and DNS record. It ignores build_config, deployment_configs and source: those belong to the deploys. Compatibility flags such as nodejs_compat come from the portal's wrangler.toml.

Versioning

  • The app version comes from package.json at build time (__APP_VERSION__).
  • The backend version comes from the nexus /healthz response, polled every 15 seconds.
  • Both are shown in the About dialog, opened from the user menu.