Files
csbot-prototype/documentation/15-rejstrik-funkci.md
T
JiriUhlirandClaude Opus 5 a771834e57 Realne sluzby, OpenAI, odesilani e-mailu a helpdesk
Katalog srovnany s tim, co opravdu bezi na services.csbot.cz/apps:
trinact sluzeb dostalo pristupove udaje a levne cteci overeni, opravena
appId, ktera nikam nevedla (ppl, microsoft365, transcription), a GA4,
Search Console, Google Ads i Sklik ted stoji na aplikaci analytics,
kazda s vlastnimi udaji. Nove sluzby SAP Business One, Google Workspace
a Meta Ads. K tomu 23 skriptu, ktere s nimi opravdu neco delaji.

OpenAI jako prvni sluzba, ktera nebezi u nas: Service.baseUrl s absolutni
adresou, prepis pres <SLUZBA>_BASE_URL nebo adresu u konektoru, predpona
hlavicky u pole udaju (uzivatel vlepi holy klic, Bearer dopise runtime).
Dotaz na model, nahrani souboru, otazka nad souborem, prepis zvuku.
Skript umi odeslat soubor pres ctx.http.postForm (multipart, obsah Base64).

Sluzba E-mail pres SMTP. Neni to skript, ale vnitrni krok - SMTP neni HTTP.
Konektor nese schranku firmy, krok ma HTML telo, ve kterem se dosazene
hodnoty escapuji (znacky autora sablony jsou zamer, ostre zavorky od
zakaznika ne). Overeni konektoru se prihlasi na server a nic neodesle.

Helpdesk: Ticket.helpdeskSourceId drzi firmu, ktera pozadavek poslala,
vlastnikem zustava ta, ktera ho resi - jinak by ho resitel nemel ve sve
fronte. Komu pozadavek pripadne, urcuje Tenant.helpdeskProviderId.
Zadavatel vidi jen svoje pozadavky a smi k nim pripsat komentar.

Opravy v portalu:
- hlasky o ulozisti a odchozi IP vidi jen spravce platformy
- typ ticketu se v automatizaci vybira ze seznamu firmy, nebo dosadi z dat
- stav ticketu je otevreny naseptavac, ne ciselnik
- ticket jde zalozit rucne, zakaznik u nej neni povinny
- kanal se prejmenoval a parametry u webhooku jsou oznacene jako nepovinne
- srovnane markdown tabulky v cele dokumentaci

Co z teto davky jeste neni: prepinac firmy je porad jen stav uvnitr stranky
Prehled, takze se prepnuti neprojevi v Lidech ani jinde.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 07:40:16 +02:00

18 KiB

Rejstřík znovupoužitelných funkcí a komponent

K čemu je co, aby se za rok nepsalo znovu něco, co už existuje. Když píšeš druhou funkci, která dělá skoro totéž jako něco odsud, je to skoro vždycky chyba - buď se má použít ta původní, nebo se má rozšířit.

Podrobný popis je vždycky v komentáři u samotné funkce. Tady je jen jedna věta a kdy to použít.

Ukládání dat (server)

Rozhoduje se na jednom místě, viz 14-databaze.md. Volající nikdy nezjišťuje, jestli běží Postgres, soubor, nebo pamět.

Co Kde K čemu
defineStore<T>(kind) src/data/store/index.ts Založí úložiště pro nový druh záznamu. Jeden řádek na entitu.
initStores({databaseReady}) src/data/store/index.ts Vybere režim. Volá se jednou při startu, nikde jinde.
flushStores() src/data/store/index.ts Dopíše rozepsané zápisy. Jen při ukončení procesu.
withCache(store) src/data/store/cached.ts Kopie v paměti pro konfigurační entity, které se čtou při každém requestu (uživatelé, role, firmy). Čte se synchronně, obnovuje se po zápisu.
withMirror(store) src/data/store/mirror.ts Opačný směr než withCache: data se mění v paměti a po každé změně se celý záznam zapíše. Pro provozní data (tickety, automatizace, incidenty, rozložení).
isVisible(entity, options) src/data/store/types.ts Vidí volající tenhle záznam? Prázdný seznam firem znamená "nic", ne "vše".
nowIso() src/data/store/types.ts Časová značka. Ať se nepíše new Date().toISOString() na třiceti místech.
memorySnapshot / fileSnapshot src/data/snapshot.ts Nižší vrstva pod createLocalStore: atomický zápis JSONu s debounce. Přímo se nepoužívá.
db(), query, queryOne, transaction src/db/pool.ts Postgres. dbFor(tenantId) je připravený šev pro rozdělení na víc databází.
seal, open, sealAll, openAll src/db/secretBox.ts Šifrování přístupových údajů konektorů (AES-256-GCM). Nic tajného se neukládá jinak.
runMigrations() src/db/migrate.ts Migrace pod zámkem, jeden soubor = jedna transakce.

