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>
29 KiB
04 - API
Interaktivni dokumentace je na /apps/<app-id>/docs. Tenhle soubor popisuje to,
co ze Swaggeru neni videt.
Endpointy
Verejne:
| Metoda | Cesta | Popis |
|---|---|---|
| GET | /health |
liveness, nezavisi na databazi |
| GET | /health/ready |
readiness, 503 pri nedostupne databazi |
| GET | /docs |
Swagger UI |
| GET | /openapi.json |
OpenAPI definice |
| POST | /api/auth/login |
prihlaseni, vraci JWT |
| POST | /api/contact |
poptavka z webu, vznikne ticket provozovatele |
| GET | /api/public/brand |
udaje provozovatele portalu pro web |
| POST | /webhook/:token |
prijem dat do automatizace |
| POST | /webhook/ticket/:token |
prijem udalosti do ticketu |
| GET | /webhook/ticket/:token |
napoveda k prijmu |
Vyzaduji Authorization: Bearer <token>:
| Metoda | Cesta |
|---|---|
| GET | /api/auth/me |
| POST | /api/auth/logout |
| GET | /api/dashboard/access |
| GET | /api/dashboard/widgets |
| GET | /api/dashboard/layout |
| PUT | /api/dashboard/layout |
| DELETE | /api/dashboard/layout |
| GET | /api/dashboard/summary |
| GET | /api/dashboard/people |
| GET | /api/dashboard/people/:id |
| GET | /api/dashboard/intake |
| POST | /api/dashboard/intake/regenerate |
| GET | /api/dashboard/settings/actions/:id/scope |
| GET | /api/dashboard/notifications |
| POST | /api/dashboard/notifications/read |
| GET | /api/dashboard/runs |
| GET | /api/dashboard/tickets |
| GET | /api/dashboard/tickets/workload |
| GET | /api/dashboard/tickets/:id |
| POST | /api/dashboard/tickets/:id/assign |
| POST | /api/dashboard/tickets/:id/status |
| POST | /api/dashboard/tickets/:id/comment |
| POST | /api/dashboard/tickets/:id/type |
| POST | /api/dashboard/tickets/:id/tags |
| POST | /api/dashboard/tickets/:id/group |
| POST | /api/dashboard/tickets/:id/claim |
| GET | /api/dashboard/tickets/:id/actions |
| POST | /api/dashboard/tickets/:id/actions/:actionId |
| GET | /api/dashboard/tickets/:id/attachments |
| POST | /api/dashboard/tickets/:id/attachments |
| GET | /api/dashboard/tickets/:id/attachments/:attachmentId/content |
| DELETE | /api/dashboard/tickets/:id/attachments/:attachmentId |
| GET | /api/dashboard/invites |
| POST | /api/dashboard/invites |
| DELETE | /api/dashboard/invites/:id |
| GET | /api/invites/:kod |
| POST | /api/invites/:kod/accept |
| GET | /api/dashboard/incidents |
| GET | /api/dashboard/incidents/:id |
| PATCH | /api/dashboard/incidents/:id/status |
| GET | /api/dashboard/storage |
| GET | /api/dashboard/services |
| GET | /api/dashboard/connectors/services |
| GET | /api/dashboard/connectors |
| POST | /api/dashboard/connectors |
| GET | /api/dashboard/connectors/:id |
| PATCH | /api/dashboard/connectors/:id |
| DELETE | /api/dashboard/connectors/:id |
| POST | /api/dashboard/connectors/:id/test |
| GET | /api/dashboard/scripts |
| GET | /api/dashboard/scripts/:id |
| PUT | /api/dashboard/scripts/:id |
| POST | /api/dashboard/scripts/:id/test |
| POST | /api/dashboard/scripts/reload |
| GET | /api/dashboard/stream |
| GET | /api/dashboard/automations |
| POST | /api/dashboard/automations |
| GET | /api/dashboard/automations/:id |
| PUT | /api/dashboard/automations/:id |
| DELETE | /api/dashboard/automations/:id |
| POST | /api/dashboard/automations/:id/webhook/regenerate |
| POST | /api/dashboard/widget-data |
| GET | /api/dashboard/widget-data/options |
| GET | /api/dashboard/settings/catalog |
| GET | /api/dashboard/settings/features |
| GET | /api/dashboard/settings/people-overview |
| GET | /api/dashboard/settings/users-overview |
| PATCH | /api/dashboard/settings/users/:id/password |
| GET | /api/dashboard/settings/ares/companies |
| GET | /api/dashboard/settings/ares/companies/:ico/persons |
| POST | /api/dashboard/settings/ares/tenants |
| POST | /api/admin/impersonate |
| POST | /api/admin/impersonate/stop |
| GET | /api/admin/impersonate/candidates |
| GET | /api/admin/audit |
| GET | /api/admin/export |
Sprava zaznamu ma u kazde entity stejnou petici (seznam, detail, vytvoreni,
uprava, mazani) na /api/dashboard/settings/<entita>, protoze ji dela jedna
fabrika (src/routes/crud.ts):
tenants, users, roles, groups, ticket-types, actions,
widgets, features.
people ma stejne cesty a stejne pravo (people.manage), ale vlastni
handlery v src/routes/settings/people.ts: zaznam, ktery se meni, je ucet bez firmy
a odpoved je pohled za jednu firmu, coz fabrika neumi. Popis je nize
v sekci Lide.
Format chyb
Jednotny pro cele API:
{
"error": "validation_error",
"message": "Zadejte platny e-mail.",
"issues": [{ "field": "email", "message": "Zadejte platny e-mail." }]
}
| HTTP | error |
Kdy |
|---|---|---|
| 400 | validation_error |
vstup neprosel schematem, issues po polich |
| 401 | unauthorized |
chybi nebo neplatny token |
| 401 | invalid_credentials |
spatny e-mail nebo heslo |
| 403 | forbidden |
nedostatecne pravo v dane firme |
| 404 | not_found |
zaznam nebo endpoint neexistuje, nebo je cizi firmy |
| 409 | ruzne | operace nedava v danem stavu smysl |
| 429 | too_many_requests |
prekrocen limit requestu, hlavicka Retry-After |
| 500 | internal_error |
neodchycena chyba, detail jen mimo produkci |
message je vzdy cesky a je urcena k zobrazeni uzivateli. Chybu validace
sklada validationError v src/middleware/validation.ts, aby issues mely
vsude stejny tvar a formular umel chybu ukazat u pole.
Limity (src/middleware/rateLimit.ts) jsou jen na verejnych endpointech, kde
se da hadat: prihlaseni 20 pokusu za 15 minut, kontakt 5 za hodinu, prijeti
pozvanky 5 za 15 minut. Pocita se podle adresy klienta, proto ma Express
trust proxy = 1 - bez toho by vsichni za Caddy sdileli jeden limit.
Kazdy asynchronni handler je obaleny (safeRouter v src/middleware/asyncHandler.ts).
Odmitnuta promise je 500 s logem, ne pad procesu.
Strop tela requestu
Globalni express.json v src/app.ts ma 256 kB. To staci na formulare
a stromy automatizaci, ne na soubory. Dve cesty prijimaji soubory v base64
a maji vlastni express.json s vetsim stropem; globalni parser je
preskakuje (hasOwnBodyLimit v src/routes/bodyLimit.ts), jinak by telo
odmitl driv, nez se k nemu router dostane.
| Cesta | Strop |
|---|---|
POST /api/contact |
jsonLimitFor(3, 5 MB), tj. 3 soubory |
POST /api/dashboard/tickets/:id/attachments |
jsonLimitFor(10, 5 MB), tj. 10 souboru |
jsonLimitFor(count, maxBytes) pocita count * maxBytes * 4/3 (base64) plus
64 kB rezervy na zbytek JSONu. Vypocet je na jednom miste, aby formular
a prilohy pocitaly stejne. U kontaktu bezi limit pokusu pred parserem
tela: kdo uz pokusy vycerpal, nema server nutit cist megabajty.
Poptavka z webu
POST /api/contact je verejny, 5 poptavek za hodinu z jedne adresy. Telo:
name, email, topic (automatizace, voicebot, integrace,
dashboard, podpora, jine), message, nepovinne company, phone
a attachments (nejvys 3, kazda { name, mime?, content } s obsahem
v base64).
Poptavka vznikne jako ticket firmy, ktera je provozovatelem portalu
(Tenant.portalOperator, viz 07-firmy-a-prava.md):
kanal form, predmet Poptávka: <tema>, telo je JSON formulare, tagy
Poptávka a tema, externalSource web-form. Kdyz zadna firma
provozovatelem neni, poptavka se jen zaloguje (warn). Odpoved je v obou
pripadech 202 - zvenku nema byt poznat, jak je portal nastaveny. Podrobnosti
v 06-tickety.md.
Udaje provozovatele
GET /api/public/brand je verejny, bez limitu pokusu (cte z kopie firem
v pameti, je levnejsi nez health) a s Cache-Control: public, max-age=60.
Vraci name, legalName, ico, dic, address, legalForm, email,
phone, website provozovatele portalu; bez provozovatele same null
a porad 200, aby web umel rict "neni nastaveno" misto padu. Web ho cte
pres useBrand, viz 22-znacka-a-design.md.
Prilohy ticketu
Model je v 06-tickety.md. Prilohy nejsou pole ticketu, maji
vlastni cesty pod /api/dashboard/tickets/:id/attachments:
| Volani | Co se stane |
|---|---|
GET attachments |
seznam bez obsahu (id, name, mime, size, uploadedBy, createdAt) |
POST attachments |
telo { files: [{ name, mime?, content }] }, obsah base64; vraci 201 a items |
GET attachments/:attachmentId/content |
binarni obsah s Content-Type a Content-Disposition (nazev v RFC 5987) |
DELETE attachments/:attachmentId |
204 |
Limity: 5 MB na soubor po dekodovani, 10 priloh na ticket, nazev bez cesty a ridicich znaku, nejvys 200 znaku. Kontrola bezi nad celou davkou pred prvnim zapisem: kdyz neprojde treti soubor, neulozi se ani prvni dva.
Ticket se hleda stejne jako u detailu (visibleTicketOrDeny): cizi nebo nad
strop viditelnosti je 404. Zapis a mazani chce ticket.comment za firmu
ticketu - priloha je jen dalsi zprava k ticketu. Kazda zmena zapise radek do
logu ticketu, posle ticket.updated a jde do auditu jako
ticket.attachment.add / ticket.attachment.remove.
Stazeni chce hlavicku Authorization, obycejny odkaz <a href> ji neposle.
Portal proto stahuje pres fetch (apiBlob v web/src/lib/api.ts) a docasny
odkaz na blob.
Autentizace
Hesla se hashuji bcryptem, plaintext se nikde neuklada. Login vraci JWT
podepsany JWT_SECRET s platnosti JWT_EXPIRES_IN.
Spatne heslo i neexistujici e-mail vraci stejnou odpoved, aby se neprozradilo, ktere ucty existuji. Pokus se loguje bez hesla.
Token si drzi klient v localStorage. Pro produkci je cilovy stav httpOnly
cookie se Secure a SameSite plus CSRF token.
Zivy stream
GET /api/dashboard/stream je Server-Sent Events. Po pripojeni posle potvrzeni
a poslednich par udalosti, pak uz jen nove. Kazdych 25 sekund jde komentarovy
radek, aby spojeni neuspalo proxy.
Typy udalosti: ticket.created, ticket.updated, ticket.assigned,
ticket.resolved, incident.started, incident.updated, incident.resolved,
automation.created, automation.updated, automation.deleted,
automation.run, webhook.received.
K tomu udalosti entit tenant, user, role, person, group,
ticketType, action, widget, connector, feature s priponou
.created, .updated, .deleted. Payload je { id, <druh>: zaznam },
u smazani jen { id }. Publikuje je crudRouter (volba event), routy
konektoru a PUT features. Klient z nich opravuje sklad ciselniku bez dotazu.
Kazda udalost nese tenantId (null = cela platforma). Stream posila jen
udalosti firem, do kterych uzivatel patri, a to i v historii po pripojeni.
Udalost s payload.userId jde jen tomu cloveku. Spravce platformy vidi vse.
Driv videl kazdy prihlaseny udalosti vsech firem - nazev ticketu cizi firmy
v bubline je unik dat, i kdyz se na ticket nedostane.
ticket.updated, ticket.assigned a ticket.resolved nesou
v payload.ticket cely ticket, aby klient opravil seznam na miste.
Klient se pripojuje pres fetch s hlavickou Authorization, ne pres EventSource.
Duvod je v 03-architektura-a-mapa-kodu.md.
Webhook
Verejny endpoint bez prihlaseni. Autorizuje neuhodnutelny token v adrese,
32 znaku z randomBytes(24) v base64url.
Token generuje vyhradne server. Hodnota webhookToken poslana klientem se
ignoruje, jinak by si sel nastavit predvidatelnou adresu.
| Situace | Odpoved |
|---|---|
| vse v poradku | 202 |
| neznamy token | 404 |
| automatizace je pozastavena | 409 |
| chybi povinny parametr, spatny typ | 400 |
Parametry navic se neodmitaji, jen loguji. Odesilatele bezne posilaji i vlastni data a odmitat je by rozbijelo integrace.
curl -X POST https://services.csbot.cz/apps/<app-id>/webhook/<token> \
-H "Content-Type: application/json" \
-d '{"customer":"Nordis","score":18}'
Token se porovnava v konstantnim case (timingSafeEqualString
v src/lib/secure.ts), stejne jako token prijmu a kod pozvanky. V logu
requestu je z tokenu videt jen prvnich sest znaku.
Firmy a pohledy
Prava popisuje 07-firmy-a-prava.md, tady jen API.
Endpointy dashboardu berou scope (all, tenant, mine) a tenantId.
GET /api/dashboard/access rekne, co uzivatel smi, aby to klient nedovozoval.
Pozadavek na pohled nebo firmu bez opravneni vraci 403 nebo 404, nikdy tise zuzeny vysledek. Uzivatel nesmi koukat na cizi cisla v domneni, ze jsou spravna.
Tickety
Popis modelu je v 06-tickety.md, tady jen to, co se tyka API.
GET /api/dashboard/tickets bere navic filtry assignee, status, channel.
U assignee je zvlastni hodnota unassigned pro frontu bez resitele.
U pohledu mine se assignee ignoruje, pohled je silnejsi. Neznama hodnota
filtru se zaloguje a ignoruje - je lepsi ukazat vic ticketu nez prazdny seznam
bez vysvetleni.
Odpoved nese vedle items jeste meId. Klient podle nej pozna, ktere tickety
jsou jeho, a jestli ma vubec smysl nabizet filtr "moje".
Strankovani. /tickets a /runs berou limit a offset, limit nejvys
500. Celkovy pocet je v hlavicce X-Total-Count, u ticketu i v tele jako
total. Bez limit se vraci vse jako driv (u behu poslednich 50), aby se
nerozbily stavajici odkazy. Klient cte hlavicku pres apiFetchWithMeta.
Filtrovani dela server, ne klient. Seznam a prehled vytizeni tak nikdy neukazuji jina cisla. Vyhledavaci pole v portalu je jina vec - to jen dohledava v uz nactenem seznamu.
GET /api/dashboard/tickets/:id vraci navic trace, tedy log prubehu vcetne
toho, co ktera volana sluzba vratila.
POST /api/dashboard/tickets/:id/assign s telem {"assigneeId": null} vrati
ticket do fronty. assigneeId je ID uctu; kdo ve firme ticketu neni clenem,
vraci 404, ne tiche odpojeni.
POST /api/dashboard/tickets/:id/claim je prevzeti prace, ne prehozeni:
volajici si bere ticket sam a telo je prazdne. Smi to u ticketu bez resitele
a u ticketu ve skupine, ve ktere je. Kdyz uz ticket nekdo resi, vraci 409, resp.
403 u cizi skupiny - vzit nekomu rozdelanou praci je jine rozhodnuti a chce to
pravo ticket.assign.others. Kdo ve firme nema clenstvi (neni resitel),
dostane 400.
Lide (resitele)
Resitel je clenstvi uctu ve firme, ne vlastni zaznam; ID resitele je ID uctu.
Duvody v 06-tickety.md. Person je pohled: id a name,
email, enabled z uctu, tenantId, role (popisek), capacity,
externalIds a roleIds z clenstvi. Tentyz clovek ve dvou firmach prijde
dvakrat se stejnym id.
GET /api/dashboard/people vraci cleny zvolene firmy s povolenym uctem
a jejich skupiny (items, groups, meId). Sprava je na
/api/dashboard/settings/people pod pravem people.manage:
| Volani | Telo | Co se stane |
|---|---|---|
GET people |
vcetne vypnutych uctu | |
POST people |
{ name, email, password?, roleIds?, role?, capacity?, externalIds?, enabled? } |
zalozi ucet (heslo nahodne, kdyz chybi; role role_agent, kdyz chybi) s clenstvim ve firme, nebo prida clenstvi uctu, ktery s tim e-mailem uz je |
PATCH people/:id |
tataz pole, vsechna nepovinna | name, email (unikatni) a enabled meni ucet, ostatni clenstvi v teto firme |
DELETE people/:id |
odebere jen clenstvi; ucet bez clenstvi, ktery neni spravce platformy, se vypne |
Spravce firmy na spravce platformy nesaha, stejne jako u /users. Udalosti
jsou person.created, person.updated, person.deleted s payloadem
{ id, person }, u smazani { id }. enabled je za clenstvi: vypne cloveka
jen v teto firme, ucet a ostatni clenstvi zustavaji.
/users (spravce platformy) bere u clenstvi vedle roleIds i
seesAllTenant, role, capacity a externalIds a pri uprave je zachova,
takze zmena role v Nastaveni nesmaze kapacitu nastavenou v Lidech.
Pozvanky do firmy
/api/invites/:kod je verejne, protoze kdo prijde za odkazem, jeste ucet
mit nemusi. Autorizuje kod v adrese, proto je nahodny a dlouhy - stejne jako
u webhooku. Odpoved je zamerne uzka: nazev firmy, jestli pozvanka plati, a kdyz
uz adresu zname, tak ji, at ji clovek nemusi psat. Nic o tom, kdo ve firme je.
POST /api/invites/:kod/accept s telem {"name", "email", "password"}:
| Situace | Co se stane |
|---|---|
| ucet neexistuje | zalozi se a pripoji k firme |
| ucet existuje, heslo sedi | jen se pripoji k firme |
| ucet existuje, heslo nesedi | 401 |
| pozvanka je na jinou adresu | 403 |
| pozvanka uz byla pouzita nebo vyprsela | 409 s konkretnim duvodem |
Overeni hesla u existujiciho uctu neni formalita: bez nej by kdokoliv s odkazem pripojil cizi adresu ke sve firme a videl by jeji data.
Sprava pozvanek (/api/dashboard/invites) chce pravo user.manage. Seznam
vraci u kazde pozvanky celou adresu vcetne prefixu proxy, aby slo rovnou
kopirovat - relativni cesta se do zpravy vlepit neda. Pozvanka nese jen
email a roleIds; stary priznak asPerson se prijme a ignoruje, protoze
resitelem je kazdy clen firmy.
Akce na ticketu
Popis modelu je v 09-navrh-rozsireni.md.
GET /api/dashboard/tickets/:id/actions vraci jen akce, ktere v teto situaci
opravdu jdou spustit: sedi typ nebo tag, projdou podminky a volajici na ne ma
pravo. Klient si nefiltruje nic - jinak by se to pocitalo na dvou mistech
a jednou se to rozejde.
POST /api/dashboard/tickets/:id/actions/:actionId vraci 200 i kdyz akce
selhala. Selhani akce neni chyba API. V odpovedi je ok, summary, detail
s celym chybovym hlasenim a durationMs. Cely prubeh se zapise do logu ticketu.
Vestavene akce (type, tags, group, assign, status, comment) jsou
zvlast: meni ticket sam, ne cizi sluzbu, a kazda ma vlastni pravo. Vsechny
vcetne claim jdou pres builtinAction v src/routes/ticketActions.ts, kde
se pravo pta za firmu ticketu a ticket se nejdriv najde pres strop
viditelnosti (visibleTicketOrDeny). Driv mely assign, status a comment
vlastni handlery a kazdy se ptal jinak.
Firma z registru ARES
Jen spravce platformy (/api/dashboard/settings/ares). Popis rozhodnuti je
v 07-firmy-a-prava.md.
| Endpoint | Co vraci |
|---|---|
GET ares/companies?query= |
same cislice (1 az 8) hledaji IC presne, jinak nazev, nejvys 10. U firmy, ktera uz v portalu je, existingTenantId |
GET ares/companies/{ico}/persons |
soucasni statutari a prokura z verejneho rejstriku, u kazdeho navrzeny e-mail IC-poradi@placeholder.cz |
POST ares/tenants |
zalozi firmu a ucty vybranych osob (role_admin, popisek clenstvi z funkci v rejstriku, kapacita 8), vraci firmu a seznam uctu. Ucet je zaroven resitel |
Chyba registru je ares_error s kodem podle toho, co ARES vratil - neni to
chyba naseho API a nema se opakovat automaticky. Adresa registru je
ARES_BASE_URL.
Prijem udalosti do ticketu
Popis modelu je v 18-ticketovaci-system.md.
POST /webhook/ticket/:token je verejny, autorizuje token firmy v adrese.
Vraci 201 kdyz ticket vznikl, 200 kdyz se udalost navesila na existujici:
{ "ok": true, "created": false, "ticketId": "TK-4822", "externalId": "3", "eventId": "tev_2" }
externalId je unikatni v ramci firmy. Token urcuje firmu, takze dve firmy
mohou obe poslat objednavku cislo 3 a nedojde ke smichani. Cislo i retezec jsou
tentyz klic.
Neznamy typeId se zahodi a zaloguje, ticket vznikne bez typu. Odmitnout celou
udalost kvuli jednomu poli by znamenalo ztratu dat.
Fronta behu
Popis je v 20-fronta-a-runtime.md.
POST /webhook/:token vraci 202, ne 200: data jsme prevzali a strom se
vykona na pozadi. Vysledek se hleda v GET /api/dashboard/runs nebo v logu
ticketu. Cekat na cizi sluzbu v requestu nejde - za jeji rychlost nerucime
a odesilateli by vyprsel timeout.
GET /webhook/:token vraci kontrakt: co se v tele ceka, na jakych cestach
a ukazku. Bez toho by musel ten, kdo webhook zapojuje, hadat.
GET /api/dashboard/runs ma u kazdeho behu cele chybove hlaseni, pocet pokusu
a kdy se to zkusi znovu.
Prava a navigace
GET /api/dashboard/access vraci permissions (efektivni prava po slouceni
roli), nav (zalozky, ktere ma volajici videt), platformAdmin a roleNames
(nazvy roli v prepnute firme, pro popisek u uctu). Klient podle toho kresli,
ale nic si nedovozuje - kdo co smi, rozhoduje server u kazdeho requestu
znovu.
Kdo co smi, po routach
Pravo se vzdy pta za firmu zaznamu, ne za prepnutou firmu. Cizi firma je 404, chybejici pravo 403.
| Co | Pravo |
|---|---|
| firmy CRUD, ARES | spravce platformy |
| uzivatele CRUD | spravce platformy, nebo user.manage jen v ramci sve firmy |
lide (/settings/people) |
people.manage jen v ramci sve firmy |
| pozvanky | user.manage, role jen z te firmy |
| konektory create, update, delete, test | connector.manage |
| automatizace create, update, delete, regenerate | automation.edit |
/services, /connectors/services |
clenstvi ve firme |
| assign, status, comment, claim na ticketu | prava vestavene akce za firmu ticketu plus strop viditelnosti |
| prilohy ticketu (POST, DELETE) | ticket.comment za firmu ticketu plus strop viditelnosti |
/api/admin/impersonate* |
impersonate |
/api/admin/audit |
audit.view |
/api/admin/export |
audit.view (vraci i hashe hesel, stejna citlivost jako audit) |
/storage, /scripts s cestami na serveru |
cesty jen spravci platformy, ostatni dostanou odpoved bez nich |
Spravce firmy s user.manage nenastavi platformAdmin, neprida clenstvi
v jine firme, nesahne na spravce platformy a nesmaze cloveka, ktery je i
v jine firme - ten ucet neni jen jeho. Podrobnosti
v 07-firmy-a-prava.md.
GET /api/dashboard/settings/catalog vraci katalog prav a modulu, aby formular
role nemel seznam prav napsany v kodu klienta.
Prepnuti na jiny ucet
POST /api/admin/impersonate vraci novy token s narokem act (kdo se za koho
vydava) a writes. Bez writes middleware odmitne cokoliv jineho nez GET
s 403. Kazde prepnuti i ukonceni je v auditu vcetne toho, kdo to byl doopravdy.
Svuj puvodni token si klient odklada do sessionStorage, server o nem nic nevi.
Export nastaveni
GET /api/admin/export vraci { exportedAt, kinds: { <druh>: [zaznam] } }
se vsemi zaznamy konfiguracnich druhu tak, jak lezi v ulozisti: firmy, ucty
(vcetne passwordHash), role, skupiny, moduly firem, typy ticketu,
akce, widgety, rozlozeni, automatizace, skripty firmy. Konektory, tickety,
incidenty, audit, upozorneni, prilohy ani pozvanky v nem nejsou. Kazdy
export je v auditu jako admin.export s pocty po druzich.
Je to zdroj pro seed/records.json, ktery se pri startu nasype do prazdneho
uloziste; stahuje ho npm run seed:export (scripts/export-seed.mjs).
Proc a co to obnasi je v 14-databaze.md, sekce Nastaveni
v repozitari.
Sluzby a konektory
Popis modelu je v 12-sluzby-a-konektory.md, tady jen API.
Sluzba je to, co umime. Konektor je napojeni jedne firmy vcetne jejich pristupovych udaju.
Hodnoty pristupovych udaju se nikdy nevraci, jen filled a missing.
V PATCH staci poslat jen to, co se meni: prazdny retezec hodnotu smaze,
chybejici klic ji nechava.
Sluzba, kterou uzivatel nevidi, se nevraci vubec, ne se stavem 403.
POST /connectors/:id/test vraci 200 i pri neuspechu. checked rika, co se
vlastne overilo - u sluzby bez verifyPath jen dostupnost, ne udaje.
Skripty konektoru
Popis modelu je v 11-skripty-konektoru.md, tady jen API.
Cteni smi kazdy prihlaseny, protoze builder potrebuje vedet, co skript umi. Uprava, zkusebni spusteni a vynucene nacteni smi jen spravce platformy - uprava skriptu meni chovani vseho, co ho pouziva.
GET /api/dashboard/scripts vraci vedle manifestu i problems s rozbitymi
skripty a connections se stavem napojeni. Hodnoty pristupovych udaju se
nevraci nikdy, jen jmena chybejicich environment variables.
PUT /api/dashboard/scripts/:id kod nejdriv nacte a overi a az pak prepise
soubor. Rozbita uprava vraci 400 s issues a puvodni skript dal funguje.
POST /api/dashboard/scripts/:id/test vola opravdovou sluzbu. Chyba skriptu
neni chyba API, vraci se 200 a popis v error vcetne toho, jestli ma smysl
zkusit to znovu.
Pri pridani endpointu
Soucasne aktualizovat src/openapi/paths/*.ts a tenhle soubor. Swagger musi odpovidat
skutecnemu chovani aplikace, jinak je horsi nez zadny.