Skip to content

Nasazení

Tahle stránka popisuje, jak se projekt staví a nasazuje — lokálně, na test a na produkci.

Přehled architektury

Systém se skládá ze čtyř kontejnerů + jednoho samostatného static buildu:

KomponentaCo to jeKde se buildí
atrea-user-apiAPI (/api) + Swagger (/api/docs) + docs (/docs). Panel NENÍ uvnitř.deploy.sh na DEV → push do hub.atrea.name
atrea-user-panelAdmin panel (Vue SPA), servírovaný statickybuild.sh přímo na serveru
atrea-zitadel-loginZitadel Login v2 UI (Next.js)image z hub.atrea.name
atrea-zitadelZitadel server (OIDC/SAML IdP)oficiální image ghcr.io/zitadel/zitadel
atrea-postgresJeden PostgreSQL se dvěma databázemi: atrea-user-db + zitadelpostgres:16-alpine

Klíčové principy:

  • Image je env-agnostický. Tentýž atrea-user-api image běží na prod i testu — prostředí vybírá NODE_ENV (→ config/production.yaml / config/test.yaml).
  • Panel se vydává samostatně (často, nezávisle na API), buduje se na serveru z repa atrea-user-panel.
  • Panel ↔ API jsou cross-origin (každý na své doméně). CORS je v API otevřený, takže to funguje bez whitelistu.
  • TLS terminuje Caddy na proxy serveru; kontejnery jedou v HTTP.

Domény

Produkce (amotion.cloud)

DoménaCíl
zitadel.auth.amotion.cloudZitadel server (issuer / mgmt API / konzole)
auth.amotion.cloudLogin v2 UI
api.auth.amotion.cloudAPI /api + Swagger /api/docs + docs /docs
admin.auth.amotion.cloudAdmin panel (static)
test.admin.auth.amotion.cloudAdmin panel — test-production build (test FE proti ostrému prod backendu)

Test (kagb.cloud)

DoménaCíl
admin.kagb.cloudZitadel server
login.kagb.cloudLogin v2 UI
api.user.kagb.cloudAPI + Swagger + docs
panel.kagb.cloudAdmin panel (static)

1) Lokální vývoj

docker-compose.yml postaví celý stack lokálně z buildů (ne z hubu) — vlastní postgres (app/app, auto-init z sql/), API, panel i login s build kontexty.

bash
./build.sh            # git pull 3 repa + docker compose up --build
  • API: localhost:3001, Postgres: localhost:5434, Zitadel: localhost:3000/8080
  • NODE_ENV=development → padá na config/default.yaml
  • Panel pro lokální vývoj: npm run dev v repu atrea-user-panel (Vite na :5173, proxy /apilocalhost:3001)

2) API image — build & push (DEV stroj)

API image se staví a pushuje skriptem deploy.sh na vývojářském stroji:

bash
# 1) bump verze (vytvoří git tag vX.Y.Z)
npm version patch        # nebo minor / major

# 2) build + push do hub.atrea.name
./deploy.sh

Co deploy.sh dělá:

  • vyžaduje čistý git tree;
  • docker buildx cross-build pro linux/amd64 (servery jsou x86_64 — na Macu by jinak vznikl arm64 a spadl by „exec format error");
  • build + push v jednom kroku, tagy :vX.Y.Z (z git tagu) a :latest (na main);
  • panel nebuildí (od oddělení panelu) — image obsahuje jen API + docs.

Přebít platformu jde přes PLATFORMS=… ./deploy.sh.

3) Panel — build na serveru

Panel se buduje přímo na serveru z repa atrea-user-panel skriptem build.sh. Je to per-env build (build:production / build:test / build:test-production) — build-time hodnoty se berou z příslušného .env.<mode> (hlavně VITE_API_BASE_URL + Zitadel clientId/orgs).

bash
cd /opt/apps/atrea-user-panel

./build.sh           # nabídne: 1) production  2) test  3) test-production
./build.sh -q        # production bez dotazu

Tři módy a jejich výstup (verzovaný adresář + symlink latest):

MódEnv souborVýstupDoménaBackend
production.env.productionrelease/latestadmin.auth.amotion.cloudprod API + prod Zitadel
test.env.testrelease-test/latestpanel.kagb.cloudtest API + test Zitadel (kagb)
test-production.env.test-productionrelease-test-production/latesttest.admin.auth.amotion.cloudprod API + prod Zitadel

