feat(portals): serve each portal with Caddy instead of nginx (refs #166)
nginx resolves a variable `proxy_pass` upstream itself, using only the `resolver` directive and never the search domains in /etc/resolv.conf. That cost two workarounds in one script: rewriting the resolver address for rootless podman (Docker's 127.0.0.11 is wrong there), and injecting a full FQDN so the bare `bff` name could resolve on Kubernetes at all. Caddy dials its upstream per request through the system resolver, which reads nameserver *and* search domains, so `reverse_proxy bff:8080` resolves on every engine with no per-engine configuration — and it still starts before the BFF exists and picks up its restarts. Both workarounds are deleted with the script. Routing uses mutually-exclusive `handle` blocks, not a bare `try_files`: Caddy sorts rewrites *before* reverse_proxy, so a top-level SPA fallback would rewrite every API path to /index.html before the proxy saw it.
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
:80 {
|
||||||
|
# Same-origin API: behandelaars authenticate against the medewerker realm; the BFF validates it
|
||||||
|
# for /behandel/* (S-12c).
|
||||||
|
# `handle` blocks are mutually exclusive and matched most-specific-first, so the
|
||||||
|
# SPA fallback below can never swallow an API call — unlike a bare `try_files`,
|
||||||
|
# which Caddy sorts *before* reverse_proxy and would rewrite it to /index.html.
|
||||||
|
#
|
||||||
|
# No `resolver` stanza is needed: Caddy dials the upstream per
|
||||||
|
# request through the system resolver, so it starts before the BFF is up, picks up
|
||||||
|
# its restarts, and honours the DNS search domains in /etc/resolv.conf — which is
|
||||||
|
# what lets the bare `bff` name resolve on Kubernetes as well as under compose.
|
||||||
|
handle /behandel/* {
|
||||||
|
reverse_proxy bff:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
# The Angular app. Client-side routing: an unknown path serves index.html.
|
||||||
|
handle {
|
||||||
|
root * /usr/share/caddy
|
||||||
|
try_files {path} /index.html
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Multi-stage build for the behandel portal (Angular → nginx).
|
# Multi-stage build for the behandel portal (Angular → Caddy).
|
||||||
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
||||||
FROM node:24-slim AS build
|
FROM node:24-slim AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
@@ -13,15 +13,12 @@ COPY apps/behandel apps/behandel
|
|||||||
COPY libs libs
|
COPY libs libs
|
||||||
RUN pnpm nx build behandel
|
RUN pnpm nx build behandel
|
||||||
|
|
||||||
FROM nginx:1.27-alpine AS runtime
|
FROM caddy:2-alpine AS runtime
|
||||||
COPY apps/behandel/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY apps/behandel/Caddyfile /etc/caddy/Caddyfile
|
||||||
COPY --from=build /src/dist/apps/behandel/browser /usr/share/nginx/html
|
COPY --from=build /src/dist/apps/behandel/browser /usr/share/caddy
|
||||||
# Compose-time OIDC config: the browser (Playwright, on the compose network) reaches Keycloak by
|
# Compose-time OIDC config: the browser (Playwright, on the compose network) reaches Keycloak by
|
||||||
# service name, so the token issuer matches the BFF's medewerker authority (host-consistent, ADR-0013).
|
# service name, so the token issuer matches the BFF's medewerker authority (host-consistent, ADR-0013).
|
||||||
RUN printf '{ "authority": "http://keycloak:8080/realms/medewerker" }\n' > /usr/share/nginx/html/config.json
|
# Kubernetes mounts a ConfigMap over this file with the node address instead (ADR-0033).
|
||||||
# Make the reverse-proxy resolver engine-portable (Docker 127.0.0.11 vs podman aardvark); runs from
|
RUN printf '{ "authority": "http://keycloak:8080/realms/medewerker" }\n' > /usr/share/caddy/config.json
|
||||||
# the nginx image's /docker-entrypoint.d before nginx starts.
|
|
||||||
COPY apps/portal-nginx-resolver.sh /docker-entrypoint.d/40-resolver.sh
|
|
||||||
RUN chmod +x /docker-entrypoint.d/40-resolver.sh
|
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
# Resolve the BFF via Docker's embedded DNS at request time (variable proxy_pass), so nginx starts
|
|
||||||
# even before the BFF is up and picks up restarts — instead of failing to load the config.
|
|
||||||
resolver 127.0.0.11 ipv6=off valid=30s;
|
|
||||||
|
|
||||||
# Same-origin API: proxy the behandel endpoint group to the bff service. The api-client uses
|
|
||||||
# relative URLs, so the browser calls this origin and nginx forwards to the BFF — no CORS, and the
|
|
||||||
# medewerker token (same-origin) is attached by the app's interceptor (ADR-0013).
|
|
||||||
location /behandel/ {
|
|
||||||
set $bff http://bff:8080;
|
|
||||||
proxy_pass $bff;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
}
|
|
||||||
|
|
||||||
# SPA fallback — Angular client-side routing.
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ export interface RuntimeConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Route prefixes whose requests carry the medewerker token. These MUST match the **relative** URLs
|
* Route prefixes whose requests carry the medewerker token. These MUST match the **relative** URLs
|
||||||
* the api-client actually calls (same-origin via the nginx proxy) — the interceptor matches on
|
* the api-client actually calls (same-origin via the Caddy proxy) — the interceptor matches on
|
||||||
* `req.url`, which stays relative, so an absolute origin would never match and the token would go
|
* `req.url`, which stays relative, so an absolute origin would never match and the token would go
|
||||||
* unattached. Only `/behandel/` is secured; the app calls no other endpoint group.
|
* unattached. Only `/behandel/` is secured; the app calls no other endpoint group.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
:80 {
|
||||||
|
# Same-origin API: beheerders use the same medewerker realm as behandel (S-15a).
|
||||||
|
# `handle` blocks are mutually exclusive and matched most-specific-first, so the
|
||||||
|
# SPA fallback below can never swallow an API call — unlike a bare `try_files`,
|
||||||
|
# which Caddy sorts *before* reverse_proxy and would rewrite it to /index.html.
|
||||||
|
#
|
||||||
|
# No `resolver` stanza is needed: Caddy dials the upstream per
|
||||||
|
# request through the system resolver, so it starts before the BFF is up, picks up
|
||||||
|
# its restarts, and honours the DNS search domains in /etc/resolv.conf — which is
|
||||||
|
# what lets the bare `bff` name resolve on Kubernetes as well as under compose.
|
||||||
|
handle /beheer/* {
|
||||||
|
reverse_proxy bff:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
# The Angular app. Client-side routing: an unknown path serves index.html.
|
||||||
|
handle {
|
||||||
|
root * /usr/share/caddy
|
||||||
|
try_files {path} /index.html
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Multi-stage build for the beheer portal (Angular → nginx).
|
# Multi-stage build for the beheer portal (Angular → Caddy).
|
||||||
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
||||||
FROM node:24-slim AS build
|
FROM node:24-slim AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
@@ -13,15 +13,12 @@ COPY apps/beheer apps/beheer
|
|||||||
COPY libs libs
|
COPY libs libs
|
||||||
RUN pnpm nx build beheer
|
RUN pnpm nx build beheer
|
||||||
|
|
||||||
FROM nginx:1.27-alpine AS runtime
|
FROM caddy:2-alpine AS runtime
|
||||||
COPY apps/beheer/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY apps/beheer/Caddyfile /etc/caddy/Caddyfile
|
||||||
COPY --from=build /src/dist/apps/beheer/browser /usr/share/nginx/html
|
COPY --from=build /src/dist/apps/beheer/browser /usr/share/caddy
|
||||||
# Compose-time OIDC config: the browser (Playwright, on the compose network) reaches Keycloak by
|
# Compose-time OIDC config: the browser (Playwright, on the compose network) reaches Keycloak by
|
||||||
# service name, so the token issuer matches the BFF's medewerker authority (host-consistent, ADR-0013).
|
# service name, so the token issuer matches the BFF's medewerker authority (host-consistent, ADR-0013).
|
||||||
RUN printf '{ "authority": "http://keycloak:8080/realms/medewerker" }\n' > /usr/share/nginx/html/config.json
|
# Kubernetes mounts a ConfigMap over this file with the node address instead (ADR-0033).
|
||||||
# Make the reverse-proxy resolver engine-portable (Docker 127.0.0.11 vs podman aardvark); runs from
|
RUN printf '{ "authority": "http://keycloak:8080/realms/medewerker" }\n' > /usr/share/caddy/config.json
|
||||||
# the nginx image's /docker-entrypoint.d before nginx starts.
|
|
||||||
COPY apps/portal-nginx-resolver.sh /docker-entrypoint.d/40-resolver.sh
|
|
||||||
RUN chmod +x /docker-entrypoint.d/40-resolver.sh
|
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
# Resolve the BFF via Docker's embedded DNS at request time (variable proxy_pass), so nginx starts
|
|
||||||
# even before the BFF is up and picks up restarts — instead of failing to load the config.
|
|
||||||
resolver 127.0.0.11 ipv6=off valid=30s;
|
|
||||||
|
|
||||||
# Same-origin API: proxy the beheer endpoint group to the bff service. The api-client uses
|
|
||||||
# relative URLs, so the browser calls this origin and nginx forwards to the BFF — no CORS, and the
|
|
||||||
# medewerker token (same-origin) is attached by the app's interceptor (ADR-0013).
|
|
||||||
location /beheer/ {
|
|
||||||
set $bff http://bff:8080;
|
|
||||||
proxy_pass $bff;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
}
|
|
||||||
|
|
||||||
# SPA fallback — Angular client-side routing.
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ export interface RuntimeConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Route prefixes whose requests carry the medewerker token. These MUST match the **relative** URLs
|
* Route prefixes whose requests carry the medewerker token. These MUST match the **relative** URLs
|
||||||
* the api-client actually calls (same-origin via the nginx proxy) — the interceptor matches on
|
* the api-client actually calls (same-origin via the Caddy proxy) — the interceptor matches on
|
||||||
* `req.url`, which stays relative, so an absolute origin would never match and the token would go
|
* `req.url`, which stays relative, so an absolute origin would never match and the token would go
|
||||||
* unattached. Only `/beheer/` is secured; the app calls no other endpoint group.
|
* unattached. Only `/beheer/` is secured; the app calls no other endpoint group.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
:80 {
|
||||||
|
# Same-origin API: the public register is anonymous, but still reads through the BFF (S-09).
|
||||||
|
# `handle` blocks are mutually exclusive and matched most-specific-first, so the
|
||||||
|
# SPA fallback below can never swallow an API call — unlike a bare `try_files`,
|
||||||
|
# which Caddy sorts *before* reverse_proxy and would rewrite it to /index.html.
|
||||||
|
#
|
||||||
|
# No `resolver` stanza is needed: Caddy dials the upstream per
|
||||||
|
# request through the system resolver, so it starts before the BFF is up, picks up
|
||||||
|
# its restarts, and honours the DNS search domains in /etc/resolv.conf — which is
|
||||||
|
# what lets the bare `bff` name resolve on Kubernetes as well as under compose.
|
||||||
|
handle /openbaar/* {
|
||||||
|
reverse_proxy bff:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
# The Angular app. Client-side routing: an unknown path serves index.html.
|
||||||
|
handle {
|
||||||
|
root * /usr/share/caddy
|
||||||
|
try_files {path} /index.html
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Multi-stage build for the openbaar portal (Angular → nginx).
|
# Multi-stage build for the openbaar portal (Angular → Caddy).
|
||||||
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
||||||
FROM node:24-slim AS build
|
FROM node:24-slim AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
@@ -13,13 +13,9 @@ COPY apps/openbaar apps/openbaar
|
|||||||
COPY libs libs
|
COPY libs libs
|
||||||
RUN pnpm nx build openbaar
|
RUN pnpm nx build openbaar
|
||||||
|
|
||||||
FROM nginx:1.27-alpine AS runtime
|
FROM caddy:2-alpine AS runtime
|
||||||
COPY apps/openbaar/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY apps/openbaar/Caddyfile /etc/caddy/Caddyfile
|
||||||
COPY --from=build /src/dist/apps/openbaar/browser /usr/share/nginx/html
|
COPY --from=build /src/dist/apps/openbaar/browser /usr/share/caddy
|
||||||
# No runtime config: the openbaar register is anonymous (no OIDC authority to inject).
|
# No runtime config: the openbaar register is anonymous (no OIDC authority to inject).
|
||||||
# Make the reverse-proxy resolver engine-portable (Docker 127.0.0.11 vs podman aardvark); runs from
|
|
||||||
# the nginx image's /docker-entrypoint.d before nginx starts.
|
|
||||||
COPY apps/portal-nginx-resolver.sh /docker-entrypoint.d/40-resolver.sh
|
|
||||||
RUN chmod +x /docker-entrypoint.d/40-resolver.sh
|
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
# Resolve the BFF via Docker's embedded DNS at request time (variable proxy_pass), so nginx starts
|
|
||||||
# even before the BFF is up and picks up restarts — instead of failing to load the config.
|
|
||||||
resolver 127.0.0.11 ipv6=off valid=30s;
|
|
||||||
|
|
||||||
# Same-origin API: proxy the anonymous openbaar endpoint group to the bff service. The api-client
|
|
||||||
# uses relative URLs, so the browser calls this origin and nginx forwards to the BFF — no CORS.
|
|
||||||
location /openbaar/ {
|
|
||||||
set $bff http://bff:8080;
|
|
||||||
proxy_pass $bff;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
}
|
|
||||||
|
|
||||||
# SPA fallback — Angular client-side routing.
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import { appRoutes } from './app.routes';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The openbaar register is a public, anonymous read: no DigiD, no auth interceptor. The app is served
|
* The openbaar register is a public, anonymous read: no DigiD, no auth interceptor. The app is served
|
||||||
* same-origin as the BFF (nginx proxies /openbaar), so the api-client's relative calls stay same-origin.
|
* same-origin as the BFF (Caddy proxies /openbaar), so the api-client's relative calls stay same-origin.
|
||||||
*/
|
*/
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Point nginx's reverse-proxy `resolver` at THIS container's real DNS server.
|
|
||||||
#
|
|
||||||
# The portal nginx configs use a variable proxy_pass, which needs a `resolver` so the BFF hostname is
|
|
||||||
# resolved at request time (nginx can start before the BFF is up). The config hardcodes Docker's
|
|
||||||
# embedded DNS (127.0.0.11) — correct on Docker/Docker Desktop, but rootless podman uses a
|
|
||||||
# network-specific address (aardvark, e.g. 10.89.0.1), so proxied calls 502 there. Read the actual
|
|
||||||
# nameserver from /etc/resolv.conf and substitute it, so the reverse proxy works on any engine.
|
|
||||||
#
|
|
||||||
# Runs from the nginx image's /docker-entrypoint.d/ before nginx starts. On Docker the nameserver IS
|
|
||||||
# 127.0.0.11, so the substitution is a no-op. Guarded (no `set -e`) so it's safe whether the nginx
|
|
||||||
# entrypoint executes or sources it.
|
|
||||||
ns="$(awk '/^nameserver/{print $2; exit}' /etc/resolv.conf 2>/dev/null)"
|
|
||||||
if [ -n "$ns" ] && [ "$ns" != "127.0.0.11" ]; then
|
|
||||||
sed -i "s/resolver 127\.0\.0\.11/resolver $ns/" /etc/nginx/conf.d/default.conf 2>/dev/null || true
|
|
||||||
echo "portal-nginx-resolver: set resolver to $ns"
|
|
||||||
fi
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
:80 {
|
||||||
|
# Same-origin API: the api-client uses relative URLs, so the browser calls this origin and Caddy
|
||||||
|
# forwards to the BFF — no CORS, and the DigiD token is attached by the app interceptor
|
||||||
|
# (S-08d/ADR-0010).
|
||||||
|
# `handle` blocks are mutually exclusive and matched most-specific-first, so the
|
||||||
|
# SPA fallback below can never swallow an API call — unlike a bare `try_files`,
|
||||||
|
# which Caddy sorts *before* reverse_proxy and would rewrite it to /index.html.
|
||||||
|
#
|
||||||
|
# No `resolver` stanza is needed: Caddy dials the upstream per
|
||||||
|
# request through the system resolver, so it starts before the BFF is up, picks up
|
||||||
|
# its restarts, and honours the DNS search domains in /etc/resolv.conf — which is
|
||||||
|
# what lets the bare `bff` name resolve on Kubernetes as well as under compose.
|
||||||
|
handle /self-service/* {
|
||||||
|
reverse_proxy bff:8080
|
||||||
|
}
|
||||||
|
handle /openbaar/* {
|
||||||
|
reverse_proxy bff:8080
|
||||||
|
}
|
||||||
|
|
||||||
|
# The Angular app. Client-side routing: an unknown path serves index.html.
|
||||||
|
handle {
|
||||||
|
root * /usr/share/caddy
|
||||||
|
try_files {path} /index.html
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Multi-stage build for the self-service portal (Angular → nginx).
|
# Multi-stage build for the self-service portal (Angular → Caddy).
|
||||||
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
# Build context is the repo root (the app needs the pnpm workspace + libs). See infra/docker-compose.yml.
|
||||||
FROM node:24-slim AS build
|
FROM node:24-slim AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
@@ -13,15 +13,12 @@ COPY apps/self-service apps/self-service
|
|||||||
COPY libs libs
|
COPY libs libs
|
||||||
RUN pnpm nx build self-service
|
RUN pnpm nx build self-service
|
||||||
|
|
||||||
FROM nginx:1.27-alpine AS runtime
|
FROM caddy:2-alpine AS runtime
|
||||||
COPY apps/self-service/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY apps/self-service/Caddyfile /etc/caddy/Caddyfile
|
||||||
COPY --from=build /src/dist/apps/self-service/browser /usr/share/nginx/html
|
COPY --from=build /src/dist/apps/self-service/browser /usr/share/caddy
|
||||||
# Compose-time OIDC config: the browser (Playwright, on the compose network) reaches Keycloak by
|
# Compose-time OIDC config: the browser (Playwright, on the compose network) reaches Keycloak by
|
||||||
# service name, so the token issuer matches the BFF's authority (host-consistent, ADR-0010).
|
# service name, so the token issuer matches the BFF's authority (host-consistent, ADR-0010).
|
||||||
RUN printf '{ "authority": "http://keycloak:8080/realms/digid" }\n' > /usr/share/nginx/html/config.json
|
# Kubernetes mounts a ConfigMap over this file with the node address instead (ADR-0033).
|
||||||
# Make the reverse-proxy resolver engine-portable (Docker 127.0.0.11 vs podman aardvark); runs from
|
RUN printf '{ "authority": "http://keycloak:8080/realms/digid" }\n' > /usr/share/caddy/config.json
|
||||||
# the nginx image's /docker-entrypoint.d before nginx starts.
|
|
||||||
COPY apps/portal-nginx-resolver.sh /docker-entrypoint.d/40-resolver.sh
|
|
||||||
RUN chmod +x /docker-entrypoint.d/40-resolver.sh
|
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
# Resolve the BFF via Docker's embedded DNS at request time (variable proxy_pass), so nginx starts
|
|
||||||
# even before the BFF is up and picks up restarts — instead of failing to load the config.
|
|
||||||
resolver 127.0.0.11 ipv6=off valid=30s;
|
|
||||||
|
|
||||||
# Same-origin API: proxy the BFF endpoint groups to the bff service. The api-client uses relative
|
|
||||||
# URLs, so the browser calls this origin and nginx forwards to the BFF — no CORS, and the DigiD
|
|
||||||
# token (same-origin) is attached by the app's interceptor (S-08d/ADR-0010).
|
|
||||||
location /self-service/ {
|
|
||||||
set $bff http://bff:8080;
|
|
||||||
proxy_pass $bff;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
}
|
|
||||||
location /openbaar/ {
|
|
||||||
set $bff http://bff:8080;
|
|
||||||
proxy_pass $bff;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
}
|
|
||||||
|
|
||||||
# SPA fallback — Angular client-side routing.
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,7 @@ export interface RuntimeConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Route prefixes whose requests carry the DigiD token. These MUST match the **relative** URLs the
|
* Route prefixes whose requests carry the DigiD token. These MUST match the **relative** URLs the
|
||||||
* api-client actually calls (same-origin via the nginx proxy) — the interceptor matches on `req.url`,
|
* api-client actually calls (same-origin via the Caddy proxy) — the interceptor matches on `req.url`,
|
||||||
* which stays relative, so an absolute origin would never match and the token would go unattached.
|
* which stays relative, so an absolute origin would never match and the token would go unattached.
|
||||||
* `/openbaar/` is deliberately excluded: it is the anonymous public register.
|
* `/openbaar/` is deliberately excluded: it is the anonymous public register.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -510,7 +510,7 @@ services:
|
|||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
# ── Portals (S-08/S-09/S-12) ──────────────────────────────────────────────
|
# ── Portals (S-08/S-09/S-12) ──────────────────────────────────────────────
|
||||||
# nginx serves each Angular app and reverse-proxies its endpoint group to the BFF (same-origin).
|
# Caddy serves each Angular app and reverse-proxies its endpoint group to the BFF (same-origin).
|
||||||
# The images bake config.json with the compose authority (keycloak:8080), which a HOST browser
|
# The images bake config.json with the compose authority (keycloak:8080), which a HOST browser
|
||||||
# can't resolve — so here we bind-mount a config.json pointing at the host-published localhost:8180
|
# can't resolve — so here we bind-mount a config.json pointing at the host-published localhost:8180
|
||||||
# (matching KC_HOSTNAME). openbaar is anonymous and needs no config.
|
# (matching KC_HOSTNAME). openbaar is anonymous and needs no config.
|
||||||
@@ -522,7 +522,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8140:80"
|
- "8140:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./local-config/self-service.config.json:/usr/share/nginx/html/config.json:ro,z
|
- ./local-config/self-service.config.json:/usr/share/caddy/config.json:ro,z
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
@@ -562,7 +562,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8142:80"
|
- "8142:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./local-config/behandel.config.json:/usr/share/nginx/html/config.json:ro,z
|
- ./local-config/behandel.config.json:/usr/share/caddy/config.json:ro,z
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ services:
|
|||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
# ── Self-Service portal (S-08d) ────────────────────────────────────────────
|
# ── Self-Service portal (S-08d) ────────────────────────────────────────────
|
||||||
# nginx serves the Angular app and reverse-proxies /self-service + /openbaar to the BFF
|
# Caddy serves the Angular app and reverse-proxies /self-service + /openbaar to the BFF
|
||||||
# (same-origin, no CORS). The Playwright e2e drives it inside this network so the DigiD
|
# (same-origin, no CORS). The Playwright e2e drives it inside this network so the DigiD
|
||||||
# token issuer (keycloak:8080) matches the BFF's authority (ADR-0010).
|
# token issuer (keycloak:8080) matches the BFF's authority (ADR-0010).
|
||||||
self-service:
|
self-service:
|
||||||
@@ -507,7 +507,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8140:80"
|
- "8140:80"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# 127.0.0.1, not localhost: nginx listens on IPv4 only, but localhost resolves to ::1 first.
|
# 127.0.0.1, not localhost: keeps the check on the interface Caddy is published on.
|
||||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
@@ -520,7 +520,7 @@ services:
|
|||||||
condition: service_started
|
condition: service_started
|
||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
# The openbaar (public) register portal: nginx serves the Angular app and reverse-proxies
|
# The openbaar (public) register portal: Caddy serves the Angular app and reverse-proxies
|
||||||
# /openbaar to the BFF. Anonymous — no DigiD, no Keycloak dependency (S-09).
|
# /openbaar to the BFF. Anonymous — no DigiD, no Keycloak dependency (S-09).
|
||||||
openbaar:
|
openbaar:
|
||||||
build:
|
build:
|
||||||
@@ -530,7 +530,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8141:80"
|
- "8141:80"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# 127.0.0.1, not localhost: nginx listens on IPv4 only, but localhost resolves to ::1 first.
|
# 127.0.0.1, not localhost: keeps the check on the interface Caddy is published on.
|
||||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
@@ -541,7 +541,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
# The behandel portal: nginx serves the Angular app and reverse-proxies /behandel to the BFF.
|
# The behandel portal: Caddy serves the Angular app and reverse-proxies /behandel to the BFF.
|
||||||
# Behandelaars log in against the Keycloak medewerker realm (ADR-0013; S-12).
|
# Behandelaars log in against the Keycloak medewerker realm (ADR-0013; S-12).
|
||||||
behandel:
|
behandel:
|
||||||
build:
|
build:
|
||||||
@@ -551,7 +551,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8142:80"
|
- "8142:80"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# 127.0.0.1, not localhost: nginx listens on IPv4 only, but localhost resolves to ::1 first.
|
# 127.0.0.1, not localhost: keeps the check on the interface Caddy is published on.
|
||||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
@@ -564,7 +564,7 @@ services:
|
|||||||
condition: service_started
|
condition: service_started
|
||||||
networks: [cg]
|
networks: [cg]
|
||||||
|
|
||||||
# The beheer portal: nginx serves the Angular app and reverse-proxies /beheer to the BFF.
|
# The beheer portal: Caddy serves the Angular app and reverse-proxies /beheer to the BFF.
|
||||||
# Beheerders log in against the Keycloak medewerker realm (same realm as behandel, S-15a).
|
# Beheerders log in against the Keycloak medewerker realm (same realm as behandel, S-15a).
|
||||||
beheer:
|
beheer:
|
||||||
build:
|
build:
|
||||||
@@ -574,7 +574,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8143:80"
|
- "8143:80"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# 127.0.0.1, not localhost: nginx listens on IPv4 only, but localhost resolves to ::1 first.
|
# 127.0.0.1, not localhost: keeps the check on the interface Caddy is published on.
|
||||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/ || exit 1"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ services:
|
|||||||
KC_HOSTNAME_BACKCHANNEL_DYNAMIC: "true"
|
KC_HOSTNAME_BACKCHANNEL_DYNAMIC: "true"
|
||||||
self-service:
|
self-service:
|
||||||
volumes:
|
volumes:
|
||||||
- ./local-config/self-service.config.json:/usr/share/nginx/html/config.json:ro,z
|
- ./local-config/self-service.config.json:/usr/share/caddy/config.json:ro,z
|
||||||
behandel:
|
behandel:
|
||||||
volumes:
|
volumes:
|
||||||
- ./local-config/behandel.config.json:/usr/share/nginx/html/config.json:ro,z
|
- ./local-config/behandel.config.json:/usr/share/caddy/config.json:ro,z
|
||||||
# beheer is the same medewerker realm as behandel, so it reuses behandel's config verbatim.
|
# beheer is the same medewerker realm as behandel, so it reuses behandel's config verbatim.
|
||||||
beheer:
|
beheer:
|
||||||
volumes:
|
volumes:
|
||||||
- ./local-config/behandel.config.json:/usr/share/nginx/html/config.json:ro,z
|
- ./local-config/behandel.config.json:/usr/share/caddy/config.json:ro,z
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export interface DigiadAuthOptions {
|
|||||||
redirectUrl: string;
|
redirectUrl: string;
|
||||||
/**
|
/**
|
||||||
* Route prefixes whose requests get the bearer token attached. The api-client calls the BFF with
|
* Route prefixes whose requests get the bearer token attached. The api-client calls the BFF with
|
||||||
* **relative** URLs (same-origin via the nginx proxy), so these must be relative path prefixes
|
* **relative** URLs (same-origin via the Caddy proxy), so these must be relative path prefixes
|
||||||
* (e.g. `/self-service/`) — angular-auth-oidc-client matches `req.url.startsWith(route)`, and a
|
* (e.g. `/self-service/`) — angular-auth-oidc-client matches `req.url.startsWith(route)`, and a
|
||||||
* relative `req.url` never starts with an absolute origin.
|
* relative `req.url` never starts with an absolute origin.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export interface MedewerkerAuthOptions {
|
|||||||
redirectUrl: string;
|
redirectUrl: string;
|
||||||
/**
|
/**
|
||||||
* Route prefixes whose requests get the bearer token attached. The api-client calls the BFF with
|
* Route prefixes whose requests get the bearer token attached. The api-client calls the BFF with
|
||||||
* **relative** URLs (same-origin via the nginx proxy), so these must be relative path prefixes
|
* **relative** URLs (same-origin via the Caddy proxy), so these must be relative path prefixes
|
||||||
* (e.g. `/behandel/`) — angular-auth-oidc-client matches `req.url.startsWith(route)`, and a
|
* (e.g. `/behandel/`) — angular-auth-oidc-client matches `req.url.startsWith(route)`, and a
|
||||||
* relative `req.url` never starts with an absolute origin.
|
* relative `req.url` never starts with an absolute origin.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ test('DigiD submit → public INGEDIEND → documenten → behandelaar goedkeurt
|
|||||||
await expect(goedkeuren).toBeVisible({ timeout: 30_000 });
|
await expect(goedkeuren).toBeVisible({ timeout: 30_000 });
|
||||||
|
|
||||||
// Click and wait for the decide POST to finish (204) BEFORE leaving the page. `click()` only
|
// Click and wait for the decide POST to finish (204) BEFORE leaving the page. `click()` only
|
||||||
// dispatches the request; navigating away immediately cancels it in flight (nginx logs a 499) and
|
// dispatches the request; navigating away immediately cancels it in flight (the proxy logs a client-cancelled request) and
|
||||||
// the decision never reaches the domain — so the registration would stay INGEDIEND.
|
// the decision never reaches the domain — so the registration would stay INGEDIEND.
|
||||||
const decided = staff.waitForResponse(
|
const decided = staff.waitForResponse(
|
||||||
(r) =>
|
(r) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user