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.jsonat build time (__APP_VERSION__). - The backend version comes from the nexus
/healthzresponse, polled every 15 seconds. - Both are shown in the About dialog, opened from the user menu.