test-production

Slouží k testování změn pouze ve frontendu proti ostrému produkčnímu backendu. Je shodný s production, liší se jen redirect/logout URI (míří na test.admin.auth.amotion.cloud) — ta URI musí být povolená v produkční Zitadel aplikaci (Redirect URIs), jinak login selže.

Caddy pak servíruje statiku z latest symlinku:

caddyfile
admin.auth.amotion.cloud {
    root * /opt/apps/atrea-user-panel/release/latest
    try_files {path} /index.html      # SPA fallback
    file_server
}
panel.kagb.cloud {
    root * /opt/apps/atrea-user-panel/release-test/latest
    try_files {path} /index.html
    file_server
}
test.admin.auth.amotion.cloud {
    root * /opt/apps/atrea-user-panel/release-test-production/latest
    try_files {path} /index.html
    file_server
}

Produkční client ID

Protože panel nemá runtime config, ZITADEL clientId se zapéká při buildu z panelového .env.production (VITE_ZITADEL_CLIENT_ID). Organization ID se do panelového prostředí už nezapisují: odkazy i směrování auth e-mailů se odvozují z brand.zitadelOrgId načteného přes API.

4) Stack na serveru — pull & up

Images (atrea-user-api, atrea-zitadel-login) se na server jen tahají z hubu, nebuildí se tam.

Produkce

Nasazuje se přes ansible (volá docker compose), nebo ručně:

bash
docker compose -f docker-compose.production.yml --env-file .env.production pull
docker compose -f docker-compose.production.yml --env-file .env.production up -d

Test

bash
docker compose -f docker-compose.test.yml --env-file .env.test pull
docker compose -f docker-compose.test.yml --env-file .env.test up -d

