Přeskočit obsah

Admin app onboarding — dev guide

Jak v praxi zaregistrovat novou vendor aplikaci do AVAX platformy. Pro super_adminy a vývojáře platformy. User-facing průvodce je v user-guide/admin/aplikace-gateway.md.

Spec: docs/spec/admin-app-onboarding.md.

Architektura toho, co se děje

Vendor app onboarding se skládá ze čtyř kroků:

1. create_app endpoint  → DB row + Gitea repo + S3 bucket + secrets + port
2. Vendor pushne kód    → Gitea Actions CI postaví image + spustí kontejner
3. Gateway toggle       → flip apps.gateway_enabled=true + pubsub trigger
4. Background probe     → poll /<slug>/health 30s → apps.health_status

Kroky 1, 3 a 4 jsou plně automatizované přes admin UI v launcheru (Vendoři tab → "+ Nová aplikace" + Aplikace tab → checkbox "Gateway routing"). Krok 2 dělá vendor sám (push do Gitea).

Standardní flow (UI cesta)

  1. Login do launcheru jako super_admin (michal@avaxis.cz nebo jiný system_role=super_admin).
  2. Tab "Vendoři" → "+ Nová aplikace" — vyplnit slug, jméno, popis, zaškrtnout "Má backend (per-app kontejner)" pokud aplikace má serverovou část.
  3. Klik "Vytvořit" — backend volá POST /admin/app-management/create. Po ~10 s response s linkem na Gitea repo a S3 bucket info.
  4. Vendor naklonuje repo (např. paincelebrator Michal pro AVAXIS apps), pushe vlastní kód. Gitea Actions automaticky spustí .gitea/workflows/backend.yml co postaví image a spustí kontejner avax-app-<slug> na 127.0.0.1:<port> (port byl alokován v kroku 1).
  5. Tab "Aplikace" → najít novou app → otevřít detail → sekce "🌐 Distribuce" → zaškrtnout "Gateway routing". Backend volá PUT /admin/app-management/<slug>/gateway {enabled:true}:
  6. flip gateway_enabled=true + is_public=true v DB
  7. Redis pubsub apps.changed → apps-gateway refresh cache <1s
  8. FastAPI BackgroundTasks → health_probe.probe_app_health() 30s
  9. Sledovat badge "⏳ gateway" v Aplikace seznamu — během ~30 s se přebarví na ✅ gateway (healthy) nebo ❌ gateway (probe selhala).

Po tomto je https://api.avaxis.cz/apps/<slug>/... (nebo http://192.168.1.55/apps/<slug>/... z dev LAN) plně funkční.

Ruční řešení (fallback když UI nefunguje)

Pokud admin UI / launcher není dostupný, nebo jeden z automatických kroků selže, postup přes SSH + curl:

Diagnostika: kde to padá?

# 1. Apps-gateway běží?
curl -sS http://127.0.0.1:8100/health
# → {"status":"ok","version":"0.1.0"}

# 2. App row v DB?
sudo docker exec avax-postgres psql -U avaxis -d avaxis_dev \
  -c "SELECT slug, gateway_enabled, is_public, container_port, health_status \
      FROM apps WHERE slug='<slug>';"

# 3. Backend kontejner běží?
sudo docker ps --filter "name=avax-app-<slug>" \
  --format '{{.Names}}\t{{.Status}}\t{{.Ports}}'

# 4. Routing přes nginx (s Host headerem)
curl -H 'Host: api.avaxis.cz' http://127.0.0.1/apps/<slug>/health
# Bez Host headeru z LAN nefunguje — viz reference_avaxdev_infra_gotchas
# v session memory (nginx default_server priorita).

Ruční flip gateway_enabled (když UI checkbox nefunguje)

sudo docker exec avax-postgres psql -U avaxis -d avaxis_dev -c \
  "UPDATE apps SET gateway_enabled=true, is_public=true, \
                   health_status='unknown', last_health_error=NULL \
   WHERE slug='<slug>';"

# Trigger apps-gateway cache refresh (pubsub fallback)
curl -X POST http://127.0.0.1:8100/admin/refresh

Ruční vypnutí gateway_enabled

