Files
csbot-prototype/documentation/03-architektura-a-mapa-kodu.md
T
JiriUhlirandClaude Fable 5.1 ea9387bea9 Seed z repozitare, zatezove testy, worker bez spanku, dialogy bez rozmazani
Nastaveni prezije nasazeni: seed/records.json se pri prazdnem ulozisti
nacte misto ukazkovych dat (zive uloziste se nikdy neprepisuje). Soubor
nese soucasny stav produkce (firmy s provozovatelem, role, typ ticketu,
akce, widgety, skupiny, rozlozeni). Novy GET /api/admin/export a skript
npm run seed:export pro dalsi exporty, Dockerfile slozku kopiruje.

Vykonnostni testy (npm run test:perf) nad 200 firmami a 10 000 tickety
a zatezovy skript (npm run load) proti bezici instanci vcetne davky
udalosti na webhook. Mereni odhalilo strop workeru: po obsazeni vsech
mist spal sekundu, takze fronta odbavila nejvys 4 behy za sekundu. Ted
ceka na prvni dokonceny beh: 500 udalosti za 1,3 s (395 behu/s). Strop
posluchacu streamu zvednut na 2 000.

Dialogy: prekryv modalu a menu v portalu bez backdrop-blur, tecka Zive
pulzuje jen pri navazovani spojeni - rozmazani cele obrazovky pod trvalou
animaci sekalo video vedle portalu. Bublina udalosti drzi 0,5 s.

Dokumentace 14, 19, 20, 22, 04, 01, 03, 15 a 99 aktualizovana.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 20:41:42 +02:00

22 KiB

03 - Architektura a mapa kodu

Technologie

Vrstva Technologie
Server Node.js 20, Express 4, TypeScript, ESM
Web React 18, Vite 6, TypeScript, Tailwind 4, React Router 6
Auth JWT (jsonwebtoken), hesla bcrypt
Validace zod
Docs swagger-ui-express nad rucne psanou OpenAPI definici

Jeden package.json pro server i web (znamy stav, viz nize). Runtime zavislosti jsou v dependencies, nastroje pro build webu v devDependencies - runtime image je pak instaluje pres --omit=dev.

Build a kontroly

tsc   src/**.ts   ->  dist/*.js
vite  web/        ->  dist/public/

Server obsluhuje dist/public jako statiku. Dockerfile kopiruje do vysledneho image dist a connectors/ - skripty konektoru se ctou za behu ze souboru, ne z buildu.

Prikaz Co dela
npm run build server (tsc) a web (vite build)
npm run typecheck tsc --noEmit pro oba tsconfig, nic nezapisuje
npm run lint eslint .: server, web, testy i connectors/
npm run format prettier --write .; format:check jen kontroluje
npm run test vitest run nad tests/**; test:watch pri vyvoji

Build, lint i testy se spousti jen se svolenim uzivatele (pravidlo 2 v D:\GitHubRepository\CLAUDE.md).

Oba tsconfig maji strict, noUncheckedIndexedAccess, noUnusedLocals a noFallthroughCasesInSwitch. Index do pole nebo slovniku je tak T | undefined a kod to musi osetrit; ! na umlceni se nepouziva. Prave tahle volba odhalila dve skryte chyby (prodleva fronty a Retry-After), viz 99-zmeny.md.

Nastroje v korenu

Soubor K cemu
eslint.config.js typescript-eslint, react-hooks v7 pro web, connectors/** jako obycejny JS bez globalu, scripts/**/*.mjs jako Node ESM; zadne any, zadny prazdny catch
.prettierrc, .prettierignore jednotne formatovani (jednoduche uvozovky, sirka 100)
.editorconfig odsazeni, konce radku a kodovani pro editor
.nvmrc Node 20, stejne jako engines v package.json
.env.example vsechny promenne prostredi s popisem; .env neni v gitu
vitest.config.ts testy z tests/**/*.test.ts, alias @shared, tests/setup.ts pred kazdym souborem
tests/tsconfig.json typecheck testu nad src/ bez emitu
Dockerfile vicefazovy build, runtime jen s --omit=dev, kopiruje dist, connectors a seed
seed/records.json nastaveni z repozitare pro prazdne uloziste, vystup npm run seed:export; viz 14-databaze.md
scripts/export-seed.mjs pomocny skript vyvoje (npm run seed:export): prihlasi se, stahne GET /api/admin/export a zapise seed/records.json

