Skip to content

Billit — Ops

Deploy, restart, troubleshoot. Pro overview viz INDEX.md.

Deploy

cd /root/projects/sentinel
bash deploy/deploy.sh billit hub <commit>      # nejdřív hub
# HC + sanity check (1-2 min)
bash deploy/deploy.sh billit prod <commit>     # pak prod

⚠️ Vždy DEPLOY OK od Libora předem (orthogonal: feedback_deploy_authorization.md).

Selektivní deploy

Pokud commit mění jen billit-web/ nebo jen billit-api/, deploy.sh restartuje pouze ten container s --no-deps (od commit c6f980c). Vidíš v deploy.sh logu jako SELECTIVE_SERVICES=....

Migration

deploy.ymldatabase.migration: typeorm. Migration command:

node node_modules/typeorm/cli.js migration:run -d dist/database/data-source.js

Spouští se v billit-billit-api-1 containeru.

⚠️ Při deploy s migrací → ověřit, že deploy.yml nemá top-level migration: yes flag (to deploy.sh blokuje od commit fd378b4).

Health check po deployi

curl https://billit.s60hub.cz/health    # hub
curl https://billit.cz/health           # prod

Sanity:

ssh root@100.68.138.14 "docker ps | grep billit"
ssh root@100.68.138.14 "docker logs s60-billit-api --tail 50"

Restart (bez deploye)

ssh root@100.68.138.14 "cd /opt/billit && docker compose restart billit-api"
ssh root@100.78.87.88 "cd /opt/billit && docker compose restart billit-web"

⚠️ Deploy guard hook blokuje přímé docker compose restart přes Bash tool. Pokud potřebuješ restart, jdi přes deploy.sh nebo požádej Libora o explicitní RESTART OK.

Integrace nové aplikace

Detail: runbooks/billit-integration.md.

Krátká verze:

  1. JWT — generovat HS256 token signed s SENTINEL_JWT_SECRET z /root/secrets/billit/.env.prod, iss: https://auth.studio60.cz, owner user ID z tenant_users kde tenant.slug = 'studio-60'
  2. API keyPOST /v1/accounts/studio-60/api-keys s {name, scopes}. Předat token přes dočasný soubor (SSH quoting!)
  3. WebhookPOST /v1/accounts/studio-60/webhooks s {url, events: ["order.paid", "order.cancelled"]}. Vrátí secret (whsec_…)
  4. Env vars v cílové app — BILLIT_API_URL, BILLIT_API_KEY, BILLIT_WEBHOOK_SECRET
  5. Secrets uložit do /root/secrets/<app>/billit-integration-prod.env (chmod 600)

Dostupné scopes

Scope Popis
read:invoices, write:invoices Faktury
read:orders, write:orders Objednávky
read:subjects, write:subjects Kontakty
read:expenses, write:expenses Náklady
read:accounting, write:accounting Účetnictví
export:accounting Export do ABRA/Pohoda/Money
read:webhooks Správa webhooků

Troubleshoot

"401 Unauthorized" při volání billit API

  • Ověřit, že JWT byl signed s aktuálním SENTINEL_JWT_SECRET (může se rotovat při migraci)
  • Ověřit iss claim = https://auth.studio60.cz
  • ForwardAuth headers už nefungují (od 2026-03-12) — používat JWT

Container nestartuje po deployi

  1. docker logs s60-billit-api --tail 100 — TypeORM migration error?
  2. Ověřit DB connection: docker exec s60-billit-api env | grep DB_
  3. Pokud TypeORM synchronize:true v logu — je to bug, MUSÍ být false (orthogonal: feedback_no_synchronize.md)
  4. Pre-deploy snapshot: /var/backups/deploy-snapshots/billit/<timestamp>/
  5. Rollback: viz runbooks/rollback.md

Slow query / DB pool exhausted

  1. Check pgBouncer pool: psql -h 100.125.3.121 -p 6432 -U pgbouncer pgbouncer -c "SHOW POOLS"
  2. Default pool size = 30, max client conn = 1000 (viz infra/postgres.md)
  3. Při blokujících queries — povolit debug user (RO):
    ALTER ROLE s60_billit_debug LOGIN;
    
    Použít /root/secrets/billit/.env.prod.debug pro SELECT * FROM pg_stat_activity WHERE datname = 's60_billit_prod'
  4. Po debug ihned zablokovat: ALTER ROLE s60_billit_debug NOLOGIN

Webhook nedoručuje

  1. Ověřit billit-api logs: docker logs s60-billit-api 2>&1 | grep webhook
  2. Cíl URL dostupný z prod-alfa? curl -I https://<app-domain>/webhooks/billit
  3. Webhook secret rotován? — porovnat BILLIT_WEBHOOK_SECRET v cílové app vs to, co je v DB webhooks.secret

Oblíbené příkazy

# Stav kontejnerů
ssh root@100.78.87.88 "docker ps --filter name=billit"

# Logy posledních 100 řádků
ssh root@100.78.87.88 "docker logs s60-billit-api --tail 100"

# DB connection check uvnitř containeru
ssh root@100.78.87.88 "docker exec s60-billit-api env | grep -E '^(DB_|REDIS_)'"

# DB query (přes one-off postgres container kvůli neabsenci psql v containeru)
ssh root@100.78.87.88 'PWD_VAL=$(docker exec s60-billit-api printenv DB_PASSWORD); \
  docker run --rm --network s60-network -e PGPASSWORD="$PWD_VAL" postgres:17-alpine \
  psql -h 100.125.3.121 -p 6432 -U s60_billit_prod_user -d s60_billit_prod -c "SELECT count(*) FROM tenants"'

# Manuální health check
curl https://billit.s60hub.cz/health
curl https://billit.cz/health