Přeskočit obsah

Vendor onboarding — Gitea teams a per-developer accounts

Status: draft (2026-05-14) Verze spec: 0.1 Související: per-app-container.md, app-distribution.md, auth-organization.md

1. Motivace

Před touto změnou push template do nově vytvořeného repa probíhal pod jediným site-admin Gitea účtem (paincelebrator). To bránilo:

  • Auditugit log ukazoval „AVAX Platform service" pro všechny commity, ne skutečného developera
  • Code review — žádná branch protection s „PR od ne-autora"
  • Multi-vendor izolaci — vendor A měl read-only přístup ke všem repům, ne jen ke svým
  • Onboardingu — přidat developera znamenalo dát mu super-admin token

2. Cílový stav

Každá firma typu vendor v AVAX (řádek v companies tabulce s vlastní AVAX aplikací v adminu) má:

  1. Vlastní Gitea team v org avax-apps
  2. Sloupec gitea_team_slug v companies tabulce mapuje AVAX company UUID na Gitea team
  3. Členové týmu jsou skuteční developeři vendora s individuálními Gitea účty

Když Avaxis super-admin vytvoří app pro vendora: 1. POST /admin/app-management/create vytvoří Gitea repo v avax-apps/<slug>-app 2. Push template kostry 3. NEW: Pokud má publisher's company nastavený gitea_team_slug → přidá team jako collaborator s write permission 4. Developeři vendora okamžitě mohou klonovat + commitovat pod svým jménem

3. Architecturní rozhodnutí

Teams vs collaborators

Gitea nabízí dvě cesty grantování přístupu: - Collaborators — per-repo, per-user - Teams — per-org, granularita per-repo přiřazení

Volíme Teams protože: - Vendor má typicky víc apps (např. acme-soft → fakturace + mzdy + sklad) — team se přiřadí ke všem repům, member změna se promítne všude - Onboarding/offboarding developera = jedna operace, ne N - Branch protection rules lze nastavit per-team

Permission level pro vendor team

Write — developer může push do feature branche + PR do main. Avaxis tým může mít separátní admin permission přes site-admin nebo dedikovaný avaxis-internal team s admin ve všech repech.

Slug konvence

Gitea team slug = krátké pojmenování firmy/vendora, lowercase, kebab-case. Příklady: - qwen (existující vendor avax-legal) - acme-soft - avaxis-internal (Avaxis sám)

Validace: [a-z0-9-]+, length 2-32.

4. DB schema změna

ALTER TABLE companies
  ADD COLUMN gitea_team_slug VARCHAR(64) NULL;

CREATE UNIQUE INDEX uq_companies_gitea_team_slug
  ON companies (gitea_team_slug)
  WHERE gitea_team_slug IS NOT NULL;
  • NULL = firma nemá vendor status (běžní zákazníci AVAXu, kteří jen kupují/používají apps)
  • NOT NULL UNIQUE = vendor s teamem v avax-apps org

Migrace: XXX_add_companies_gitea_team_slug.py, downgrade dropne sloupec.

5. Backend API rozšíření

Gitea service (services/gitea.py)

Nové funkce — wrapper nad Gitea REST API:

def create_team(slug: str, description: str = "", permission: str = "write") -> dict:
    """POST /orgs/{GITEA_ORG}/teams — vytvoří team. Vrátí {id, name, permission}.
    Vyhodí GiteaError pokud team už existuje."""

def get_team_id(slug: str) -> int | None:
    """GET /orgs/{GITEA_ORG}/teams/search?q=<slug> — najde team. None pokud neexistuje."""

def add_team_member(team_id: int, username: str) -> None:
    """PUT /teams/{team_id}/members/{username}"""

def remove_team_member(team_id: int, username: str) -> None:
    """DELETE /teams/{team_id}/members/{username}"""

def add_team_to_repo(team_id: int, repo_name: str) -> None:
    """PUT /orgs/{org}/teams/{team_id}/repos/{org}/{repo}"""

def list_team_members(team_id: int) -> list[str]:
    """GET /teams/{team_id}/members"""

Wire do create_app

V routers/app_management.py po úspěšném push_template:

publisher_company = await db.get(Company, publisher_id)
team_slug = publisher_company.gitea_team_slug if publisher_company else None
if team_slug:
    try:
        team_id = gitea.get_team_id(team_slug)
        if team_id is None:
            log_entries.append(f"⚠ Vendor team `{team_slug}` neexistuje v Gitea")
        else:
            gitea.add_team_to_repo(team_id, f"{body.slug}-app")
            log_entries.append(f"✓ Team `{team_slug}` přidán k repu (write)")
    except gitea.GiteaError as e:
        log_entries.append(f"⚠ Team grant selhal: {e}")
else:
    log_entries.append("⚠ Publisher company nemá gitea_team_slug — repo bez vendor přístupu")

