deploy: route frontend and core through one origin
This commit is contained in:
@@ -1,15 +1,23 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
backend_base_url=${BACKEND_BASE_URL-/api}
|
||||
if ! /usr/local/bin/validate-backend-url "$backend_base_url"; then
|
||||
echo "Invalid BACKEND_BASE_URL: use empty/root, /api, or a valid http(s) base without credentials, query, or fragment" >&2
|
||||
THT_FRONTEND_API_UPSTREAM=${THT_FRONTEND_API_UPSTREAM:-http://core:8787}
|
||||
THT_FRONTEND_API_UPSTREAM=${THT_FRONTEND_API_UPSTREAM%/}
|
||||
case "$THT_FRONTEND_API_UPSTREAM" in
|
||||
http://*|https://*) ;;
|
||||
*)
|
||||
echo "Invalid THT_FRONTEND_API_UPSTREAM: use an internal http(s) upstream" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
export THT_FRONTEND_API_UPSTREAM
|
||||
|
||||
if ! envsubst '${THT_FRONTEND_API_UPSTREAM}' \
|
||||
< /etc/nginx/templates/default.conf.template \
|
||||
> /etc/nginx/conf.d/default.conf; then
|
||||
echo "Unable to render nginx API upstream configuration" >&2
|
||||
exit 2
|
||||
fi
|
||||
runtime_config=$(jq -cn --arg backend_base_url "$backend_base_url" \
|
||||
'{backendBaseUrl: $backend_base_url}')
|
||||
printf 'window.__THOTHII_CONFIG__ = %s;\n' "$runtime_config" \
|
||||
> /usr/share/nginx/html/config.js
|
||||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
exec "$@"
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# thothii-frontend: build Vite (React) + nginx-unprivileged (porta 8080).
|
||||
# Build args:
|
||||
# VITE_BASE prefisso asset ("/" standalone, "/datamart-builder/assets/" embedded)
|
||||
# VITE_BACKEND_URL base API ("http://localhost:8787" standalone, "/datamart-builder/api" embedded)
|
||||
FROM node:22-bookworm AS build
|
||||
WORKDIR /src
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci
|
||||
COPY frontend/ ./
|
||||
ARG VITE_BASE=/
|
||||
ARG VITE_BACKEND_URL=http://localhost:8787
|
||||
ENV VITE_BASE=$VITE_BASE VITE_BACKEND_URL=$VITE_BACKEND_URL
|
||||
ENV VITE_BASE=/ VITE_BACKEND_URL=/api
|
||||
RUN npm run build
|
||||
# typecheck opzionale (non bloccante nella build dell'immagine)
|
||||
RUN npx tsc -b 2>/dev/null || true
|
||||
|
||||
FROM nginxinc/nginx-unprivileged:1.27-alpine AS runtime
|
||||
COPY --from=build /src/dist /usr/share/nginx/html
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY docker/nginx.conf.template /etc/nginx/templates/default.conf.template
|
||||
COPY --chmod=755 docker/frontend-entrypoint.sh /usr/local/bin/frontend-entrypoint
|
||||
ENTRYPOINT ["/usr/local/bin/frontend-entrypoint"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
EXPOSE 8080
|
||||
|
||||
@@ -3,20 +3,16 @@ server {
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location = /config.js {
|
||||
add_header Cache-Control "no-store";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location = /health {
|
||||
proxy_pass http://core:8787/health;
|
||||
proxy_pass ${THT_FRONTEND_API_UPSTREAM}/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://core:8787/;
|
||||
# The trailing slash replaces the matched /api/ prefix before the private hop.
|
||||
proxy_pass ${THT_FRONTEND_API_UPSTREAM}/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@@ -27,7 +23,7 @@ server {
|
||||
proxy_set_header X-Authenticated-User $http_x_authenticated_user;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 1h;
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
|
||||
location / {
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
corpus=/etc/thothii/backend-url-cases.json
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
jq -c '.[]' "$corpus" | while IFS= read -r case_json; do
|
||||
value=$(printf '%s' "$case_json" | jq -r '.value')
|
||||
valid=$(printf '%s' "$case_json" | jq -r '.valid')
|
||||
if BACKEND_BASE_URL="$value" /usr/local/bin/frontend-entrypoint true \
|
||||
>/dev/null 2>&1; then
|
||||
actual=true
|
||||
else
|
||||
actual=false
|
||||
fi
|
||||
if [ "$actual" != "$valid" ]; then
|
||||
echo "entrypoint policy mismatch for BACKEND_BASE_URL=$value: expected $valid" >&2
|
||||
nginx_config=docker/nginx.conf.template
|
||||
for setting in \
|
||||
'proxy_pass ${THT_FRONTEND_API_UPSTREAM}/;' \
|
||||
'proxy_http_version 1.1;' \
|
||||
'proxy_buffering off;' \
|
||||
'proxy_read_timeout 3600s;'; do
|
||||
if ! grep -Fq "$setting" "$nginx_config"; then
|
||||
echo "missing required nginx API/SSE setting: $setting" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "frontend entrypoint canonical URL corpus: ok"
|
||||
if ! grep -Fqx 'THT_FRONTEND_API_UPSTREAM=${THT_FRONTEND_API_UPSTREAM:-http://core:8787}' \
|
||||
docker/frontend-entrypoint.sh; then
|
||||
echo "frontend entrypoint is missing the private core default" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -Fq "envsubst '\${THT_FRONTEND_API_UPSTREAM}'" docker/frontend-entrypoint.sh; then
|
||||
echo "frontend entrypoint does not render the private upstream" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if rg -n 'BACKEND_BASE_URL|VITE_BACKEND_URL' docker/frontend-entrypoint.sh docker/nginx.conf.template; then
|
||||
echo "frontend runtime routing still accepts a browser-facing backend URL" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "frontend same-origin proxy policy: ok"
|
||||
|
||||
@@ -4,27 +4,5 @@ set -eu
|
||||
value=${1-}
|
||||
policy_file=${BACKEND_URL_POLICY_FILE:-/etc/thothii/backend-url-policy.json}
|
||||
|
||||
if jq -e --arg value "$value" '.relativeBases | index($value) != null' \
|
||||
"$policy_file" >/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! jq -e --arg value "$value" \
|
||||
'.absolutePattern as $pattern | $value | test($pattern)' \
|
||||
"$policy_file" >/dev/null; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
authority=${value#*://}
|
||||
authority=${authority%%/*}
|
||||
port=""
|
||||
case "$authority" in
|
||||
*]:*) port=${authority##*:} ;;
|
||||
*]) ;;
|
||||
*:*) port=${authority##*:} ;;
|
||||
esac
|
||||
|
||||
if [ -n "$port" ]; then
|
||||
max_port=$(jq -r '.maxPort' "$policy_file")
|
||||
if [ "${#port}" -gt 5 ] || [ "$port" -gt "$max_port" ]; then exit 2; fi
|
||||
fi
|
||||
jq -e --arg value "$value" '.relativeBases | index($value) != null' \
|
||||
"$policy_file" >/dev/null
|
||||
|
||||
Reference in New Issue
Block a user