Správa portálu — Specifikace¶
Účel¶
Samostatná aplikace přístupná výhradně firmě Avaxis (superfirma). Slouží ke správě celé platformy — schvalování registrací, správa firem a jejich uživatelů, katalog aplikací, monitoring, vzdálená podpora, fakturace. Dostupná jako webová aplikace i záložka v desktop main app pro super_admin.
Očekávaná škála: ~500 firem, ~100 000 uživatelů (40 000 aktivních)
Superfirma Avaxis¶
Avaxis je první a speciální firma v systému:
• IČO: [IČO firmy Avaxis — doplnit při nasazení]
• system_role uživatelů: dle přiřazených rolí (viz sekce Role)
• Plný přístup ke Správě portálu dle role
• Jako první se zaregistruje při nasazení (seed script)
Seed při nasazení¶
Při prvním spuštění backendu (prázdná databáze):
1. Vytvoří se firma Avaxis (IČO, název z ENV proměnných)
2. Vytvoří se první super_admin účet (e-mail + heslo z ENV)
3. Veškerá další správa probíhá přes UI portálu
Forma aplikace¶
• Web: admin.avaxis.cz — přístup jen pro uživatele Avaxis s rolí
• Desktop: záložka "Správa portálu" viditelná jen pro Avaxis uživatele
• API vrátí 403 Forbidden pro jakéhokoli jiného uživatele
Role uživatelů Avaxis¶
Uživatel může mít více rolí současně — role jsou kumulativní.
super_admin → plný přístup ke všemu
support_agent → support tikety (viz support.md)
catalog_admin → správa aplikací + operační statistiky (bez financí)
payments_admin → finanční přehledy + správa předplatných (plné změny)
accounting_readonly → finanční přehledy jen pro čtení (bez změn)
Příklady kombinací:
Jan [support_agent + catalog_admin] → tikety + katalog, ne finance
Marie [payments_admin] → veškeré finance, ne tikety
Petr [super_admin] → vše
Lucie [accounting_readonly] → vidí příjmy, nemůže měnit
avaxis_user_roles (
user_id UUID REFERENCES users(id),
role VARCHAR(50),
PRIMARY KEY (user_id, role)
)
Sekce 1 — Registrace: schvalování nových firem¶
Nová firma → potvrdí e-mail → žádost v "Čekající registrace"
┌────────────────────────────────────────────────────────────────┐
│ Firma A (IČO: 12345678) 2026-04-21 09:14 │
│ jan.novak@firmaa.cz │
│ [Schválit] [Zamítnout] [Detail] │
└────────────────────────────────────────────────────────────────┘
Po schválení → firma aktivní, company admin dostane e-mail
Po zamítnutí → e-mail s důvodem, žádost v archivu
Notifikace: badge + záložka Notifikace v detailu firmy. Žádný e-mail na super admina.
Automatické schvalování¶
platform_settings: 'auto_approve_registrations' = 'true'/'false'
Pokud zapnuto: firma aktivní ihned po potvrzení e-mailu
Doporučení: vypnout v produkci, zapnout jen pro testování
Sekce 2 — Správa firem¶
Seznam firem¶
Vyhledávání: [ název firmy nebo IČO nebo e-mail uživatele... ]
Filtry: Stav / Plán / Datum registrace
┌──────────────────┬──────────┬──────────┬──────────┬──────────┐
│ Firma │ IČO │ Uživatelé│ Plán │ Stav │
├──────────────────┼──────────┼──────────┼──────────┼──────────┤
│ Firma A │ 12345678 │ 12 │ Standard │ Aktivní │
│ Firma D │ 55667788 │ 5 │ Premium │ Grace 3d │
└──────────────────┴──────────┴──────────┴──────────┴──────────┘
Detail firmy — záložky¶
┌─────────┬───────────┬──────────┬───────────┬─────────────┬────────────┐
│ Přehled │ Uživatelé │ Aplikace │ Předplatné│ Notifikace │ Audit log │
└─────────┴───────────┴──────────┴───────────┴─────────────┴────────────┘
Přehled: základní info, stav, trial/grace stav, tlačítka akcí
Aplikace: aktivní aplikace, počty instalací, stav (aktivní/read-only/deaktivovaná)
Předplatné: aktivní předplatná, expirace, trial/grace nastavení per firma, ruční operace
Notifikace: chronologický seznam (selhání platby, expirace, registrace), přečteno/nepřečteno
Audit log: akce super adminů + impersonace, viditelné i company adminovi firmy
Sekce 2a — Správa uživatelů (škála 100 000 uživatelů)¶
Správa uživatelů je klíčová funkce pro vzdálenou podporu. Systém musí být výkonný při 500 firmách a 100 000 uživatelích.
Globální vyhledávání uživatelů (napříč firmami)¶
Vyhledávání: [ jméno / e-mail / IČO firmy... ]
Filtry: Stav (aktivní / neaktivní / smazaný) / Firma / Role / Poslední přihlášení
Výsledky stránkované (50 na stránku), full-text search přes PostgreSQL
nebo Elasticsearch pro rychlost při 100k+ záznamech.
┌──────────────────┬──────────────────────┬─────────────┬──────────────┐
│ Jméno │ E-mail │ Firma │ Posl. přih. │
├──────────────────┼──────────────────────┼─────────────┼──────────────┤
│ Jan Novák │ jan.novak@firmaa.cz │ Firma A │ dnes 09:14 │
│ Jana Nováková │ j.novakova@firmab.cz │ Firma B │ 3 dny │
│ [Smazaný uživ.] │ [anonymizováno] │ Firma C │ 6 měsíců │
└──────────────────┴──────────────────────┴─────────────┴──────────────┘
Detail uživatele (vzdálená podpora)¶
Základní info:
Jméno, e-mail, firma, role v firmě, datum registrace, stav
Přiřazené aplikace: seznam s možností přidání/odebrání
Historie přihlášení (posledních 50):
Datum/čas | IP adresa | Zařízení (device_id) | Typ (přihlášení / impersonace)
Akce vzdálené podpory:
[Reset hesla] → odešle reset e-mail
[Deaktivovat] → zablokuje přístup (soft, reverzibilní)
[Smazat / Anonymizovat] → viz GDPR sekce
[Přihlásit se jako uživatel] → Impersonace (s audit logem)
[Oživit uživatele] → pouze pro stav 'deleted' (anonymizovaný)
Stavy uživatele¶
active → normální přístup
inactive → deaktivován adminem (firma nebo Avaxis), lze reaktivovat
deleted → anonymizován (GDPR), jméno a e-mail nahrazeny zástupnými hodnotami
ID zůstává pro konzistenci cizích klíčů v datech
lze "oživit" — vytvoří nový účet propojený se starým ID
Fyzické smazání záznamu NIKDY neprobíhá — zajišťuje konzistenci dat
(faktury, audit logy, zprávy v chatu jsou vázány na user_id)
Anonymizace (GDPR výmaz)¶
users tabulka po anonymizaci:
name → "Smazaný uživatel"
email → deleted_{uuid}@avaxis.deleted
phone → NULL
is_deleted → true
deleted_at → timestamp
gdpr_request_id → odkaz na žádost
Co se smaže fyzicky ze S3:
• Zálohy dat (users/{id}/data/)
• Zálohy nastavení (users/{id}/settings/)
• Přílohy chatu odeslané uživatelem
Co zůstane (pro konzistenci):
• Zprávy v chatu → "Smazaný uživatel: [obsah zprávy]"
• Záznamy v audit logu → "Smazaný uživatel (ID: xxx)"
• Faktury a platební záznamy (daňová povinnost)
Hromadné operace¶
Pro efektivní správu při 100k uživatelích:
Hromadný výběr (checkbox) + akce:
• Deaktivovat vybrané uživatele
• Resetovat hesla vybraným
• Export vybraných do CSV
Filtry pro hromadný výběr:
• Všichni uživatelé firmy X
• Neaktivní déle než N dní
• Bez přihlášení za posledních 90 dní
Výkon a indexování¶
-- Kritické indexy pro 100k uživatelů:
CREATE INDEX idx_users_company ON users(company_id);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(is_active, is_deleted);
CREATE INDEX idx_users_last_login ON users(last_login_at DESC);
-- Full-text search (pokud bez Elasticsearch):
CREATE INDEX idx_users_fts ON users
USING GIN(to_tsvector('czech', name || ' ' || email));
-- Login history partitioning (může narůst na miliony řádků):
login_history PARTITION BY RANGE (created_at)
→ měsíční partition, starší než 1 rok přesunout do S3 archivu
Impersonace uživatele¶
Super admin klikne [Přihlásit se jako uživatel]:
→ Audit záznam: { actor, action:'impersonate', target_user, company, started_at }
→ Super admin vidí aplikaci očima uživatele
→ Banner v UI: "Prohlížíte jako: Jan Novák (Firma A) — [Ukončit]"
→ Po ukončení: { ended_at }
Kdo vidí záznam:
✓ Super admini Avaxis — globální audit log
✓ Company admin dané firmy — záložka Audit log firmy
"Administrátor Avaxis přistoupil k účtu Jana Nováka 21.4.2026 09:32–09:58"
✗ Běžní uživatelé firmy
Sekce 3 — Správa aplikací v katalogu¶
(přístup: catalog_admin, super_admin)
Seznam aplikací: název, slug, kategorie, viditelnost, verze, instalace
Nahrání nové verze:
1. Vybrat aplikaci, zadat semver verzi
2. Nahrát Win .exe + Linux .AppImage → S3, auto SHA-256 checksum
3. Changelog (Markdown, nepovinné)
4. Označit jako latest nebo preview (beta)
Vytvoření nové aplikace:
Název, slug, popis, kategorie, ikona
Viditelnost: public / custom (vybrat firmu)
Statistiky pro catalog_admin¶
(operační data pro správu katalogu, bez finančních informací)
Per aplikace:
• Celkový počet instalací (Win / Linux)
• Počet aktivních instalací (spuštěno za posledních 30 dní)
• Graf stahování v čase
• Distribuce verzí (kolik uživatelů má kterou verzi)
• Firmy s největším počtem instalací (název firmy, počet)
Sekce 4 — Předplatné, Trial a Grace period¶
(přístup: payments_admin, super_admin)
Stavy předplatného¶
active → plný přístup
trial → zkušební období, plný přístup bez platby
grace → expirováno/nezaplaceno, READ-ONLY režim (viz níže)
expired → grace period uplynula, přístup zablokován
cancelled→ zrušeno firmou
Grace period — business logika¶
Kontext: ekonomické aplikace zpracovávají data předchozího měsíce
v měsíci aktuálním. Uživatel musí mít přístup k datům za zaplacené období.
Grace period chování:
→ Aplikace přístupná, placené komponenty v READ-ONLY:
• Vidí všechna data z období aktivního předplatného
• Může tisknout a exportovat (PDF, XLSX, CSV)
• Nemůže vytvářet ani editovat záznamy
• Banner: "Vaše předplatné expiralo. Data jsou dostupná pouze
pro čtení a export. Obnovte předplatné pro plný přístup."
Po uplynutí grace period:
→ Přístup zcela zablokován, data zachována na S3
Po zaplacení (kdykoliv v grace period):
→ API přijme příznak od Stripe (invoice.paid webhook)
→ Okamžitá automatická obnova plného přístupu
→ E-mail + in-app notifikace potvrzení obnovy
Časová databáze předplatného¶
Základ pro určení, ke kterým datům má firma přístup v read-only režimu.
subscription_history (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
app_id UUID REFERENCES apps(id),
status VARCHAR(20),
period_start TIMESTAMPTZ,
period_end TIMESTAMPTZ, -- NULL = stále aktivní
plan VARCHAR(50),
changed_by VARCHAR(50), -- 'stripe_webhook' | 'super_admin' | 'system'
created_at TIMESTAMPTZ
)
Aplikace v read-only: "Ukaž data z období kdy status = 'active' pro tuto firmu."
Trial a grace period nastavení¶
Globální defaulty (platform_settings):
default_trial_days = 7
default_grace_days = 7
notification_days_before_expiry = '7,1'
Per firma (přepíše global):
company_trial_config: trial_days, grace_days
Notifikace při expiraci (obojí — e-mail + in-app):
trial: 7 dní před, 1 den před, v den expirace → přechod do grace
grace: okamžitě při přechodu, každé 3 dny připomínka
zaplacení: okamžité potvrzení obnovy
Ruční operace super admina:
• Prodloužit trial (firma potřebuje více času)
• Prodloužit grace period (řeší se platba, sporná faktura)
• Okamžitě aktivovat / deaktivovat
Sekce 5 — Uživatelé Avaxis¶
Správa interních uživatelů s přiřazením rolí (více rolí na osobu):
super_admin, support_agent, catalog_admin, payments_admin, accounting_readonly
Akce: přidat, přiřadit/odebrat roli, deaktivovat
Sekce 6 — Finanční přehled¶
(přístup: payments_admin — čtení + změny; accounting_readonly — jen čtení)
• MRR, ARR, graf příjmů v čase
• Počet předplatných per aplikace per plán
• Firmy v grace period (riziko churn)
• Seznam firem s fakturací + stav platby
• Propojení Stripe API — faktury, platební metody, detail transakcí
• Export CSV / XLSX pro účetnictví
accounting_readonly: vše viditelné, tlačítka pro změny skryta
Sekce 7 — Monitoring¶
(přístup: catalog_admin, super_admin)
• Počet aktivních firem / uživatelů / instalací
• Nové registrace za 30 dní
• Stav S3 (GB, náklady), Uptime API
• Aktivní support tikety
Sekce 8 — Audit log (globální)¶
Záznamy nelze editovat ani mazat.
Filtry: datum / actor / typ / firma
Export: CSV
Retention: smluvní vztah + 3 roky → starší záznamy do S3 archivu
GDPR¶
Při registraci: GDPR souhlas firma + každý uživatel zvlášť
Nová verze dokumentu → znovu souhlas při přihlášení
Právo na výmaz — workflow v portálu:
1. Žádost přijde přes support nebo přímo do portálu
2. Super admin schválí → systém anonymizuje (viz sekce Anonymizace)
3. Zákonná lhůta 30 dní → portál hlídá a upozorní při překročení
4. Záznam žádosti uložen pro compliance
gdpr_requests (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
type VARCHAR(20), -- 'erasure' | 'export' | 'correction'
status VARCHAR(20), -- 'pending' | 'completed' | 'rejected'
requested_at TIMESTAMPTZ,
deadline_at TIMESTAMPTZ, -- requested_at + 30 dní
completed_at TIMESTAMPTZ,
handled_by UUID REFERENCES users(id)
)
Datový model¶
company_registrations (
id UUID PRIMARY KEY,
ico VARCHAR(8),
name VARCHAR(255),
address TEXT,
contact_email VARCHAR(255),
status VARCHAR(20),
auto_approved BOOLEAN DEFAULT false,
reviewed_by UUID REFERENCES users(id),
review_note TEXT,
created_at TIMESTAMPTZ,
reviewed_at TIMESTAMPTZ
)
platform_settings (
key VARCHAR(100) PRIMARY KEY,
value TEXT,
updated_by UUID REFERENCES users(id),
updated_at TIMESTAMPTZ
)
company_trial_config (
company_id UUID REFERENCES companies(id) PRIMARY KEY,
trial_days INTEGER,
grace_days INTEGER,
updated_by UUID REFERENCES users(id),
updated_at TIMESTAMPTZ
)
subscription_history (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
app_id UUID REFERENCES apps(id),
status VARCHAR(20),
period_start TIMESTAMPTZ,
period_end TIMESTAMPTZ,
plan VARCHAR(50),
changed_by VARCHAR(50),
created_at TIMESTAMPTZ
)
avaxis_user_roles (
user_id UUID REFERENCES users(id),
role VARCHAR(50),
PRIMARY KEY (user_id, role)
)
gdpr_consents (
id UUID PRIMARY KEY,
subject_type VARCHAR(20), -- 'company' | 'user'
subject_id UUID,
version VARCHAR(20),
ip_address INET,
signed_at TIMESTAMPTZ
)
gdpr_requests (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
type VARCHAR(20),
status VARCHAR(20),
requested_at TIMESTAMPTZ,
deadline_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
handled_by UUID REFERENCES users(id)
)
portal_notifications (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
type VARCHAR(50),
message TEXT,
is_read BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ
)
-- login_history partitioned by month (může mít miliony řádků)
login_history (
id UUID,
user_id UUID REFERENCES users(id),
ip_address INET,
device_id VARCHAR(255),
type VARCHAR(20), -- 'login' | 'impersonate_start' | 'impersonate_end'
created_at TIMESTAMPTZ
) PARTITION BY RANGE (created_at)
Rozhodnuté otázky¶
| Otázka | Rozhodnutí |
|---|---|
| Forma aplikace | Samostatná webová aplikace (admin.avaxis.cz) + záložka v desktop |
| Škála | ~500 firem, ~100 000 uživatelů, ~40 000 aktivních |
| Role Avaxis | super_admin, support_agent, catalog_admin, payments_admin, accounting_readonly |
| Multi-role | Uživatel může mít více rolí současně |
| Finance — přístup | payments_admin (změny) + accounting_readonly (čtení) |
| catalog_admin finance | Žádné — vidí jen operační statistiky katalogů |
| catalog_admin statistiky | Instalace, aktivní uživatelé, graf stahování, distribuce verzí |
| Impersonace — záznam | Vidí super admini Avaxis + company admin dané firmy |
| Grace period — obnova | Okamžitě automaticky po platbě (Stripe webhook → API příznak) |
| Grace period — business | Zpracování dat předchozího měsíce → read-only je dostatečné |
| Trial default | 7 dní, nastavitelný per firma |
| Grace default | 7 dní, nastavitelný per firma (prodloužení pro sporné platby) |
| Grace chování | Read-only + tisk + export za zaplacené období |
| Notifikace expirace | E-mail + in-app: 7 dní před, 1 den před, v den expirace, každé 3 dny v grace |
| Fyzické mazání uživatelů | Nikdy — pouze anonymizace (GDPR), ID zachováno pro konzistenci |
| Smazaní uživatelé | Viditelní v seznamu jako "[Smazaný uživatel]", lze oživit |
| GDPR workflow | Formální žádost v portálu, 30denní lhůta, hlídáno systémem |
| Výkon uživatelů | PostgreSQL full-text index, login_history partitioning po měsících |
Otevřené otázky¶
Všechny otázky k portal-management.md jsou vyřešeny.
Poslední aktualizace: 2026-04-21