Resitel uz neni vlastni zaznam spojeny s uctem pres e-mail: je to clenstvi uctu ve firme a jeho ID je ID uctu. Popisek, kapacita, externi ID a zapnuti visi na clenstvi, takze clovek ve dvou firmach je v kazde jinak a spravce firmy ho vypne jen u sebe. Odebrani z firmy odebere jen clenstvi. Stara data se pri startu jednou prevedou (migratePeople.ts), vcetne odkazu v ticketech, skupinach, automatizacich, akcich a widgetech. Sprava lidi v zalozce Lide zaklada ucty, pozvanka uz nema volbu resitele. Zalozeni firmy z registru ARES: hledani podle IC nebo nazvu, dotazeni IC, DIC, sidla a pravni formy, vyber soucasnych statutarnich zastupcu a prokury, ucty spravce firmy s nahradnim e-mailem IC-poradi@placeholder.cz. Vychozi rozlozeni dashboardu bez resitele neobsahuje list.myTickets. Prepinani jazyku je docasne schovane (MULTILANG_ENABLED), web je cesky. Dokumentace aktualizovana. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
11 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. Runtime zavislosti jsou v dependencies, nastroje pro build
webu v devDependencies - runtime image je pak instaluje pres --omit=dev.
Build
tsc src/**.ts -> dist/*.js
vite web/ -> dist/public/
Server obsluhuje dist/public jako statiku. Dockerfile kopiruje do vysledneho
image jen dist, takze staci jedna slozka.
Mapa kodu - server
| Cesta | K cemu je |
|---|---|
src/index.ts |
vstupni bod: middleware, mount routeru, statika, SPA, Swagger |
src/config.ts |
cteni environment variables, normalizace ROOT_PATH |
src/openapi.ts |
OpenAPI definice vcetne servers s prefixem proxy |
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/dashboard.ts |
data portalu, tickety, behy, CRUD automatizaci |
src/routes/ticketActions.ts |
akce nad ticketem vcetne vestavenych, pravo za firmu ticketu |
src/routes/settings.ts |
CRUD entit pres crud.ts, uzivatele, ARES |
src/routes/ares.ts |
firma z registru ARES, jen spravce platformy |
src/routes/connectors.ts |
konektory firmy, overeni, nastroje MCP |
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 |
src/ares/client.ts |
klient verejneho API ARES |
src/data/store/ |
tri rezimy uloziste, withCache, withMirror, initStores |
src/data/snapshot.ts |
atomicky zapis JSONu pro rezim file |
src/data/ticketStore.ts |
tickety, jejich resitele, log prubehu, prehled vytizeni |
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 |
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 |
automatizace, strom akci, tokeny webhooku |
src/data/services.ts |
katalog sluzeb, jejich spousteču a akci |
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, sandbox skriptu firmy |
src/scripts/ |
skripty konektoru: registr, runner, HTTP, pomocne funkce |
src/mcp/ |
klient MCP, prihlaseni, dialekty, errors.ts se spolecnou chybou prihlaseni |
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 |
vsechny firemni udaje na jednom miste |
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/useApiQuery.ts |
nacitani dat, cache, spolecny debounce, refreshing misto odmontovani |
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/useSubmit.ts |
odeslani formulare: saving, chyba, reset na jednom miste |
web/src/lib/options.ts |
pevne ciselniky (priority) |
web/src/lib/useUnsavedChanges.ts |
varovani pri odchodu z rozepsaneho formulare |
web/src/lib/flow.ts |
ciste funkce nad stromem automatizace |
web/src/types/ |
fasada nad src/shared (alias @shared/*), zadne vlastni typy API |
web/src/components/ui/ |
zakladni prvky, Chip |
web/src/components/ui/form/ |
Field, Input, Select, Textarea, controlClass: jedna sada trid |
web/src/components/dashboard/ |
shell portalu, dlazdice, graf, stream, TicketCard |
web/src/components/dashboard/flow/ |
strom akci: FlowCanvas a karty ActionCard, ConditionCard, ForeachCard, StepControls |
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 |
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.