sudo docker exec avax-postgres psql -U avaxis -d avaxis_dev -c \
  "UPDATE apps SET gateway_enabled=false WHERE slug='<slug>';"
curl -X POST http://127.0.0.1:8100/admin/refresh

Ruční probe (když background task nefungoval)

Backend by měl probe spustit automaticky po gateway flip, ale pokud worker padl uprostřed nebo restart:

# Z avaxdev — přímo přes apps-gateway loopback
curl -sS http://127.0.0.1:8100/<slug>/health

# DB ručně sladit (status="ok" pokud výše vrátilo 200)
sudo docker exec avax-postgres psql -U avaxis -d avaxis_dev -c \
  "UPDATE apps SET health_status='ok', last_health_check_at=now(), \
                   last_health_error=NULL \
   WHERE slug='<slug>';"

Ruční vytvoření app row (když create_app endpoint selhal)

Krajní případ — backend nezvládl vytvořit, ale Gitea repo a S3 bucket už existují. Insert do DB ručně:

sudo docker exec avax-postgres psql -U avaxis -d avaxis_dev <<'SQL'
INSERT INTO apps (
    id, slug, name, description, category, publisher_id, vendor_company_id,
    visibility, is_active, is_public, distribution_bucket,
    backend_mount, gitea_repo_url, container_port, gateway_enabled,
    container_host, health_path, health_status, docs_slug,
    created_at, updated_at
) VALUES (
    gen_random_uuid(), '<slug>', '<Display name>', '<Description>', NULL,
    '<publisher company UUID>', '<vendor company UUID>',
    'public', true, false, 'app-<slug>',
    true, 'https://git.avaxis.cz/avax-apps/<slug>-app',
    8104,  -- první volný port v range 8101-8199
    false, 'localhost', '/health', 'unknown', '<slug>',
    now(), now()
);
SQL

Port volný najít:

SELECT container_port FROM apps WHERE container_port IS NOT NULL ORDER BY container_port;
-- Vybrat první volný v range 8101-8199 nepoužitý.

Ruční Gitea repo + secrets (když backend selhal)

Backend create_app má best-effort try/except — pokud Gitea API timeoutuje, repo se nevytvoří ale DB row ano. Manuálně:

# 1. Vytvořit repo přes Gitea API (token: admin paincelebrator)
curl -X POST https://git.avaxis.cz/api/v1/orgs/avax-apps/repos \
  -H "Authorization: token $GITEA_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"<slug>-app","description":"<desc>","private":true,
       "auto_init":false,"default_branch":"main"}'

# 2. Inject APPDIST_S3_* secrets do repa
for secret in APPDIST_S3_ENDPOINT APPDIST_S3_ACCESS_KEY APPDIST_S3_SECRET_KEY APPDIST_S3_REGION; do
  VAL=$(sudo grep "^$secret=" /etc/avax/.env | cut -d= -f2-)
  curl -X PUT "https://git.avaxis.cz/api/v1/repos/avax-apps/<slug>-app/actions/secrets/$secret" \
    -H "Authorization: token $GITEA_ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"data\":\"$VAL\"}"
done

# 3. Push template skeleton — viz tools/avax-app-template/

Ruční S3 bucket (když Ceph create_bucket selhal)

# Na avaxdev:
set -a && source <(sudo cat /etc/avax/.env) && set +a
aws --endpoint-url "$APPDIST_S3_ENDPOINT" s3 mb "s3://app-<slug>"

# Public-read policy pro channels/* + version-manifest + files
cat > /tmp/bucket-policy.json <<'EOF'
{"Version":"2012-10-17","Statement":[{
  "Sid":"PublicReadDistribution","Effect":"Allow","Principal":"*",
  "Action":"s3:GetObject",
  "Resource":["arn:aws:s3:::app-<slug>/channels/*",
              "arn:aws:s3:::app-<slug>/versions/*/version-manifest.json",
              "arn:aws:s3:::app-<slug>/versions/*/files/*"]
}]}
EOF
aws --endpoint-url "$APPDIST_S3_ENDPOINT" s3api put-bucket-policy \
  --bucket "app-<slug>" --policy file:///tmp/bucket-policy.json

Související