Tolerantní — pokud team grant selže, app row + repo zůstávají. Avaxis admin to může napravit přes admin endpoint.

Admin endpointy

PUT  /admin/companies/{id}/gitea-team
     Body: {slug: str, create_if_missing: bool = false, description: str = ""}
     → Nastaví companies.gitea_team_slug. Pokud create_if_missing a team neexistuje,
       vytvoří ho. Idempotentní (lze volat opakovaně).

POST /admin/companies/{id}/gitea-team/members
     Body: {username: str}
     → Přidá username do vendor teamu. Vyžaduje gitea_team_slug nastaven.

DELETE /admin/companies/{id}/gitea-team/members/{username}
     → Odebere uživatele z teamu.

GET  /admin/companies/{id}/gitea-team/members
     → Vrátí seznam aktuálních členů (jméno + email z Gitea).

Všechny vyžadují super_admin v AVAX (a token musí mít Gitea write:organization).

6. Migrace existujícího vendora qwen

Existující app avax-legal-clientgitea_repo_url: https://git.avaxis.cz/qwen/avax-legal — repo v uživatelské namespace qwen, ne v avax-apps org. To je legacy.

Migrace (manuální, jednorázová):

  1. V Gitea UI vytvořit team qwen v avax-apps org (write permission)
  2. Přidat existující qwen Gitea uživatele jako člena teamu
  3. V AVAX adminu volat PUT /admin/companies/{qwen-company-id}/gitea-team {slug: "qwen"} — nastaví DB sloupec
  4. Legacy repo qwen/avax-legal zatím zůstává. Možnosti pro budoucnost:
  5. Mirror: vytvořit avax-apps/avax-legal-app jako mirror legacy repa (Gitea má POST /repos/migrate)
  6. Move: transfer ownership přes POST /repos/{owner}/{repo}/transfer (vyžaduje souhlas qwen vendora)
  7. Nechat: pro tuto jedinou existující app necháme legacy URL; nové apps už půjdou novou cestou

Doporučuju nechat — migrace existující app je risk a vendor musí být součástí rozhodnutí.

7. Onboarding nového vendora — workflow

Když nový vendor podepíše smlouvu s Avaxis:

  1. AVAX admin:
  2. POST /org/companies — vytvořit firmu vendora (IČO, název, ...)
  3. PUT /admin/companies/{id}/gitea-team {slug: <vendor-slug>, create_if_missing: true} — vytvořit Gitea team + namapovat na company

  4. Vendor strana:

  5. Každý developer si v Gitea udělá vlastní účet (Gitea: Sign Up)
  6. Pošle Avaxis adminu seznam svých Gitea usernames

  7. AVAX admin:

  8. Pro každého: POST /admin/companies/{id}/gitea-team/members {username: <gitea_username>}

  9. Avaxis admin vytvoří první app pro vendora:

  10. POST /admin/app-management/create s publisher_id = <vendor company UUID>
  11. Backend automaticky přidá vendor team jako collaborator
  12. Developeři clone'nou a začnou pracovat

8. Otevřené otázky

  • Branch protection — má main vyžadovat PR + review? Lze nastavit per-repo v Gitea, ale chceme to default při create. Gitea API: POST /repos/{owner}/{repo}/branch_protections. Pravděpodobně přidat do push_template po push.
  • Avaxis internal team — má vlastní avaxis-internal team s admin permission ke všem avax-apps/* repům? Pravděpodobně ano, pro code review a hotfix.
  • Onboarding developera bez Avaxis admina — vendor admin (company_admin role) by mohl sám přidávat své developery, místo přes Avaxis. To by vyžadovalo role check (vendor admin své firmy ano, jiné ne) — gitea_team_slug v request musí matchovat current_user.company.gitea_team_slug.
  • Mazání teamu při delete_app — pokud má firma víc apps, nemažeme. Pokud poslední app firmy mazaná, ponechat team (vendor relace pokračuje i bez apps).
  • Vendor accountability — máme jen Gitea audit. Vyplatí se přidat AVAX audit_log záznam při změně členství?

9. Acceptance criteria

MVP

  • DB migrace companies.gitea_team_slug aplikovaná
  • services/gitea.py rozšířen o team API
  • create_app přidá vendor team jako collaborator (pokud nastaveno)
  • Admin endpoint PUT /admin/companies/{id}/gitea-team funguje
  • End-to-end test: vendor company → team → create app → vendor developer může push

V1

  • Admin endpoint pro člen-management (POST/DELETE/GET members)
  • Migrace qwen vendora (DB sloupec nastaven)
  • Branch protection na main (volitelné, default opt-in)
  • avaxis-internal team setup s admin přístupem všude

V2

  • UI v adminovi pro správu vendor teamů (místo přes API)
  • Vendor self-service: company_admin své firmy může spravovat své developery
  • Mirror legacy qwen/avax-legal do avax-apps/avax-legal-app

10. Související