diff --git a/.gitea/workflows/ci-deploy.yml b/.gitea/workflows/ci-deploy.yml index fdcb5039..9f2fe334 100644 --- a/.gitea/workflows/ci-deploy.yml +++ b/.gitea/workflows/ci-deploy.yml @@ -110,6 +110,29 @@ jobs: curl -k -sSI -u "$BASIC_AUTH_USER:$BASIC_AUTH_PASS" \ https://ui-dashboard.gnerim.ru/api/dictionary/1/world_regions | grep -iE "^HTTP|x-cache|x-envoy" + - name: Pre-warm /api cache (dictionaries shared across e2e specs) + id: cache_warmup + env: + BASIC_AUTH_USER: ${{ secrets.BASIC_AUTH_USER }} + BASIC_AUTH_PASS: ${{ secrets.BASIC_AUTH_PASS }} + run: | + # The four dictionary endpoints (see src/shared/dictionaries/api.ts) + # are read by every page load — fetch them once before e2e to warm + # nginx's proxy_cache. Subsequent e2e fetches hit the cache instead + # of the upstream WAF, which has a low per-source-IP rate limit. + # Brief sleep between requests to avoid tripping the WAF on the + # cold-cache pass. + for path in world_regions countries cities airports; do + url="https://ui-dashboard.gnerim.ru/api/dictionary/1/${path}" + rc=$(curl -k -sS -u "$BASIC_AUTH_USER:$BASIC_AUTH_PASS" -o /dev/null -w "%{http_code}" "$url") + echo "warm $path -> HTTP $rc" + sleep 2 + done + echo "--- verify cache HIT on a re-fetch ---" + curl -k -sSI -u "$BASIC_AUTH_USER:$BASIC_AUTH_PASS" \ + https://ui-dashboard.gnerim.ru/api/dictionary/1/cities \ + | grep -iE "^HTTP|x-cache-status" + - name: Install Playwright browsers id: playwright_install run: pnpm exec playwright install --with-deps chromium diff --git a/deployment/nginx/ui-dashboard.gnerim.ru.conf b/deployment/nginx/ui-dashboard.gnerim.ru.conf index 493aaebe..413be9f9 100644 --- a/deployment/nginx/ui-dashboard.gnerim.ru.conf +++ b/deployment/nginx/ui-dashboard.gnerim.ru.conf @@ -43,6 +43,27 @@ server { # # Cached to absorb e2e bursts that would otherwise trip the upstream # WAF rate limit. Only GET/HEAD are cached (default proxy_cache_methods). + # + # Dictionary endpoints (cities, airports, countries, world_regions) are + # essentially static — pre-warmed by CI and held for 6h. Other /api/* + # paths are dynamic queries; 1m is a reasonable freshness budget. + location /api/dictionary/ { + auth_basic off; + proxy_pass https://127.0.0.1:8443; + proxy_set_header Host flights.test.aeroflot.ru; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_ssl_server_name on; + proxy_ssl_name flights.test.aeroflot.ru; + + proxy_cache flights_api; + proxy_cache_key "$scheme$host$request_uri"; + proxy_cache_valid 200 6h; + proxy_cache_valid 404 5m; + proxy_cache_lock on; + proxy_cache_use_stale error timeout updating http_403 http_500 http_502 http_503 http_504; + add_header X-Cache-Status $upstream_cache_status always; + } + location /api/ { auth_basic off; proxy_pass https://127.0.0.1:8443;