Mapa kodu - server

Cesta K cemu je
src/index.ts start: nacteni dat, worker, listen, signaly, process.on; nic jineho
src/app.ts createApp(): middleware, routery, health, Swagger, statika, SPA. Bez listen, aby sla postavit v testu (supertest)
src/config.ts jedine misto, kde se cte process.env; serviceBaseUrlOverride(variable) pro <SLUZBA>_BASE_URL
src/openapi/index.ts buildOpenApiDocument(): sklada dokument, servers s prefixem proxy
src/openapi/helpers.ts crudPaths a opakujici se parametry, tela a odpovedi
src/openapi/components/ schemata a zabezpeceni po domenach (common, auth, tickets, attachments, automations, connectors, scripts, settings, admin), index.ts je sklada
src/openapi/paths/*.ts cesty po routerech: ops, auth, dashboard, tickets, automations, settings, connectors, scripts, helpdesk, invites, admin, contact, attachments, public, webhook
src/types.ts typy uzivatele a JWT payloadu
src/shared/ ciste typove moduly API, jediny zdroj typu pro server i web
src/middleware/auth.ts requireAuth, requireRole, requirePlatformAdmin
src/middleware/asyncHandler.ts wrap, safeRouter: odchyceni odmitnute promise v handleru
src/middleware/tenant.ts attachAccess, tenantOrDeny, scopeOrDeny: firma requestu na jednom miste
src/middleware/rateLimit.ts limit requestu v pameti, 429 s Retry-After
src/middleware/validation.ts validationError, jeden tvar chyby validace
src/lib/secure.ts timingSafeEqualString pro tokeny v adrese
src/net/guard.ts kontrola adresy, cteni tela s limitem, popis chyby site - pro vsechno, co vola ven
src/events/bus.ts sbernice udalosti, ze ktere cerpa SSE stream, udalost nese firmu
src/routes/auth.ts prihlaseni, odhlaseni, kdo jsem
src/routes/crud.ts crudRouter: fabrika CRUD nad jednou entitou
src/routes/dashboard/index.ts mount routeru dashboardu, attachAccess jednou za request
src/routes/dashboard/misc.ts prava, prehled, uloziste, katalog sluzeb
src/routes/dashboard/tickets.ts seznam s filtrem, rucni zalozeni, stavy, vytizeni, detail
src/routes/dashboard/people.ts resitele pro nabidky a detail cloveka
src/routes/dashboard/automations.ts strom akci, validace, webhook, fronta behu
src/routes/dashboard/incidents.ts incidenty: seznam, detail, posun stavu
src/routes/dashboard/layout.ts katalog widgetu a ulozene rozlozeni
src/routes/dashboard/intake.ts adresa prijmu udalosti a jeji obnova
src/routes/dashboard/notifications.ts upozorneni a pocet otevrenych ticketu
src/routes/dashboard/clientCrash.ts hlaseni padu portalu, z nej incident
src/routes/dashboard/shared.ts strankovani: pageFrom, paginate
src/routes/dashboard/attachments.ts prilohy ticketu: seznam, nahrani, stazeni, smazani; pravo ticket.comment
src/routes/ticketActions.ts akce nad ticketem vcetne vestavenych, pravo za firmu ticketu
src/routes/settings/index.ts mount routeru nastaveni
src/routes/settings/{tenants,users,roles,people,groups,features,ticketTypes,actions,widgets}.ts jedna entita = jeden soubor nad crudRouter; people a users maji vlastni handlery
src/routes/settings/catalog.ts co jde v nastaveni zvolit: prava, moduly, limity, widgety
src/routes/settings/shared.ts memberOf pro ucty a resitele
src/routes/ares.ts firma z registru ARES, jen spravce platformy
src/routes/admin.ts prepnuti na jiny ucet, audit, export nastaveni (/api/admin/export)
src/routes/connectors.ts konektory firmy, overeni, nastroje MCP
src/routes/scripts.ts, tenantScripts.ts skripty konektoru a skripty firmy
src/routes/stream.ts SSE stream zmen, filtr podle firem uzivatele
src/routes/webhook.ts verejny prijem dat do automatizace
src/routes/contact.ts poptavkovy formular z webu, zaklada ticket provozovateli portalu
src/routes/public.ts verejne udaje bez prihlaseni: GET /api/public/brand z provozovatele
src/routes/bodyLimit.ts jsonLimitFor, hasOwnBodyLimit: strop tela pro routy se soubory v base64
src/ares/client.ts klient verejneho API ARES
src/data/store/ tri rezimy uloziste, withCache, withMirror, initStores; seedFile.ts (nastaveni z repa do prazdneho uloziste, SEED_KINDS)
src/data/seedExport.ts exportSeed: vsechny zaznamy konfiguracnich druhu z ulozist pro seed/records.json
src/data/snapshot.ts atomicky zapis JSONu pro rezim file
src/data/ticketStore.ts fasada nad src/data/tickets/, importy zustavaji
src/data/tickets/ index (verejne API), model (tvar, toTicket), state (pamet a indexy), persist (zapis, initTickets), queries (seznam, detail, strop viditelnosti), store (zapisy: zalozeni, stav, resitel, typ, tagy, skupina, komentar, poznamka o priloze), intake (udalost zvenku), trace (log prubehu), stats (vytizeni a vykon), seed, remap
src/data/people.ts resitele jako pohled na clenstvi uctu (personView), skupiny
src/data/migratePeople.ts jednorazovy prevod starych zaznamu resitelu ppl_ na ucty
src/data/tenants.ts firmy, ktere portal pouzivaji, vcetne udaju z ARES; operatorTenant, clearOtherOperators
src/data/attachments.ts prilohy ticketu: kontrola davky, zapis s obsahem v base64, cteni, mazani
src/data/access.ts kdo co vidi - jedno misto pro cely portal
src/data/widgets.ts katalog widgetu prehledu
src/data/dashboardLayouts.ts rozlozeni dashboardu za dvojici uzivatel a firma
src/data/incidentStore.ts incidenty vcetne zmen a udalosti, filtr na firmu povinny
src/data/automationStore.ts fasada nad src/data/automations/
src/data/automations/ index, model (tvar, rulesOf, matchOf), state (pamet, citac ID), persist (zapis, initAutomations), store (cteni a zapisy, recordRun), validation (pocet kroku, nedodelky, druh), webhook (token, posledni volani), runs (historie po dnech), seed, seedDemo, remap
src/data/services.ts fasada nad src/data/services/
src/data/services/index.ts katalog za behu: findService, actionsFor, setScriptActions, setMcpOperations, withRuntimeOptions, serviceCatalog
src/data/services/catalog/ staticky zapis po skupinach: triggers, incident, ticket, crm, finance, logistics, email, messaging, social, office, analytics, ai, mcp, tools, polstryn; index.ts urcuje poradi v nabidce
src/data/conditions.ts typy parametru a operatory podminek
src/data/templates.ts sablony {{parametr}} v nastaveni kroku
src/data/flowScope.ts co je videt v kterem miste stromu
src/data/users.ts uzivatele portalu, demo ucty
src/data/mock.ts souhrn pro prehled a casova rada grafu
src/runtime/ fronta, worker, executor stromu, vestavene kroky, planovac, sandbox skriptu firmy
src/runtime/scripts/ runtime skriptu konektoru: registr, runner, HTTP, napojeni, manifest, kontrola hodnot, mapping, util (vcetne ctx.util)
connectors/ skripty konektoru (obycejny JS) a _sablona.js; cesta z config.scriptsDir, promenna SCRIPTS_DIR
src/mcp/ klient MCP, prihlaseni, dialekty, errors.ts se spolecnou chybou prihlaseni
tests/ vitest, stejna cesta jako modul (tests/data/tickets.test.ts pro src/data/tickets/); setup.ts nastavi rezim pameti a umlci console.info

Mapa kodu - web

Cesta K cemu je
web/src/main.tsx vstupni bod, basename routeru podle prefixu proxy
web/src/App.tsx routovani, portal se nacita lazy
web/src/index.css design tokeny a vlastni utility Tailwindu
web/src/config/brand.ts nazev, claim a staticka zaloha kontaktu; kontakty bere web z provozovatele pres useBrand
web/src/lib/files.ts soubor na base64, kontrola poctu a velikosti, limity stejne jako na serveru
web/src/lib/api.ts fetch wrapper, sprava tokenu, skladani adres, auth:expired na 401
web/src/lib/eventStream.ts cteni SSE streamu pres fetch
web/src/lib/collections.tsx klientsky sklad ciselniku za firmu, opravovany z udalosti
web/src/lib/ticketEvents.ts oprava seznamu ticketu z payload.ticket bez dotazu
web/src/lib/options.ts pevne ciselniky (priority)
web/src/lib/flow.ts ciste funkce nad stromem automatizace
web/src/lib/exampleBody.ts vzorove telo spoustece pro ukazku a strom modelu
web/src/lib/serviceIcons.ts klic ikony ze serveru na komponentu lucide
web/src/hooks/useApiQuery.ts nacitani dat, cache, spolecny debounce, refreshing misto odmontovani
web/src/hooks/useSubmit.ts odeslani formulare: busy, chyba, issues na jednom miste
web/src/hooks/useUnsavedChanges.ts varovani pri odchodu z rozepsaneho formulare
web/src/hooks/useLatest.ts ref s posledni hodnotou pro callbacky mimo zavislosti effectu
web/src/hooks/useSyncFromSource.ts prevzeti dat ze zdroje do rozepsaneho stavu pri vykresleni, ne v effectu
web/src/hooks/useMediaQuery.ts sirka obrazovky pres useSyncExternalStore
web/src/hooks/usePageMeta.ts titulek a popis stranky
web/src/hooks/useBrand.ts udaje provozovatele z /api/public/brand nad zalohou brand.ts, jeden sdileny dotaz
web/src/types/ fasada nad src/shared (alias @shared/*), zadne vlastni typy API
web/src/components/ui/ zakladni prvky: Badge, Button, Card, Chip, Modal, Section, Spinner, ...
web/src/components/ui/Table.tsx Table, TableHead, Th, TableRow, Td: jedna tabulka seznamu pro EntityAdmin, InvitePanel, People, AuditView
web/src/components/ui/ServiceIcon.tsx ikona sluzby podle klice z katalogu, misto const Icon = serviceIcon() v JSX
web/src/components/ui/form/ Field, Input, Select, Textarea, FilePicker, controlClass: jedna sada trid
web/src/components/dashboard/ shell portalu, dlazdice, graf, stream, TicketCard
web/src/components/dashboard/EntityAdmin.tsx, EntityForm.tsx sprava jedne entity: tabulka a formular v modalu
web/src/components/dashboard/flow/ strom akci: FlowCanvas a karty ActionCard, ConditionCard, ForeachCard, StepControls; k tomu TriggerConfig, SampleBody, ModelTree, WebhookCalls, MappingEditor
web/src/components/dashboard/scripts/ TestPanel (zkusebni spusteni) a CodeEditor (uprava kodu) pro stranku Skripty
web/src/components/dashboard/settings/ TenantsAdmin (firmy, provozovatel, kontakt), FeaturesAdmin (zalozky a limity), AuditView, types
web/src/components/dashboard/TicketAttachments.tsx, TicketAssignPanel.tsx sekce priloh a panel resitele v detailu ticketu
web/src/components/dashboard/widgets/ WidgetCard, WidgetPicker, CustomWidget, EditBar (lista uprav rozlozeni)
web/src/components/dashboard/TicketTrace.tsx log ticketu jako strom
web/src/components/dashboard/TicketWorkload.tsx prehled, kdo co ma u sebe
web/src/components/home/ sekce homepage
web/src/pages/ jedna stranka je jeden soubor
web/src/pages/dashboard/connectors/ casti stranky Konektory: karta, editor, log, nastroje

Soubory nad 500 radku

Zasada rika, ze soubor nad 500 radku je signal k rozdeleni. Po rozdeleni zustavaji tri, kazdy z duvodu (TicketDetail sel pod hranici vyclenenim TicketAssignPanel a TicketAttachments, Settings vyclenenim TenantsAdmin):

Soubor Proc zustava
web/src/pages/dashboard/AutomationDetail.tsx stranka drzi stav stromu a ukladani; casti bez stavu uz jsou ve flow/
web/src/components/dashboard/flow/MappingEditor.tsx dva rezimy editoru nad jednim stavem, deleni by stav zdvojilo
src/data/services/catalog/ticket.ts jedna sluzba s nejvic operacemi; deleni jedne sluzby do dvou souboru by rozbilo "jedna vec v jednom souboru"

Na serveru zustava dalsich sedm, ktere strukturalni pruchod nedelil, protoze nebyly v zadani a kazdy je jeden souvisly modul: runtime/builtinSteps.ts (929, vestavene kroky; kandidat na slozku runtime/steps/ po kroku), mcp/client.ts (884, protokol MCP; kandidat na oddeleni handshake a volani nastroju), runtime/executor.ts (779; kandidat na vycleneni vyhodnoceni podminek), routes/connectors.ts (677; kandidat na routes/connectors/), routes/ticketActions.ts (599), routes/widgetData.ts (587), mcp/auth.ts (527). Deli se pri nejblizsi praci v nich, ne naraz.

Dalsi velke soubory (builtinSteps.ts, mcp/client.ts, executor.ts, routes/connectors.ts) jsou kandidati na priste, az se do nich bude sahat.

Klicova rozhodnuti

Jeden container misto dvou. AppFactory nasazuje jednu aplikaci, proto Express obsluhuje i statiku. Odpada CORS i druha deploy jednotka.

SSE misto WebSocketu. Tok dat je jednosmerny, server ke klientovi. Klient posila zmeny beznym REST volanim. SSE prochazi reverse proxy bez zvlastni konfigurace.

Stream pres fetch, ne pres EventSource. EventSource neumi poslat hlavicku Authorization a token by musel byt v adrese, odkud se dostane do access logu. Cenou je rucni parsovani a rucni znovupripojeni v web/src/lib/eventStream.ts.

Ceske cesty v URL. /sluzby, /o-nas, /prihlaseni, /dashboard/tickety. Kod zustava anglicky.

Uloziste je za rozhranim. Tri rezimy (Postgres, soubor, pamet), jedno rozhrani a jedno rozhodnuti v initStores. Routy nevedi, ktery rezim jede. Podrobnosti v 14-databaze.md.

Typy API jsou jednou. Ciste typove moduly v src/shared ctou server i web, web pres alias @shared/*. Kopie typu na klientovi se jednou rozejde se serverem a prekladac to nepozna. web/src/types/ je jen fasada, ktera je re-exportuje; ucet uzivatele bere i AuthContext ze @shared/users.

Kazdy async handler je odchyceny. Routy vznikaji pres safeRouter, odmitnuta promise skonci jako 500 s logem, ne padem procesu. Bez toho stacil jeden zapomenuty try a AppFactory restartovala container.

Firma requestu se pocita jednou. attachAccess da do req.access, co uzivatel smi, a tenantOrDeny z toho odvodi firmu. Kazda route, ktera si to pocitala sama, to delala trochu jinak.

Filtr na firmu je povinny argument. listTickets, listPeople i listAutomations vyzaduji tenantIds. Zapomenuty filtr tak neznamena "vse", ale nezkompiluje se. Podrobnosti v 07-firmy-a-prava.md.

Prava se nikdy nedovozuji na klientovi. Server vraci GET /api/dashboard/access s tim, co uzivatel smi. Kdyby si to klient pocital sam, pocitalo by se to na dvou mistech a jednou se to rozejde.

Resitel je clenstvi uctu. ID resitele je ID uctu, Person je jen pohled na ucet a jedno jeho clenstvi ve firme (jmeno a e-mail z uctu, popisek, kapacita a externi ID z clenstvi). Driv byl resitel vlastni zaznam spojeny s uctem pres e-mail a zmena e-mailu vazbu tise rozbila. Stara uloziste prevadi migratePeople pri startu. Podrobnosti v 06-tickety.md.

Filtrovani ticketu dela server. Klient posila query parametry a dostane hotovy seznam. Kdyby filtroval sam, ukazoval by jina cisla nez prehled vytizeni.

Krok vidi jen to, co je pred nim. Parametry spoustece plus vystupy predchozich kroku. Vetev podminky nepridava nic do sekvence za podminkou, protoze nemusela probehnout. Vypocet je v flowScope.ts, priklady v 06-tickety.md.

Sablony odkazuji jmenem, ne ID. Opak podminek, a je to zamer: {{subject}} uzivatel napise a precte, {{f_42}} ne. Rozbite odkazy po prejmenovani se hlasi jako nedodelek.

Zadna ticha selhani. Kazdy catch loguje a uzivatel se o chybe dozvi.