Poptavka z webu je ticket, prilohy, udaje provozovatele, kolacovy graf
Provozovatel portalu: firma s priznakem portalOperator (jen jedna, zapnuti odebere ostatnim) a novymi poli contactEmail, contactPhone, website vedle ico, dic, adresy a pravni formy. Verejny GET /api/public/brand vraci jeji udaje a web je bere pres useBrand() na kontaktu, v paticce, O nas, prihlaseni i v titulku; brand.ts je jen zaloha. Poptavka z webu zaklada u provozovatele ticket kanalu form: predmet "Poptavka: tema", telo JSON s poli formulare, tag Poptavka plus tema, zakaznik z formulare, poznamka v logu. Bez provozovatele se jen zaloguje. Prilohy ticketu: formular az 3 soubory po 5 MB, ticket az 10; nahrani, seznam, stazeni a smazani (pravo ticket.comment, strop viditelnosti, poznamky v logu, audit). Soubor jde v JSON jako Base64 a lezi v beznem ulozisti, bez nove zavislosti; strop tela jen na techto cestach. Vlastni widget s kreslenim Graf umi i pocet ticketu se seskupenim jako kolac (PieChart.tsx, ciste SVG, osm barev z tokenu, zbytek jako ostatni). OpenAPI rozdelene na mensi soubory (102 cest, 28 schemat overeno shodnych), 28 novych testu (135 celkem), dokumentace aktualizovana.
This commit is contained in:
+76
-1
@@ -14,7 +14,8 @@ Verejne:
|
||||
| GET | `/docs` | Swagger UI |
|
||||
| GET | `/openapi.json` | OpenAPI definice |
|
||||
| POST | `/api/auth/login` | prihlaseni, vraci JWT |
|
||||
| POST | `/api/contact` | poptavka z webu |
|
||||
| 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 |
|
||||
@@ -51,6 +52,10 @@ Vyzaduji `Authorization: Bearer <token>`:
|
||||
| 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` |
|
||||
@@ -142,6 +147,75 @@ pozvanky 5 za 15 minut. Pocita se podle adresy klienta, proto ma Express
|
||||
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](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](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](22-znacka-a-design.md).
|
||||
|
||||
## Prilohy ticketu
|
||||
|
||||
Model je v [06-tickety.md](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
|
||||
@@ -401,6 +475,7 @@ Pravo se vzdy pta **za firmu zaznamu**, ne za prepnutou firmu. Cizi firma je
|
||||
| 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` |
|
||||
| `/storage`, `/scripts` s cestami na serveru | cesty jen spravci platformy, ostatni dostanou odpoved bez nich |
|
||||
|
||||
Reference in New Issue
Block a user