# Caddy на VPS (186.246.51.17) — единственная публичная точка входа. # Два домена, ОБА с твоими сертификатами; проксируют в SSH-туннели: # # forbiddenstars.ru → 127.0.0.1:9000 ← Pi (autossh, постоянно) PROD # forbidden-stars.ru → 127.0.0.1:9001 ← ПК (по требованию) DEV/TEST # # Caddy сам терминирует TLS (он и есть edge: видит реального клиента), а вниз к # приложению добавляет X-Forwarded-Proto=https / X-Forwarded-For / Host — # приложение это учитывает (uvicorn --proxy-headers). Положи файл в /etc/caddy/Caddyfile. # Сертификаты — см. deploy/vps/README.md (fullchain = leaf + промежуточные одним файлом). # # SSE (/api/events): отдельный handle БЕЗ encode и с flush_interval -1 — иначе сжатие/ # буферизация задержат поток событий (реал-тайм перестанет работать). # # Туннель лёг (апстрим недоступен) → reverse_proxy отдаёт 502/503/504, и сниппет # (offline) подменяет ответ на свою страницу-заглушку (/etc/caddy/maintenance/maintenance.html), # вместо «зависшей загрузки» SPA. Файл заглушки — deploy/vps/maintenance.html. # Заглушка «сайт недоступен» при недоступности апстрима. Импортируется в каждый сайт. (offline) { handle_errors { @down expression `{http.error.status_code} in [502, 503, 504]` handle @down { root * /etc/caddy/maintenance rewrite * /maintenance.html header Cache-Control "no-store" file_server { status 503 } } } } forbiddenstars.ru { tls /etc/caddy/certs/forbiddenstars.ru/fullchain.pem /etc/caddy/certs/forbiddenstars.ru/privkey.pem @sse path /api/events handle @sse { reverse_proxy 127.0.0.1:9000 { flush_interval -1 } } handle { encode zstd gzip reverse_proxy 127.0.0.1:9000 } import offline } forbidden-stars.ru { tls /etc/caddy/certs/forbidden-stars.ru/fullchain.pem /etc/caddy/certs/forbidden-stars.ru/privkey.pem @sse path /api/events handle @sse { reverse_proxy 127.0.0.1:9001 { flush_interval -1 } } handle { encode zstd gzip reverse_proxy 127.0.0.1:9001 } import offline }