Changeset 0.22.6 (#148)

This commit is contained in:
2026-03-03 13:12:13 +00:00
parent 45fe965c32
commit d8e0664fa3
24 changed files with 983 additions and 2473 deletions

View File

@@ -2,16 +2,23 @@
# ============================================
# Chat Switchboard - Frontend Entrypoint
# ============================================
# Injects BASE_PATH into index.html and nginx
# config at container startup. Supports path-
# based multi-env deployment on a single domain.
# nginx serves static assets and proxies page
# routes to the Go backend for template rendering.
# API/WS routes are handled by Ingress directly.
#
# Environment:
# BASE_PATH - URL prefix (e.g. "/dev", "/test", or "")
# Environment (required):
# BACKEND_URL - Backend service URL
# (e.g. "http://switchboard-be:8080")
#
# Environment (optional):
# BASE_PATH - URL prefix (e.g. "/dev", "" for root)
# ENVIRONMENT - Environment name for logging
# ============================================
set -e
BASE_PATH="${BASE_PATH:-}"
BACKEND_URL="${BACKEND_URL:?BACKEND_URL is required (e.g. http://switchboard-be:8080)}"
ENVIRONMENT="${ENVIRONMENT:-production}"
# Read version from VERSION file (baked in at build time)
APP_VERSION="dev"
@@ -19,39 +26,16 @@ if [ -f /VERSION ]; then
APP_VERSION=$(cat /VERSION | tr -d '[:space:]')
fi
# Compute base href (needs trailing slash for <base> tag)
if [ -z "${BASE_PATH}" ]; then
BASE_HREF="/"
else
BASE_HREF="${BASE_PATH}/"
fi
# ── Read branding config ────────────────────
BRANDING_JSON="{}"
if [ -f /branding/branding.json ]; then
# Compact to single line for safe sed injection
BRANDING_JSON=$(tr -d '\n' < /branding/branding.json | sed 's/ */ /g')
echo "✅ Branding config loaded from /branding/branding.json"
else
echo " No branding mount — using defaults"
fi
# ── Read environment name ─────────────────
ENVIRONMENT="${ENVIRONMENT:-production}"
# Compute build hash from frontend file contents (changes on every deploy)
# Compute build hash from JS file contents (unique per deploy)
BUILD_HASH=$(find /usr/share/nginx/html/js -name '*.js' -exec md5sum {} + 2>/dev/null | sort | md5sum | cut -c1-8)
if [ -z "${BUILD_HASH}" ]; then
BUILD_HASH=$(date +%s | md5sum | cut -c1-8)
fi
# ── Inject into index.html ──────────────────
# ── Inject into index.html (redirect page) ──
sed -i \
-e "s|%%BASE_PATH%%|${BASE_PATH}|g" \
-e "s|%%BASE_HREF%%|${BASE_HREF}|g" \
-e "s|%%APP_VERSION%%|${APP_VERSION}|g" \
-e "s|%%ENVIRONMENT%%|${ENVIRONMENT}|g" \
-e "s|%%BRANDING_JSON%%|${BRANDING_JSON}|g" \
/usr/share/nginx/html/index.html
# Inject version and build hash into service worker
@@ -60,13 +44,45 @@ sed -i \
-e "s|%%BUILD_HASH%%|${BUILD_HASH}|g" \
/usr/share/nginx/html/sw.js
echo "✅ Frontend configured: BASE_PATH=${BASE_PATH:-/} VERSION=${APP_VERSION} BUILD=${BUILD_HASH} ENV=${ENVIRONMENT}"
echo "✅ Frontend: BASE_PATH=${BASE_PATH:-/} VERSION=${APP_VERSION} BUILD=${BUILD_HASH} BACKEND=${BACKEND_URL}"
# ── Page route proxy blocks ─────────────────
page_proxy_block() {
local path="$1"
cat <<PROXY
location ${path} {
proxy_pass ${BACKEND_URL};
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
PROXY
}
PAGE_ROUTES=$(cat <<ROUTES
# ── Page routes → backend ────────────────
$(page_proxy_block "= ${BASE_PATH}/")
$(page_proxy_block "= ${BASE_PATH}/login")
location ~ ^${BASE_PATH}/chat/[^/]+\$ {
proxy_pass ${BACKEND_URL};
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
$(page_proxy_block "${BASE_PATH}/admin")
$(page_proxy_block "${BASE_PATH}/editor")
$(page_proxy_block "${BASE_PATH}/notes")
$(page_proxy_block "${BASE_PATH}/settings")
ROUTES
)
# ── Generate nginx config ───────────────────
# If BASE_PATH is set, serve under that prefix with alias.
# If empty, serve from root (default behavior).
if [ -z "${BASE_PATH}" ]; then
cat > /etc/nginx/conf.d/default.conf <<'NGINX'
cat > /etc/nginx/conf.d/default.conf <<NGINX
server {
listen 80;
server_name _;
@@ -84,25 +100,24 @@ server {
add_header Cache-Control "public, immutable";
}
# Service worker must never be cached by the browser
location = /sw.js {
expires off;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# Branding assets — served from volume mount, 404s gracefully
location /branding/ {
alias /branding/;
expires 1h;
add_header Cache-Control "public";
try_files $uri =404;
try_files \$uri =404;
}
${PAGE_ROUTES}
location / {
try_files $uri $uri/ /index.html;
try_files \$uri \$uri/ /index.html;
}
# Health check for k8s probes
location = /healthz {
return 200 'ok';
add_header Content-Type text/plain;
@@ -125,6 +140,8 @@ server {
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
${PAGE_ROUTES}
location ${BASE_PATH}/ {
alias /usr/share/nginx/html/;
try_files \$uri \$uri/ ${BASE_PATH}/index.html;
@@ -134,7 +151,6 @@ server {
add_header Cache-Control "public, immutable";
}
# Service worker must never be cached by the browser
location = ${BASE_PATH}/sw.js {
alias /usr/share/nginx/html/sw.js;
expires off;
@@ -142,7 +158,6 @@ server {
}
}
# Branding assets — under BASE_PATH so Traefik routes them here
location ${BASE_PATH}/branding/ {
alias /branding/;
expires 1h;
@@ -150,12 +165,10 @@ server {
try_files \$uri =404;
}
# Redirect bare path to trailing slash
location = ${BASE_PATH} {
return 301 ${BASE_PATH}/;
}
# Health check for k8s probes (always at root)
location = /healthz {
return 200 'ok';
add_header Content-Type text/plain;
@@ -168,5 +181,4 @@ server {
NGINX
fi
# ── Start nginx ─────────────────────────────
exec nginx -g 'daemon off;'