feat(cache): single-owner caching across nginx, edge and content edits
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / check (push) Successful in 28s
CI / tests-unit (push) Successful in 1m39s
CI / tests-integration (push) Successful in 1m41s
CI / tests-ui (push) Successful in 2m27s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 3m35s

Rebuild production nginx from the repo (deployment/proxy/*) with a single
Cache-Control owner per route: the app stays the source, nginx only manages
headers, and Cloudflare stores the public API allowlist at the edge.

- deployment/proxy: nginx.conf, mime.types, nginx-cms.conf and the
  blue/green upstream snippet; config backed by scripts/nginx-sync.sh
  (idempotent install + reload, --check/--force).
- nginx serves Cache-Tag headers on the public allowlist (cms-public),
  gamedata, client and camera responses so the edge and purge stay in sync.
- src/lib/edge-cache.ts + tests: coalesced, fire-and-forget edge purges that
  no-op unless Cloudflare is configured; scripts/cf-purge.sh and
  cf-setup-cache.sh create and purge the cache rule.
- src/lib/cloudflare-api.ts: purgeCacheByTags/purgeCacheByUrls.
- Purge hooks after catalog exports (public + gamedata) and on shop, team,
  guild, photo and rare-values edits; ci-deploy purges after each release.
- src/proxy.ts excludes the imaging/images docs from the middleware matcher.
This commit is contained in:
openhands committed 2026-09-28 21:55:18 +02:00
1 parent 30dcecd530
commit 7697728d07
18 files changed
+1001 -1

No files matched your search

+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env bash
# Sync the nginx config from this repository to /etc/nginx and reload it.
#
# Background: on 2026-09-26 /etc/nginx and /var/log/nginx disappeared from the
# host while nginx kept serving its in-memory config; any restart would have
# taken the CMS down. This script makes the repo the source of truth so that
# cannot happen again. It is idempotent and only reloads nginx when the config
# actually changed.
#
# Usage:
# sudo scripts/nginx-sync.sh # install + test + reload if changed
# sudo scripts/nginx-sync.sh --force # always reload after a passing test
# scripts/nginx-sync.sh --check # just diff repo vs live, no writes
#
# Files installed (see also deployment/proxy/):
# nginx.conf -> /etc/nginx/nginx.conf
# nginx-mime.types -> /etc/nginx/mime.types
# nginx-cms.conf -> /etc/nginx/sites-available/cms.conf
# cms_upstream_servers.conf -> /etc/nginx/snippets/cms_upstream_servers.conf
# symlink sites-enabled/cms.conf -> ../sites-available/cms.conf
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROXY_DIR="$SCRIPT_DIR/../deployment/proxy"
NGINX_DIR=/etc/nginx
BACKUP_DIR="/var/backups/nginx-$(date +%Y%m%d-%H%M%S)"
MODE="sync"
for arg in "$@"; do
case "$arg" in
--force) MODE="force" ;;
--check) MODE="check" ;;
esac
done
install_file() {
local src="$1" dst="$2"
if [[ ! -f "$src" ]]; then
echo "error: $src not found in repo" >&2
exit 1
fi
if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then
echo "= $dst up to date"
return 1
fi
if [[ "$MODE" == "check" ]]; then
echo "- $dst differs from repo"
return 0
fi
mkdir -p "$(dirname "$dst")"
if [[ -f "$dst" ]]; then
mkdir -p "$BACKUP_DIR"
cp -a "$dst" "$BACKUP_DIR/"
fi
cp -a "$src" "$dst"
echo "+ installed $dst"
return 0
}
if [[ "$MODE" != "check" && "$(id -u)" -ne 0 ]]; then
echo "error: run as root (sudo scripts/nginx-sync.sh)" >&2
exit 1
fi
changed=0
if install_file "$PROXY_DIR/nginx.conf" "$NGINX_DIR/nginx.conf"; then changed=1; fi
if install_file "$PROXY_DIR/nginx-mime.types" "$NGINX_DIR/mime.types"; then changed=1; fi
if install_file "$PROXY_DIR/nginx-cms.conf" "$NGINX_DIR/sites-available/cms.conf"; then changed=1; fi
if install_file "$PROXY_DIR/cms_upstream_servers.conf" "$NGINX_DIR/snippets/cms_upstream_servers.conf"; then changed=1; fi
if [[ ! -f "$NGINX_DIR/sites-enabled/cms.conf" ]]; then
if [[ "$MODE" == "check" ]]; then
echo "- sites-enabled/cms.conf missing"
changed=1
else
ln -sf ../sites-available/cms.conf "$NGINX_DIR/sites-enabled/cms.conf"
echo "+ linked sites-enabled/cms.conf"
changed=1
fi
fi
if [[ "$MODE" == "check" ]]; then
[[ "$changed" -eq 0 ]]
exit
fi
if [[ "$MODE" == "force" ]]; then
changed=1
fi
if [[ ! -d /var/log/nginx ]]; then
install -d -o root -g adm -m 750 /var/log/nginx
fi
for f in /var/log/nginx/access.log /var/log/nginx/error.log; do
[[ -f "$f" ]] || touch "$f"
done
echo "--- nginx -t ---"
nginx -t
if [[ "$changed" -eq 1 ]]; then
echo "--- reloading nginx ---"
nginx -s reload
else
echo "no changes; nginx reload skipped"
fi
echo "--- health check ---"
curl -sf "http://127.0.0.1:3002/api/health" > /dev/null && echo "OK: CMS reachable"
curl -skf -o /dev/null -H "Host: epicnabbo.nl" "https://127.0.0.1:9443/health" && echo "OK: nginx :9443 /health"