Cely navrh rozsireni: role, firmy, typy ticketu, akce, widgety, audit
Implementace vsech bodu z documentation/09-navrh-rozsireni.md. Vsechno lezi v obecnem ulozisti, ktere umi Postgres i JSON soubor. Zaklad, aby se nepsalo osmkrat totez: - src/data/store/: jedno rozhrani EntityStore, dve implementace (local se souborem nebo pameti, postgres nad tabulkou records). Vyber je na jednom miste v store/index.ts - src/data/store/cached.ts: synchronni kopie v pameti pro entity ctene pri kazdem requestu (uzivatel v autorizaci, firmy pri vypoctu prav). Bez toho by se autorizacni middleware musel predelat na async - src/routes/crud.ts: fabrika na CRUD routy. Kazda entita by jinak znamenala stejnych sto radku a sedmkrat by se opravila spatne - src/data/bootstrap.ts: jedno misto, kde je seznam entit a jejich vychozi sady - migrace 002_records.sql: jedna tabulka s JSONB. Tvary se jeste hybou a nikdo se nad nimi nedotazuje po polich. Az se to usadi, entita se povysi na vlastni tabulku, presne jako uz maji konektory Prava a role (bod 5): - role jsou zaznamy, pravo je retezec, katalog prav je zdroj pravdy. Union 'admin' | 'agent' na ucetni a skladnika nestacil a pridavat hodnoty je slepa ulicka, kazdy klient chce jine - Membership.roleIds misto role. Systemove role admin a agent zustavaji, takze se zadny ucet nemusel predelavat - accessFor vraci prava i zalozky. Klient si nic nedovozuje Zalozky a limity za firmu (bod 6): - TenantFeatures: moduly, limity, zpristupnene sluzby. Dve vrstvy s jinym vlastnikem, ktere se nesmi michat: co ma firma zaplacene nastavujeme my, kdo z jejich lidi to smi nastavuje jejich admin. Efektivni viditelnost je prunik, takze vypnuty modul neexistuje ani pro admina te firmy - navigace ze serveru, ne konstanta na klientovi Firmy, uzivatele, resitele a skupiny: CRUD vcetne clenstvi a hesel. Heslo se z API nikdy nevraci, ani jako hash. Skupiny resitelu kvuli tomu, ze prehazovat praci na jmeno nestaci - clovek chce rict "tohle je pro ucetni". Typy ticketu a akce (body 1, 2, 3): - typ ticketu s vlastnimi polemi, plus tagy. Akce se vazou na typ nebo tag, ale za tagem nestoji zadna pole, takze akce na tagu umi jen vestavena pole - Ticket dostal typeId, fields, tags a assigneeGroupId - definice akce s telem jako operace, strom nebo skript. Pravo vznika spolu s akci jako action:<id>, admin pak zaskrtava akce, ne prava - CTA na ticketu filtruje server podle typu, tagu, podminek a prav. Kdyby to pocital klient, pocitalo by se to na dvou mistech - vestavene akce (typ, tagy, skupina) jdou pres tutez fabriku, takze maji svoje pravo a projdou auditem - spusteni zapise do logu ticketu hned, jeste nez se neco stane Vlastni widgety (bod 4): - rozdeleni na render a source, groupBy, filtr je tentyz, ktery umi seznam ticketu. Uzivatel nepise dotazy - jeden batch endpoint na cely prehled. Widget, ktery selze, vraci chybu na sve pozici a nezhasne prehled Audit a impersonace (bod 6c): - audit zapisuje i odepreni, jinak by pokusy o cizi firmu nikde nezustaly - impersonace: jen spravce platformy, nikdy na jineho spravce platformy, 30 minut bez obnoveni, vychozi jen cteni. Zapis pod rezimem cteni vraci 403 na urovni middleware, ne az v handleru Overeno bez databaze: vsech devet ulozist se nacte, role se zaloz1 a prezije restart, agent dostane 403 na spravu roli a uzsi navigaci, neznama prava se odmitnou, heslo se nevraci, typ a tagy ticketu se ulozi, CTA se objevi, widget data pocitaji vcetne seskupeni, impersonace odmitne zapis i prepnuti na admina, audit obsahuje actedBy. Degradace pri nedostupne databazi taky overena: migrace selzou, jede se do souboru a rekne se proc. Neovereno: migrace 002_records.sql proti zive databazi. Kontejner uz nebyl k dispozici, generickou vrstvu drzi tentyz pool a migrator jako konektory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6e3d0640ff
commit
e7cf499a0b
@@ -0,0 +1,33 @@
|
||||
-- Obecna tabulka zaznamu.
|
||||
--
|
||||
-- Jedna tabulka pro vsechny entity, ktere jsou zatim jen konfigurace: typy
|
||||
-- ticketu, definice akci, role, zalozky za firmu, vlastni widgety, audit.
|
||||
--
|
||||
-- Proc jedna tabulka a JSONB, a ne osm typovanych:
|
||||
-- - tvary se jeste hybou, kazda zmena by znamenala migraci,
|
||||
-- - nikdo se nad nimi nedotazuje po jednotlivych polich, cte se cely zaznam,
|
||||
-- - osm typovanych tabulek znamena osm mapovacich vrstev, ze kterych se jedna
|
||||
-- casem opravi a ostatni ne.
|
||||
--
|
||||
-- Az se tvar usadi a bude potreba dotazovat se dovnitr nebo hlidat vazby,
|
||||
-- entita se povysi na vlastni tabulku. Presne to uz maji konektory: nesou
|
||||
-- sifrovana tajemstvi a potrebuji castecny unikatni index.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS records (
|
||||
-- Druh entity, napr. 'ticketType', 'ticketAction', 'role'.
|
||||
kind text NOT NULL,
|
||||
id text NOT NULL,
|
||||
-- NULL = platformni zaznam (nas, ne zakaznikuv). Napr. systemova role.
|
||||
tenant_id text,
|
||||
data jsonb NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
|
||||
PRIMARY KEY (kind, id)
|
||||
);
|
||||
|
||||
-- Nejcastejsi dotaz je "vsechny zaznamy druhu X pro firmu Y", proto index
|
||||
-- v tomhle poradi. `tenant_id` je v nem druhy, ne prvni: druh entity zuzuje
|
||||
-- vic, protoze firem je radove desitky a druhu jednotky.
|
||||
CREATE INDEX IF NOT EXISTS records_kind_tenant_idx
|
||||
ON records (kind, tenant_id);
|
||||
Reference in New Issue
Block a user