# Развёртывание Flamy Trade Документ описывает production-развёртывание сайта `trade.flamy.studio` без ML-runtime и будущего API. Эти части подключаются отдельным этапом. ## Схема ```text Internet -> Router :80/:443 -> edge-proxy VM / Caddy -> 10.20.0.20:18082 -> flamy_trade-nginx-1 -> flamy_trade-app-1:3000 ``` Compose project name зафиксирован как `flamy_trade`. `container_name` не задаётся, чтобы не ломать стандартное управление Compose. ## Переменные Скопируйте `.env.example` в `.env` на сервере и проверьте значения: ```bash PUBLIC_SITE_URL=https://trade.flamy.studio PUBLIC_SITE_NAME=Flamy Trade PUBLIC_SITE_DESCRIPTION=Публичная витрина ML-прогнозов для крипторынка. PUBLIC_DASHBOARD_DATA_MODE=research-static HOST=0.0.0.0 PORT=3000 ORIGIN=https://trade.flamy.studio BODY_SIZE_LIMIT=1M SHUTDOWN_TIMEOUT=15 APP_STOP_GRACE_PERIOD=20s APP_IMAGE_REPOSITORY=registry.example.com/flamy-trade APP_IMAGE_TAG= BUILD_DATE= VCS_REF= APP_HOST_IP=10.20.0.20 APP_PUBLISHED_PORT=18082 NGINX_IMAGE=nginxinc/nginx-unprivileged:1.30.4-alpine-perl@sha256:36fa38fb8f34faba45e7e1857644d6561f2faf45e7ce6b15e97d1a2a1682a0cd ``` Перед фиксацией порта проверьте, что `18082` свободен: ```bash sudo ss -lntup | grep ':18082' docker ps --format 'table {{.Names}}\t{{.Ports}}' ``` ## Проверки перед сборкой ```bash pnpm install --frozen-lockfile pnpm format:check pnpm lint pnpm check pnpm build pnpm audit docker compose config ``` ## Сборка Перед сборкой image задайте provenance: ```bash export BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" export VCS_REF="$(git rev-parse HEAD)" export APP_IMAGE_TAG="$VCS_REF" ``` Сборка и запуск: ```bash docker compose build --pull docker compose up -d --wait docker compose ps docker compose logs --tail=200 ``` ## Caddy Блок для edge-proxy: ```caddyfile trade.flamy.studio { encode zstd gzip reverse_proxy 10.20.0.20:18082 { header_up Host {host} header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} header_up X-Forwarded-Proto {scheme} } } ``` Перед reload: ```bash sudo cp -a /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak.$(date +%F_%H%M%S) sudo caddy validate --config /etc/caddy/Caddyfile sudo systemctl reload caddy ``` ## Smoke-check С Docker-runtime: ```bash curl -fsS http://10.20.0.20:18082/healthz curl -fsSI http://10.20.0.20:18082/ curl -fsSI http://10.20.0.20:18082/robots.txt curl -fsSI http://10.20.0.20:18082/sitemap.xml ``` Снаружи: ```bash curl -fsSI https://trade.flamy.studio/ curl -fsS https://trade.flamy.studio/healthz curl -fsS https://trade.flamy.studio/robots.txt curl -fsS https://trade.flamy.studio/sitemap.xml ``` Маршруты: ```text / 200 /about 200 /blog 200 /blog/osnovy-upravleniya-riskami 200 /dashboard 200 /healthz 200 /favicon.svg 200 /robots.txt 200 /sitemap.xml 200 /nesuschestvuyuschiy-url 404 ``` Проверка headers: ```bash curl -fsSI https://trade.flamy.studio/ curl -fsSI https://trade.flamy.studio/favicon.svg curl -fsSI https://trade.flamy.studio/_app/immutable/.js ``` Ожидаемо: ```text X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin X-Frame-Options: SAMEORIGIN Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=() ``` `Content-Security-Policy` задаётся SvelteKit и ожидается на HTML-ответах приложения. Для `/_app/immutable/*` ожидается: ```text Cache-Control: public, max-age=31536000, immutable ``` Для HTML: ```text Cache-Control: no-cache ``` ## Rollback Надёжный rollback должен опираться на immutable image tag: ```text ${APP_IMAGE_REPOSITORY}: ``` На production-сервере меняется только `APP_IMAGE_TAG`, затем выполняется: ```bash docker compose pull app docker compose up -d --no-build --wait ``` Если проблема в маршрутизации, восстановите backup Caddyfile и выполните: ```bash sudo systemctl reload caddy ``` Предыдущий image не удаляется до успешного smoke-check нового релиза.