All checks were successful
Deploy / changes (push) Successful in 8s
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-browser (push) Successful in 35s
Deploy Trade-In / build-frontend (push) Successful in 38s
Deploy / build-worker (push) Successful in 42s
Deploy / build-backend (push) Successful in 43s
Deploy / build-frontend (push) Successful in 42s
Deploy / deploy (push) Successful in 1m32s
Deploy Trade-In / test (push) Successful in 1m50s
Deploy Trade-In / build-backend (push) Successful in 52s
Deploy Trade-In / deploy (push) Successful in 51s
229 lines
10 KiB
Caddyfile
229 lines
10 KiB
Caddyfile
# Caddy config.
|
||
#
|
||
# - gendsgn.ru — main production site, auto-TLS via Let's Encrypt.
|
||
# - www.gendsgn.ru — 301 redirect to apex (canonical URL).
|
||
# - :80 — fallback for raw IP access. Closed by same auth gate (pilot phase).
|
||
#
|
||
# `handle /api/*` (NOT `handle_path`) — we keep the /api prefix because
|
||
# the FastAPI router is mounted at /api/v1/*.
|
||
#
|
||
# Auth gate: basic_auth for pilot phase (2026-05-23).
|
||
# Users managed via caddy/users.caddy.snippet (git history = audit trail).
|
||
# Public exclusions: /health (liveness probe), /preview/* (static mockups).
|
||
#
|
||
# IMPORTANT: route { } block is required to preserve directive order.
|
||
# Without route { }, Caddy executes directives in hard-coded default order
|
||
# (basic_auth runs before handle), making /health and /preview/* exclusions
|
||
# ineffective. With route { }, handlers are matched top-to-bottom as written.
|
||
|
||
# #2213: global `log_credentials` УБРАН. Он заставлял Caddy писать raw
|
||
# Authorization header (Base64 `user:password`, тривиально обратимый) в ОБА лог-файла
|
||
# (gendsgn.ru.log + auth_audit.log). Даже при root-only доступе и коротком retention
|
||
# это plaintext-пароли пилотов на диске — недопустимый риск против скромной выгоды.
|
||
# Trade-off: glitchtip-auth-forwarder больше не извлекает `attempted_username` из 401
|
||
# (тег деградирует в "(none)" — forwarder это уже обрабатывает gracefully, не падает).
|
||
# Событие basic_auth 401 (remote_ip / uri / method) по-прежнему уходит в GlitchTip.
|
||
|
||
gendsgn.ru {
|
||
encode zstd gzip
|
||
|
||
log {
|
||
output file /var/log/caddy/gendsgn.ru.log {
|
||
roll_size 50MiB
|
||
roll_keep 5
|
||
roll_keep_for 720h
|
||
}
|
||
format json
|
||
}
|
||
|
||
# Отдельный лог только для auth-событий.
|
||
# Forwarder (ops/glitchtip-auth-forwarder) читает именно этот файл.
|
||
# Retention 7 дней (меньше чем main log) — содержит plain Base64 credentials.
|
||
log auth_audit {
|
||
output file /var/log/caddy/auth_audit.log {
|
||
roll_size 10MiB
|
||
roll_keep 3
|
||
roll_keep_for 168h
|
||
}
|
||
format json
|
||
}
|
||
|
||
route {
|
||
# /health и /preview/* — public, без auth, short-circuit.
|
||
handle /health {
|
||
reverse_proxy backend:8000
|
||
}
|
||
|
||
# Static HTML mockups для review (audit alternatives).
|
||
# Public access — без auth (по запросу 2026-05-17).
|
||
handle_path /preview/* {
|
||
root * /srv/preview
|
||
file_server browse
|
||
}
|
||
|
||
# Trade-In UI preview — public CI surface (#801). Рендерит mock-фикстуру
|
||
# «денежного экрана» без бэкенда → axe/lighthouse гоняются без креды.
|
||
# Реальных клиентских данных нет (статичная фикстура) → безопасно публично.
|
||
# ДО auth-import: route матчит сверху вниз, handle short-circuit'ит.
|
||
# Без strip — Next.js basePath=/trade-in ждёт префикс в URL (как @tradein).
|
||
# ui-preview + его статика (_next/static — CSS/JS бандлы, без секретов).
|
||
# Оба ДО auth-import, иначе ассеты страницы уходят в @tradein (под auth) → 401 → без CSS.
|
||
@uipreview path /trade-in/ui-preview/* /trade-in/_next/static/*
|
||
handle @uipreview {
|
||
reverse_proxy tradein-frontend:3000
|
||
}
|
||
|
||
# Auth gate (applies to all routes below within this route block).
|
||
import caddy/users.caddy.snippet
|
||
|
||
# Trade-In MVP subproject (tradein-mvp/) — gendesign-tradein docker stack,
|
||
# подключен через gendesign_shared network. Routes ДО универсального handle
|
||
# потому что Caddy матчит handle-блоки сверху вниз.
|
||
handle /trade-in/api/* {
|
||
# `handle_path /trade-in/api/*` стрипал бы целиком /trade-in/api;
|
||
# FastAPI router замаунтен на /api/v1/trade-in/* — нужен strip только
|
||
# префикса basePath /trade-in (Next.js basePath leak).
|
||
uri strip_prefix /trade-in
|
||
reverse_proxy tradein-backend:8000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
# #2213 defense-in-depth: общий секрет Caddy↔tradein-backend. header_up
|
||
# с value ПЕРЕЗАПИСЫВАЕТ (стирает) любой клиентский X-Internal-Auth-Secret —
|
||
# тот же механизм, что защищает X-Authenticated-User выше. Пусто пока
|
||
# TRADEIN_INTERNAL_AUTH_SECRET не задан в .env (fail-open, backend не проверяет).
|
||
header_up X-Internal-Auth-Secret {env.TRADEIN_INTERNAL_AUTH_SECRET}
|
||
}
|
||
}
|
||
|
||
# gendsgn.ru/sale-share — короткий адрес standalone-продукта «Поиск домов».
|
||
# Next basePath=/trade-in → редиректим на канонический /trade-in/sale-share
|
||
# (тот же tradein-frontend контейнер; query-string сохраняется). True vanity-URL
|
||
# в адресной строке требует отдельного Next-app с basePath=/sale-share.
|
||
@saleshare path /sale-share /sale-share/
|
||
handle @saleshare {
|
||
redir /trade-in/sale-share permanent
|
||
}
|
||
|
||
# Matcher `path /trade-in /trade-in/*` ловит И /trade-in (без слеша),
|
||
# И /trade-in/ + /trade-in/anything. Без обоих случаев `handle /trade-in/*`
|
||
# пропускал /trade-in без слеша → попадал в общий frontend → пустой ответ.
|
||
@tradein path /trade-in /trade-in/*
|
||
handle @tradein {
|
||
# Next.js basePath=/trade-in — фронт сам ждёт префикса в URL
|
||
reverse_proxy tradein-frontend:3000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
# #2213: симметрично с /trade-in/api/* — перезаписываем секрет из env
|
||
# (стирает клиентский), на случай SSR-forwardʼa фронтом в backend.
|
||
header_up X-Internal-Auth-Secret {env.TRADEIN_INTERNAL_AUTH_SECRET}
|
||
}
|
||
}
|
||
|
||
handle /api/* {
|
||
reverse_proxy backend:8000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
}
|
||
}
|
||
|
||
handle {
|
||
reverse_proxy frontend:3000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
www.gendsgn.ru {
|
||
redir https://gendsgn.ru{uri} permanent
|
||
}
|
||
|
||
# Obsidian Self-hosted LiveSync (CouchDB backend).
|
||
# Auto-TLS Let's Encrypt. CORS уже включён на стороне CouchDB через bootstrap
|
||
# (см. scripts/setup-couchdb.sh). Basic-auth — на стороне CouchDB (admin user).
|
||
#
|
||
# DNS: A-record obsidian.gendsgn.ru → IP VPS.
|
||
# Клиенты Obsidian + Self-hosted LiveSync plugin указывают на этот URL.
|
||
obsidian.gendsgn.ru {
|
||
encode zstd gzip
|
||
|
||
reverse_proxy couchdb:5984 {
|
||
# Большие документы (vault attachments / images) — увеличиваем timeout
|
||
transport http {
|
||
response_header_timeout 120s
|
||
}
|
||
}
|
||
}
|
||
|
||
# GlitchTip — self-hosted error tracking (Sentry-compatible).
|
||
# DNS: A-record errors.gendsgn.ru → IP VPS.
|
||
errors.gendsgn.ru {
|
||
encode zstd gzip
|
||
|
||
reverse_proxy glitchtip-web:8080
|
||
|
||
log {
|
||
output file /var/log/caddy/errors.gendsgn.ru.log
|
||
}
|
||
}
|
||
|
||
# Uptime Kuma — self-hosted uptime monitoring + public status page (#75 B6-1).
|
||
# DNS: A-record status.gendsgn.ru → IP VPS (добавить перед деплоем стека).
|
||
# Контейнер из docker-compose.uptime.yml (project gendesign-uptime) на shared
|
||
# gendesign_shared network. Если стек не запущен — Caddy отдаёт 502 ТОЛЬКО на
|
||
# этом домене, main-сайт не страдает (как obsidian.gendsgn.ru).
|
||
#
|
||
# ВНИМАНИЕ: status-page НАМЕРЕННО публичен (trust-building для пилотов, issue #75).
|
||
# Admin-панель Kuma (/dashboard, /manage-*) защищена собственным логином Kuma —
|
||
# НЕ кладём её за caddy/users.caddy.snippet, иначе double-auth сломает setup.
|
||
status.gendsgn.ru {
|
||
encode zstd gzip
|
||
|
||
reverse_proxy uptime-kuma:3001
|
||
|
||
log {
|
||
output file /var/log/caddy/status.gendsgn.ru.log
|
||
}
|
||
}
|
||
|
||
# Forgejo — self-hosted git (migration 2026-05-16).
|
||
# DNS: A-record git.gendsgn.ru → IP VPS.
|
||
# Forgejo container из forgejo-migration/docker-compose.yml на shared
|
||
# gendesign_default network. HTTP port 3000 (default Forgejo).
|
||
# Был добавлен вручную при migration, потерян при первом auto-deploy после
|
||
# изменения Caddyfile (deploy.yml делает git reset --hard). См. fix issue.
|
||
git.gendsgn.ru {
|
||
encode zstd gzip
|
||
|
||
reverse_proxy forgejo:3000
|
||
|
||
log {
|
||
output file /var/log/caddy/git.gendsgn.ru.log
|
||
}
|
||
}
|
||
|
||
# Plain HTTP by IP — closed by same auth gate (prevent bypass via direct IP / SSH tunnel).
|
||
# Caddy issues no TLS here (no hostname). /health remains public.
|
||
:80 {
|
||
encode zstd gzip
|
||
|
||
route {
|
||
# /health — public, без auth (GHA deploy smoke check, liveness probe).
|
||
handle /health {
|
||
reverse_proxy backend:8000
|
||
}
|
||
|
||
# Auth gate (same snippet as gendsgn.ru).
|
||
import caddy/users.caddy.snippet
|
||
|
||
handle /api/* {
|
||
reverse_proxy backend:8000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
}
|
||
}
|
||
|
||
handle {
|
||
reverse_proxy frontend:3000 {
|
||
header_up X-Authenticated-User {http.auth.user.id}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
# Test deploy flow 2026-05-15T21:43:32Z
|