Prava a bezpecnost: spravce firmy uz nemuze nastavit priznak spravce platformy ani clenstvi v cizi firme; pozvanky, konektory a automatizace kontroluji sve pravo; cizi firma v query je 404; zivy stream posila udalosti jen firmam, kterych se tykaji; akce nad ticketem maji kontrolu prava za firmu ticketu a strop viditelnosti; tokeny se nelogujou; limit pokusu na prihlaseni, kontakt a pozvanky; bezpecnostni hlavicky; zachyceni chyb v async handlerech; timing-safe porovnani tokenu. Vykon: audit neskenuje celou kolekci pri kazdem zapisu a konecne maze firemni zaznamy; ticket se uklada jednou misto trikrat; zapisy do Postgresu jsou serializovane podle ID; prava se pocitaji jednou na request; widgety nacitaji tickety jednou; strankovani seznamu; worker je pool misto kol; na webu udalost ze streamu neodmontuje stranku, dotazy maji spolecny debounce a cache, ciselniky drzi typovany sklad. Runtime: opakuji se jen chyby oznacene retryable; smycka nenarazi na strop 50 kroku (novy strop 1000 akci); podminka nad datem funguje; vystup MCP nastroje neprepisuje spoustec; sandbox skriptu firmy nejde opustit; MCP session id se drzi mezi volanimi; incident z kroku patri firme; jedno rozhodnuti o rezimu uloziste; snapshot neprepise soubor po chybe cteni. Refaktory: sdilene typy API v src/shared (web nic nekopiruje, osm rozjetych tvaru sjednoceno); spolecny modul net/guard pro volani ven; formularova vrstva ui/form; rozdeleni Connectors a FlowCanvas; jeden helper pro firmu z query, validaci a CRUD udalosti; pomucky ctx.util pro skripty konektoru; i18n verejneho webu vcetne anglictiny. Nova funkce: zalozeni firmy z registru ARES v Nastaveni (IC nebo nazev, dotazeni IC, DIC, sidla a pravni formy, vyber soucasnych statutarnich zastupcu a prokury, ucty spravce firmy s nahradnim e-mailem IC-poradi@placeholder.cz). Dokumentace: zaznam v 99-zmeny.md a aktualizace 15 dalsich dokumentu. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
9.1 KiB
Nastavení, práva, typy ticketů, akce a widgety
Co všechno se dá nastavit z portálu a proč je to postavené takhle. Model je z 09-navrh-rozsireni.md, tady je hotový stav. Seznam funkcí a komponent, které se u toho mají použít, je v 15-rejstrik-funkci.md.
Jedna vrstva pro všechny záznamy
Firmy, uživatelé, role, řešitelé, skupiny, typy ticketů, akce a widgety mají společné to, že se u nich dělá totéž: seznam za firmu, detail, zápis, mazání, kontrola práva, zápis do auditu. Napsat to osmkrát znamená osm skoro stejných souborů, ze kterých se jeden opraví a ostatní ne.
Proto tři vrstvy, každá napsaná jednou:
| Vrstva | Kde | Co dělá |
|---|---|---|
| Úložiště | src/data/store/ |
EntityStore<T> a dvě implementace. Volající nepozná, jestli běží Postgres nebo JSON soubor. |
| API | src/routes/crud.ts |
crudRouter vyrobí pětici endpointů včetně práva a auditu. |
| Klient | components/dashboard/EntityAdmin.tsx |
Tabulka, modál, validace, mazání. |
Nová entita v nastavení pak znamená: defineStore v modulu entity, jeden řádek
v bootstrap.ts, jeden crudRouter v settings.ts, jeden popis v
Settings.tsx. Nic víc.
crudRouter navic s volbou event publikuje <druh>.created, .updated
a .deleted s celym zaznamem v payloadu, takze klientsky sklad ciselniku
(lib/collections.tsx) se opravi bez dotazu. Po zapisu se obnovi jen cache
te entity (bootstrapDataRefresh(route)), ne vsechny.
Práva jsou data
Práv je 26 a jsou v katalogu (src/data/permissions.ts). Role je záznam,
ne konstanta v kódu: firma si může udělat vlastní roli s vlastní kombinací
práv. Systémové role (admin, agent, viewer) se měnit nedají, aby si nikdo
neodebral právo, kterým je odebírá.
Členství uživatele ve firmě nese roleIds, protože jeden člověk může být
v Automii řešitel a u Nordisu správce jejich servicedesku.
Neznámé právo se odmítne už při ukládání role. Kdyby se jen ignorovalo, překlep by znamenal roli, která tiše nic nesmí.
Prava se kontroluji u kazde route, za firmu zaznamu
Do zari 2026 se vetsina rout ptala jen "je clen firmy". Prava v katalogu
byla, ale viewer mohl zalozit konektor nebo smazat automatizaci. Ted:
| Co | Pravo |
|---|---|
| firmy | jen spravce platformy |
| uzivatele | spravce platformy, nebo user.manage jen ve sve firme |
| pozvanky | user.manage, role jen z te firmy |
| konektory (zalozeni, uprava, smazani, test) | connector.manage |
| automatizace (zalozeni, uprava, smazani, novy token) | automation.edit |
| vestavene akce na ticketu | pravo akce za firmu ticketu plus strop viditelnosti |
| prepnuti na jiny ucet, audit | impersonate, audit.view |
Spravce firmy s user.manage nenastavi platformAdmin, neprida clenstvi
v jine firme, nesahne na spravce platformy a nesmaze cloveka, ktery je i
v jine firme. Duvody a rozhodnuti "kdo koho zaklada" jsou
v 07-firmy-a-prava.md.
Firma z registru ARES
Zalozka Firmy ma vedle rucniho zalozeni cestu pres ARES
(components/dashboard/AresTenantDialog.tsx), jen pro spravce
platformy: IC nebo nazev, vyber firmy, vyber statutaru, kteri dostanou ucet
s roli spravce a zastupnou adresou IC-poradi@placeholder.cz. Zastupne
adresy se musi nahradit skutecnymi, jinak se ti lide neprihlasi. Firma pak
nese ico, dic, address a legalForm, IC je unikatni. Endpointy jsou
v 04-api.md.
Navigace chodí ze serveru
Co uživatel vidí za záložky, je průnik dvou věcí:
- co má firma zaplacené a zapnuté (
tenantFeatures), - na co má člověk právo.
Počítá to server (navFor) a klient jen kreslí. Kdyby si klient záložky
dovozoval sám, počítalo by se to na dvou místech a jednou by se to rozešlo -
a ta chyba by znamenala odkaz do 403.
Typy ticketů a tagy
Typ ticketu nese vlastní pole (klíč, popisek, typ, povinnost, nápověda) a může mít vlastní workflow stavů. Tag je volné označení bez polí za ním.
Rozdíl není kosmetický:
- Typ znamená "za tímhle stojí data". Objednávka má číslo objednávky a částku, takže se na ni dá navázat akce, která ta data potřebuje.
- Tag znamená "takhle si to označuju". Hodí se na filtry a widgety.
Akce se proto váže na typ nebo tag, ale akci s daty má smysl vázat na typ.
Pole object a list se v detailu ticketu ručně nevyplňují - plní je
automatizace. Textarea s JSONem by tam byla past, ne pohodlí.
Vydefinované akce
Akce a automatizační stromy jsou dvě různé věci a nemají mezi sebou žádnou společnou mezivrstvu. Automatizace běží sama, akce je tlačítko, které zmáčkne člověk.
Tělo akce je jedno z trojice:
| Tělo | Kdy | Příklad |
|---|---|---|
| Operace konektoru | běžný případ | odeslat objednávku do iDokladu |
| Vlastní strom | akce má víc kroků a rozhodování | dohledat kontakt, vystavit fakturu, odeslat e-mailem |
| Skript | nic z toho nestačí | vlastní výpočet nebo cizí API, které v katalogu není |
Server vrací k ticketu jen akce, které v té situaci opravdu jdou spustit: sedí typ nebo tag, projdou podmínky a volající na ně má právo. Klient nefiltruje nic.
Spuštění akce vrací 200 i když akce selhala - selhání akce není chyba API. V odpovědi je celé chybové hlášení a celý průběh se zapíše do logu ticketu.
Vestavěné akce (typ, tagy, skupina, přiřazení, stav, komentář) jsou zvlášť: mění ticket sám, ne cizí službu, a každá má vlastní právo.
Vlastní widgety
Widget je definice zdroje dat plus způsob zobrazení. Zdroj je ticket nebo automatizace, k tomu filtr a případné seskupení (podle stavu, kanálu, řešitele, typu, tagu).
Data pro celý přehled chodí jedním requestem. Deset dlaždic nesmí znamenat deset dotazů.
Viditelnost je stejná jako u služeb: všichni, konkrétní firmy, konkrétní lidé, nebo jen správce.
Přepnutí na jiný účet
Správce platformy se může podívat na portál očima konkrétního uživatele. Dvě věci, na kterých to stojí:
- Výchozí je jen pro čtení. Podívat se, co klient vidí, je mnohem častější než za něj něco měnit, a nechtěný zápis pod cizím jménem je to nejhorší, co se tady může stát. Zápis se musí zapnout vědomě a middleware ho jinak odmítne u všeho kromě GET.
- Všechno je v auditu včetně toho, kdo to doopravdy byl. Bez auditu nemá přepínání účtů co dělat v produktu.
Svůj původní token si klient odkládá do sessionStorage. Server o něm nic neví,
takže ho nemůže ani omylem prodloužit.
Co se ukládá a co se drží v paměti
| Data | Jak | Proč |
|---|---|---|
| Firmy, uživatelé, role, řešitelé, skupiny, typy, akce, widgety, záložky | withCache |
Čtou se při každém requestu, mění se zřídka. Kopie v paměti (byId je Map), obnova jen te entity po zápisu. |
| Tickety včetně logu, automatizace, incidenty, rozložení dashboardu | withMirror |
Mění se v paměti za provozu, po každé změně se celý záznam zapíše. Zápisy téhož ID jdou za sebou, ne naráz. |
| Audit | přímo do úložiště | Jen se připisuje, nikdy nečte při každém requestu. Oreza se davkou po 50 zapisech nebo jednou za minutu. |
| Konektory | vlastní úložiště | Nesou šifrovaná tajemství a potřebují částečný unikátní index. Viz 14-databaze.md. |
Čítače ID se při startu dopočítají z uložených záznamů, takže nový ticket nikdy nepřepíše starý.