.gitignore mel vzorec `data/`, ktery se shodl i se `src/data/`. Sestnact zdrojovych souboru tim tise chybelo v gitu vcetne cele slozky `src/data/store/`. Opraveno na `/data/`, stejne v .dockerignore. Tickety vcetne logu, automatizace, incidenty a rozlozeni dashboardu se po kazde zmene ukladaji. Pomocnik `withMirror` je opak `withCache`: data se meni v pameti a zapisuji cela, misto aby se po zapisu znovu nacitala. Citace ID se pri startu dopocitaji z ulozenych zaznamu, takze novy ticket neprepise stary. Detail ticketu umi typ, tagy, vlastni pole typu a prehozeni na skupinu. Nastaveni ma prepnuti spravce na jiny ucet, vychozi jen pro cteni. Skupiny resitelu chodi spolu s lidmi jednim requestem. Dokumentace: rejstrik znovupouzitelnych funkci (15), navrh monetizace a ceny za krok (16), popis nastaveni a prav (17). Doplneny endpointy do openapi.ts, petice CRUD rout se generuje jednou funkci. Overeno v rezimu souboru: zmeny prezily tvrde ukonceni procesu a po restartu byly zpatky vcetne logu ticketu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.8 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.
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í.
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, obnova 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. |
| Audit | přímo do úložiště | Jen se připisuje, nikdy nečte při každém requestu. |
| 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ý.