Runtime: `src/runtime/executor.ts` jde krok po kroku, u podminky se vetvi, do poli dosadi parametry, akci pusti pres runScript a vystupy pripise do kontextu pro dalsi krok. Cely prubeh jde do logu ticketu vcetne toho, co sluzba vratila. Pouzivaji ho obe cesty: akce na ticketu i webhook. Opraveno: rozlozeni dashboardu s vlastnim widgetem se NEDALO ULOZIT. `validateLayout` znala jen vestaveny katalog, takze kazdy pokus skoncil hlaskou "widget v katalogu neexistuje" - presne to, co hlasil uzivatel. Katalog je ted jedna funkce a pouziva ji nabidka i kontrola. Zaroven je za konkretni firmu, driv slo polozit dlazdici jedne firmy na dashboard druhe. Prokliky: z widgetu lidi na cloveka, ze seskupeni na vyfiltrovany seznam ticketu. Odkazy sklada server, protoze on jediny zna filtr widgetu. Seznam ticketu cte filtr z adresy a umi filtrovat na typ, tag a skupinu. Tabulky: spolecna `TicketTable` pro seznam i detail osoby. Na mobilu se neposouva do strany, uzka obrazovka dostane karty. Detail osoby ma velkou tabulku se zalozkami "ma u sebe" a "vyresil" a prepinacem pohledu. Odebrano: simulace vcetne tlacitka, dialogu i endpointu. Trojice pohledu nad tickety - vyber firmy je select, "moje" je prepinac, driv to delalo totez dvakrat. Pridan zmereny rozbor kapacity pro 200 firem (19-kapacita-200-firem.md): soucasny stav to nezvladne, protoze data jsou v pameti a vypis je linearni. Zmereno na 5 000 ticketech, vcetne toho, co s tim a kolik serveru to chce. Overeno 7 kontrolami proti bezicimu serveru. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
13 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 |
| 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/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 |
| GET | /api/dashboard/tickets/:id/actions |
| POST | /api/dashboard/tickets/:id/actions/:actionId |
| GET | /api/dashboard/incidents |
| 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 |
| POST | /api/admin/impersonate |
| POST | /api/admin/impersonate/stop |
| GET | /api/admin/impersonate/candidates |
| GET | /api/admin/audit |
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, people, groups, ticket-types, actions,
widgets, features.
Format chyb
Jednotny pro cele API:
{ "error": "validation_error", "message": "Zadejte platny e-mail." }
| HTTP | error |
Kdy |
|---|---|---|
| 400 | validation_error |
vstup neprosel schematem |
| 401 | unauthorized |
chybi nebo neplatny token |
| 401 | invalid_credentials |
spatny e-mail nebo heslo |
| 403 | forbidden |
nedostatecna role |
| 404 | not_found |
zaznam nebo endpoint neexistuje |
| 409 | ruzne | operace nedava v danem stavu smysl |
| 500 | internal_error |
neodchycena chyba, detail jen mimo produkci |
message je vzdy cesky a je urcena k zobrazeni uzivateli.
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.
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}'
Prototyp pozadavek prijme, zvaliduje a zapocita do metrik, ale strom akci nevykona - runtime neexistuje.
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".
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. Neznamy resitel vraci 404, ne tiche odpojeni.
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.
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.
Prava a navigace
GET /api/dashboard/access vraci permissions (efektivni prava po slouceni
roli), nav (zalozky, ktere ma volajici videt) a platformAdmin. Klient podle
toho kresli, ale nic si nedovozuje - kdo co smi, rozhoduje server u kazdeho
requestu znovu.
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.
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.ts a tenhle soubor. Swagger musi odpovidat
skutecnemu chovani aplikace, jinak je horsi nez zadny.