NODE_ENV (production / test) je v compose souboru a vybere config/*.yaml. Hesla a secrety jdou z .env.production / .env.test (gitignored).

Databáze

Jeden postgres (atrea-postgres) drží dvě databáze:

  • atrea-user-db — user-api (vlastník app).
  • zitadel — Zitadel si ji vytvoří sám při prvním startu (app je instance admin, vyrobí DB + runtime usera zitadel).

Schéma a seed user-api se aplikují ručně po prvním startu (server nemá repo se sql/):

bash
docker exec -i atrea-postgres psql -U app -d atrea-user-db < sql/init.sql
docker exec -i atrea-postgres psql -U app -d atrea-user-db < sql/data.sql

# Pak doženi pending migrace, které ještě nejsou v init.sql:
docker exec -i atrea-postgres psql -U app -d atrea-user-db < sql/migrations/<soubor>.sql

Existující vs. nová databáze

Aktuální sql/init.sql vytváří finální schéma pro novou databázi. Na existující databázi ale vždy aplikujte novější migrace, protože opětovné spuštění initu nenahrazuje postupné transformace starého schématu.

Spuštění migrace na běžícím prostředí:

bash
docker exec -i atrea-postgres psql -U app -d atrea-user-db < sql/migrations/<soubor>.sql

Pro kanonický admin detail, globální timezone a audit aktivity:

bash
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-07-23_user_activity_and_settings.sql

Pro explicitní příznak systémových notifikačních typů a pevnou politiku auth/auth_zitadel:

bash
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-07-23_notification_orgname_system_types.sql

Samotné orgName ve vykreslení šablon a odesílatele je aplikační změna bez dalšího databázového sloupce. Pokud na prostředí ještě nejsou seedované auth šablony, spusťte nejdříve sql/migrations/2026-07-23_auth_notification_templates.sql.

Pro seznam podporovaných brandů aplikace a mapování instancí na brand pro Login v2:

bash
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-08-03_application_instances.sql

Pro automatickou správu ZITADEL projektů a OIDC aplikací spusť následně:

bash
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-08-03_zitadel_application_sync.sql

Jednorázové bezpečné připojení existujících projektů aManager a KGB jako unmanaged (bez automatického přepisování nebo mazání):

bash
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-08-03_adopt_existing_zitadel_apps.sql

Před vydáním verze s auditem, katalogem entit a synchronizací profilu spusťte také všechny tři následující idempotentní migrace:

bash
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-08-03_admin_audit_log.sql
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-08-03_application_entity_types.sql
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-08-03_panel_application_entity_permissions.sql
docker exec -i atrea-postgres psql -v ON_ERROR_STOP=1 -U app -d atrea-user-db \
  < sql/migrations/2026-08-03_zitadel_profile_sync.sql

Login v2 po úspěšné změně profilu volá interní POST /api/internal/auth/profile-sync; používá stejné USER_API_URL a USER_API_INTERNAL_KEY jako registrace. ZITADEL Action může navíc posílat na POST /api/zitadel/webhook event user.updated jako záložní cestu pro změny provedené mimo Login v2. Stejný webhook secret zůstává beze změny.

Potom v admin panelu zkontroluj vzdálené callbacky, založ lokální instance s jejich brandy a teprve pak potvrď Převzít správu.

Pořadí je důležité: nejdřív tabulka instancí, potom metadata synchronizace a až poté verze API, která managed sync používá. atrea-user-api je zdrojem pravdy: jedna lokální aplikace spravuje jeden vlastní ZITADEL projekt, jednu OIDC SPA aplikaci a callbacky odvozené z instancí. Existující vzdálené objekty nejdřív importujte pomocí jejich project/application/client ID jako unmanaged a teprve po ověření je případně přepněte na managed.

Smazání lokální aplikace smí smazat vzdálený projekt pouze při zitadel_managed = true; u importované unmanaged aplikace se vzdálený stav nemění. Pokud synchronizace nebo vzdálené mazání selže, lokální záznam musí zůstat zachovaný se stavem error a operace se po odstranění příčiny opakuje.

Konfigurace prostředí

Hierarchie configu (node-config):

  • config/default.yaml — společné defaulty (+ dev).
  • config/test.yaml (NODE_ENV=test) / config/production.yaml (NODE_ENV=production).
  • config/custom-environment-variables.yaml — mapuje secrety na env proměnné.

Secrety se nikdy necommitují — jdou přes .env.production / .env.test:

ProměnnáVýznam
POSTGRES_PASSWORDheslo DB app (user-api + Zitadel admin)
ZITADEL_DB_PASSWORDheslo Zitadel runtime usera
ZITADEL_MASTERKEYklíč, kterým Zitadel šifruje secrety v DB (32 znaků)
ZITADEL_ADMIN_USER / _PASSWORDprvní admin Zitadelu (bootstrap)
ZITADEL_SERVICE_ACCOUNT_TOKENPAT service účtu, kterým API volá Zitadel
ZITADEL_PROJECT_ORG_IDCentrální organizace vlastnící managed projekty (377696186024394755)
ZITADEL_WEBHOOK_SECRETověření příchozích Zitadel Action callů
INTERNAL_API_KEYsdílený klíč pro interní service-to-service endpointy
BRAND_SMTP_KEYAES klíč pro šifrování per-brand SMTP hesel v DB
EMAIL_SMTP_*globální SMTP fallback (když brand nemá vlastní)

Login kontejner musí pro předání jazyka registrace dostat USER_API_URL (bez koncového /api) a USER_API_INTERNAL_KEY, jehož hodnota je shodná s INTERNAL_API_KEY user API.

Health check

bash
curl https://api.auth.amotion.cloud/api/health     # { status, version, uptime }
curl https://api.auth.amotion.cloud/api/version     # { version, gitSha, buildMode }

Kontejner app má i Docker healthcheck na /api/health.

Checklist před nasazením

API image (DEV):

  • [ ] npm version bump (git tag vX.Y.Z)
  • [ ] čistý git tree
  • [ ] ./deploy.sh → image v hubu (:vX.Y.Z + :latest)

Panel (server):

  • [ ] .env.production / .env.test má správný VITE_API_BASE_URL
  • [ ] produkční VITE_ZITADEL_CLIENT_ID doplněné
  • [ ] každý relevantní brand má v databázi správné zitadelOrgId
  • [ ] ./build.shrelease(-test)/latest
  • [ ] Caddy servíruje z latest symlinku

Stack (server):

  • [ ] .env.production / .env.test se secrety (žádné defaulty change-me-*)
  • [ ] ZITADEL_MASTERKEY nastaven (a při migraci DB sedí se starým)
  • [ ] docker compose … pull && up -d
  • [ ] DB inicializována (init.sql + data.sql + pending migrace)
  • [ ] Superadmin spustil POST /api/zitadel/admin/notification-provider/setup
  • [ ] Caddy: 4 domény (zitadel / login / api / panel)
  • [ ] SSL certifikáty platné
  • [ ] Zálohování DB nastaveno

Atrea User API — interní dokumentace