Files
csbot-prototype/documentation/17-nastaveni-a-prava.md
T
JiriUhlir c25e826766 Poptavka z webu je ticket, prilohy, udaje provozovatele, kolacovy graf
Provozovatel portalu: firma s priznakem portalOperator (jen jedna, zapnuti
odebere ostatnim) a novymi poli contactEmail, contactPhone, website vedle
ico, dic, adresy a pravni formy. Verejny GET /api/public/brand vraci jeji
udaje a web je bere pres useBrand() na kontaktu, v paticce, O nas,
prihlaseni i v titulku; brand.ts je jen zaloha.

Poptavka z webu zaklada u provozovatele ticket kanalu form: predmet
"Poptavka: tema", telo JSON s poli formulare, tag Poptavka plus tema,
zakaznik z formulare, poznamka v logu. Bez provozovatele se jen zaloguje.

Prilohy ticketu: formular az 3 soubory po 5 MB, ticket az 10; nahrani,
seznam, stazeni a smazani (pravo ticket.comment, strop viditelnosti,
poznamky v logu, audit). Soubor jde v JSON jako Base64 a lezi v beznem
ulozisti, bez nove zavislosti; strop tela jen na techto cestach.

Vlastni widget s kreslenim Graf umi i pocet ticketu se seskupenim jako
kolac (PieChart.tsx, ciste SVG, osm barev z tokenu, zbytek jako ostatni).

OpenAPI rozdelene na mensi soubory (102 cest, 28 schemat overeno shodnych),
28 novych testu (135 celkem), dokumentace aktualizovana.
2026-09-09 19:35:00 +02:00

12 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 src/routes/settings/<entita>.ts a mount v settings/index.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
lide (resitele = clenove firmy) people.manage jen ve sve firme, zaklada ucet s clenstvim
pozvanky user.manage, role jen z te firmy
konektory (zalozeni, uprava, smazani, test) connector.manage
automatizace (zalozeni, uprava, smazani, novy token) automation.edit
stav incidentu incident.manage za firmu incidentu; platformni jen spravce platformy
vestavene akce na ticketu pravo akce za firmu ticketu plus strop viditelnosti
prilohy ticketu (nahrani, smazani) ticket.comment za firmu ticketu plus strop viditelnosti; seznam a stazeni jen strop
provozovatel portalu jen spravce platformy, je to pole firmy
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.

Zalozka Lide (people.manage) uz nespravuje zvlastni zaznam resitele: resitel je clenstvi uctu ve firme a ID resitele je ID uctu, viz 06-tickety.md. Zalozeni cloveka v Lidech zalozi ucet (heslo nepovinne, bez nej nahodne) nebo prida clenstvi uctu, ktery uz s tim e-mailem existuje. Upravit jde jmeno, e-mail, popisek, kapacita, externi ID a role clenstvi; smazani odebere jen clenstvi. Sprava uctu v Nastaveni (/users, spravce platformy) k clenstvi bere i seesAllTenant, role, capacity, externalIds a enabled a pri uprave je zachova. Prepinac enabled v Lidech je za clenstvi: vypne cloveka jen v teto firme, cely ucet vypina jen sprava uzivatelu.

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. Ucet je zaroven resitel (resitel je clenstvi uctu), takze jsou hned v Lidech; funkce z rejstriku je popisek clenstvi. 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.

Provozovatel portalu a kontakt firmy

Zalozka Firmy (components/dashboard/settings/TenantsAdmin.tsx) ma u firmy prepinac Provozovatel portalu (portalOperator) a tri kontaktni pole: contactEmail, contactPhone, website. Provozovatel je firma, ktere chodi poptavky z verejneho kontaktniho formulare jako tickety a ze ktere web bere kontaktni a fakturacni udaje (GET /api/public/brand). V seznamu firem ma odznak "provozovatel".

Provozovatel je prave jeden. Zaskrtnuti u jine firmy sunda priznak te puvodni (afterWrite CRUD firem vola clearOtherOperators), obe zmeny prijdou do portalu jako tenant.updated. Nastavuje ho jen spravce platformy, stejne jako zaklada firmy - kdo portal provozuje, je nase rozhodnuti, ne zakaznicke. Kontaktni pole jde vyplnit u kazde firmy, vyznam maji jen u provozovatele; prazdne pole se uklada jako null. Duvody v 07-firmy-a-prava.md.

Navigace chodí ze serveru

Co uživatel vidí za záložky, je průnik dvou věcí:

  1. co má firma zaplacené a zapnuté (tenantFeatures),
  2. 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).

Kresleni "Graf" ma dva tvary podle zdroje: casova rada je krivka, pocet ticketu se seskupenim je kolac (widgets/PieChart.tsx, nejvys osm vysecu, zbytek jako "ostatni"). Pocet bez seskupeni graf odmitne uz pri ulozeni, kolac z jedne vysece nic nerika. Tataz seskupena data jako "Tabulka" jsou sloupce: sloupce srovnavaji, kolac ukazuje podily.

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í:

  1. 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.
  2. 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ý.