Entity a práva (server)

Co Kde K čemu
crudRouter(options) src/routes/crud.ts Celý CRUD nad jednou entitou: seznam, detail, vytvoření, úprava, mazání, právo, audit. Nová entita v nastavení = jeden crudRouter, ne pět handlerů.
readScope(req) src/routes/crud.ts Ze které firmy smí request číst. Povinný argument všech list volání.
accessFor(user, tenantId?) src/data/access.ts Co uživatel smí: práva, záložky, výchozí firma. Klient si nic nedovozuje sám.
permissionsOf(user, tenantId) src/data/permissions.ts Efektivní práva z rolí. Pětisekundová cache, invalidatePermissions() po zápisu.
hasPermission(...) src/data/permissions.ts Jedna kontrola. Používá ji crudRouter i ruční handlery.
navFor(...) src/data/tenantFeatures.ts Průnik toho, co firma má, a toho, na co má člověk právo. Navigace chodí ze serveru.
recordAudit(input) src/data/audit.ts Zápis do auditu. Nevrací chybu a nečeká se - rozbitý audit nesmí rozbít aplikaci.
enqueue(input) src/runtime/queue.ts Zařadí běh. Klíč proti dvojímu zařazení drží jeden běh na jednu událost.
claimBatch(limit) src/runtime/queue.ts Vezme další práci, spravedlivě po firmách. Místo, kde nad Postgresem musí být SKIP LOCKED.
onTicketEvent(kind, ticket) src/runtime/triggers.ts Změna ticketu zařadí navázané automatizace, včetně ochrany proti smyčce.
withRun(marker, work) src/runtime/context.ts Označí, který běh práci způsobil. Bez toho automatizace spouští sama sebe.
findBuiltinStep(...) src/runtime/builtinSteps.ts Kroky, které sahají do našeho úložiště, ne ven přes HTTP.
findPersonByExternalId(...) src/data/people.ts Řešitel podle ID z cizí aplikace, například voicebotId.
notify(input) src/data/notifications.ts Upozorní člověka. Nečeká se a nevyhazuje chyby, stejně jako audit.
runFlow(steps, context, options) src/runtime/executor.ts Vykoná strom kroků. Nikdy nevyhodí výjimku, chyba je výsledek. Používá to akce na ticketu i webhook, aby se strom choval všude stejně.
widgetCatalog(tenantIds, userId) src/data/widgets.ts Jediná definice toho, co jde položit na dashboard. Používá ji nabídka i kontrola ukládaného rozložení.
intakeEvent(input) src/data/ticketStore.ts Přijme událost zvenku: podle externího ID buď založí ticket, nebo ji navěsí na existující. Jediná cesta, kterou se událost stává ticketem.
getAgentStats(...) src/data/ticketStore.ts Výkon řešitelů: odbavené, mediány časů, vrácené, fronta. Používá to widget i detail osoby, aby čísla seděla.
findByExternalId(...) src/data/ticketStore.ts Ticket firmy podle externího ID. Klíč je dvojice firma a ID.
findByIntakeToken(token) src/data/tenants.ts Firma podle tokenu příjmu. Určuje i to, v jakém rozsahu je externí ID unikátní.
refreshCaches() src/data/bootstrap.ts Obnoví všechny kopie v paměti. Volá se po zápisu, který je může změnit.
bootstrapData({databaseReady}) src/data/bootstrap.ts Seznam všech entit a provozních dat. Nová entita se přidává tady, ne rozesetě po modulech.

Skripty a konektory (server)

Viz 11-skripty-konektoru.md.

