Portal nemel zadnou tenanci. Kterykoliv prihlaseny uzivatel videl vsechny tickety vsech firem i cely seznam resitelu, requireRole se nikde nevolal. Tenant je hranice viditelnosti, tenantId na ticketu, resiteli i automatizaci. Uzivatel muze patrit do vic firem, v kazde s jinou roli. Pristup napric firmami je zvlast jako platformAdmin. Tri pohledy na tickety: all, tenant, mine. Admin mezi nimi prepina vcetne vyberu firmy. O pravech rozhoduje jedine data/access.ts, klient si nic nedovozuje a bere je z GET /api/dashboard/access. Filtr na firmu je v ulozistich povinny argument, takze zapomenuty filtr neznamena vse, ale nezkompiluje se. Cizi firma vraci 403 nebo 404, nikdy tise zuzeny vysledek. Prirazeni jen v ramci firmy. Prehazovat praci mezi lidmi smi jen admin, agent si smi vzit ticket na sebe. Zmena prihlasovani: ucet klient@firma.cz zanikl, demo ucty jsou nove. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6.7 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/middleware/auth.ts |
requireAuth, requireRole |
src/events/bus.ts |
sbernice udalosti, ze ktere cerpa SSE stream |
src/routes/auth.ts |
prihlaseni, odhlaseni, kdo jsem |
src/routes/dashboard.ts |
data portalu, katalog konektoru, CRUD automatizaci |
src/routes/stream.ts |
SSE stream zmen |
src/routes/simulate.ts |
vyvolani provoznich udalosti |
src/routes/webhook.ts |
verejny prijem dat do automatizace |
src/routes/contact.ts |
poptavkovy formular z webu |
src/data/ticketStore.ts |
tickety, jejich resitele, log prubehu, prehled vytizeni |
src/data/people.ts |
resitele ticketu - oddeleni od uzivatelu portalu |
src/data/tenants.ts |
firmy, ktere portal pouzivaji |
src/data/access.ts |
kdo co vidi - jedno misto pro cely portal |
src/data/incidentStore.ts |
incidenty vcetne zmen a udalosti |
src/data/automationStore.ts |
automatizace, strom akci, tokeny webhooku |
src/data/connectors.ts |
katalog konektoru, 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 |
demo uzivatele |
src/data/mock.ts |
souhrn pro prehled a casova rada grafu |
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 |
web/src/lib/eventStream.ts |
cteni SSE streamu pres fetch |
web/src/lib/useApiQuery.ts |
nacitani dat vcetne obnoveni pri udalosti |
web/src/lib/flow.ts |
ciste funkce nad stromem automatizace |
web/src/components/dashboard/ |
shell portalu, dlazdice, graf, stream, simulace |
web/src/components/dashboard/flow/ |
strom akci, vyber kroku, nastaveni poli akce |
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 |
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.
Data v pameti. Vedome zjednoduseni prototypu. Uloziste jsou oddelena od rout,
takze napojeni na databazi znamena prepsat soubory v src/data/, ne endpointy.
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 neni uzivatel. Uzivatel se prihlasuje do portalu, resitel ma u sebe tickety. Technik muze mit tickety a ucet nikdy nemit. Spojka je e-mail, 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.