Co Kde K čemu
runScript(id, inputs, ctx) src/scripts/runner.ts Spustí skript. Nikdy nevyhodí výjimku, chybu vrací jako výsledek s celým hlášením.
validateValues(...) src/scripts/values.ts Jedna kontrola pro vstupy i výstupy skriptu podle manifestu.
scriptUtil src/scripts/util.ts Nádobíčko pro skripty: pick, first, num, date, need, get, applyRules, fillJson. Skript nemá sahat na nic jiného.
createRedactor(...) src/scripts/util.ts Vyškrtá tajemství z textu před logováním. Používá se u všeho, co jde do logu.
applyRules, fillJson src/scripts/mapping.ts Transformace dat: pole na pole s převody, nebo objekt na objekt. Viz 13-transformace-dat.md.
getPath(obj, path) src/scripts/mapping.ts Čtení zakaznik.adresa.mesto z neznámého objektu.
resolveTarget(...) src/scripts/connections.ts Z konektoru poskládá adresu a hlavičky. Přístupové údaje nikam jinam nevedou.
serviceBaseUrl(service) src/scripts/connections.ts Adresa služby: naše aplikace ze SERVICES_BASE_URL, cizí (OpenAI) z jejího baseUrl. Přebít jde přes <SLUZBA>_BASE_URL.
targetSecrets(target) src/scripts/connections.ts Co se musí vyškrtat z logu. Vrací i holý klíč bez předpony Bearer , protože v něm ho cizí služby vracejí v chybách.
createHttp(...) src/scripts/http.ts HTTP se timeoutem, limitem odpovědi a rozlišením "zkusit znovu" a "marné".
isPrivateHost(host) src/scripts/http.ts Míří jméno do vnitřní sítě? Jedno pravidlo pro HTTP i pro SMTP server z konektoru.
sendMail(target, message) src/mail/smtp.ts Odešle e-mail přes SMTP z konektoru. Nikdy nevyhodí výjimku, vrací i to, jestli má smysl zkusit znovu.
verifySmtp(target) src/mail/smtp.ts Přihlásí se na server bez odeslání zprávy. Tím se ověřuje konektor e-mailu.
escapeHtml(value) src/data/templates.ts Escapuje dosazenou hodnotu v HTML šabloně. Značky autora šablony zůstávají, ostré závorky od zákazníka ne.
ctx.http.postForm(...) src/scripts/http.ts Odeslání souboru (multipart/form-data). Obsah přichází jako Base64, hranici dopisuje runtime.
scriptIdFor(serviceId, operationId) src/scripts/lookup.ts Který skript obsluhuje operaci z katalogu.

Klient

Co Kde K čemu
EntityAdmin components/dashboard/EntityAdmin.tsx Celá správa jedné entity: tabulka, modál, validace, mazání. Nová záložka nastavení = popis sloupců a polí, ne nová stránka.
parseJsonField components/dashboard/EntityAdmin.tsx Textové pole s JSONem na hodnotu, s hlášením, kde je chyba.
TicketActions components/dashboard/TicketActions.tsx CTA akcí na ticketu plus typ, tagy a vlastní pole. Seznam akcí chodí ze serveru už vyfiltrovaný.
ErrorDetail components/dashboard/ErrorDetail.tsx Rozbalovací celé chybové hlášení s kopírováním. Chyba se nikdy nezkracuje.
CustomWidgetCard components/dashboard/widgets/CustomWidget.tsx Vykreslí widget, jehož data počítá server: číslo, pruhy, tabulka výkonu, časová řada, seznam, data z konektoru.
TicketTable components/dashboard/TicketTable.tsx Tabulka ticketů pro všechna místa. Na mobilu se místo posouvání do strany kreslí karty.
TicketEvents components/dashboard/TicketEvents.tsx Příchozí události ticketu včetně celého přijatého JSONu.
ViewSwitch components/dashboard/ViewSwitch.tsx Přepínač tabulka nebo dlaždice. Používají ho všechny seznamy.
FlowCanvas s start components/dashboard/flow/FlowCanvas.tsx Tentýž strom kroků i bez spouštěče - pro tělo akce, které spouští člověk.
MappingEditor components/dashboard/flow/MappingEditor.tsx Editor transformací v obou režimech (pole na pole, JSON).
DataState components/dashboard/DataState.tsx Načítání, chyba, prázdno. Ať to každá stránka nekreslí po svém.
apiFetch<T> lib/api.ts Jediná cesta na API: base path, token, ApiError s celým hlášením ze serveru.
useApiQuery<T> lib/useApiQuery.ts Načtení dat do stránky včetně reload. S body pošle POST (dávkové načtení), s enabled: false se neptá vůbec.
cn(...) lib/cn.ts Skládání tříd. Podmíněné třídy nikdy ručně přes šablonu.
format* lib/format.ts Čísla, procenta, datum, relativní čas, trvání. Formátování se nepíše v komponentě.
serviceIcon(key) lib/serviceIcons.ts Klíč ikony ze serveru na komponentu. Server neposílá komponenty.
usePageMeta lib/usePageMeta.ts Titulek stránky.
Badge, Button, Modal, Card, ... components/ui/ Základní prvky. Nový vzhled tlačítka patří sem, ne do stránky.

Pravidla, která z toho plynou

  1. Nová entita v nastavení: defineStore v modulu entity, řádek v bootstrap.ts, crudRouter v settings.ts, popis v Settings.tsx. Nic jiného se psát nemusí.
  2. Data, která se mění za provozu, jdou přes withMirror. Data, která se čtou při každém requestu a mění zřídka, přes withCache. Obojí nikdy.
  3. Chybu se nesmí zkracovat. Server vrací celé hlášení, klient ho umí zobrazit (ErrorDetail).
  4. Klient nepočítá práva. Co smí, říká accessFor.