# 21 - Realne sluzby a co k nim potreba Naprogramovano. Tenhle dokument rika, **ktera sluzba v katalogu ma za sebou opravdu bezici aplikaci**, jake udaje po firme chce a co s ni umime udelat. Obecny popis vrstev je v [12-sluzby-a-konektory.md](12-sluzby-a-konektory.md), popis skriptu v [11-skripty-konektoru.md](11-skripty-konektoru.md). ## Zdroj pravdy Seznam bezicich aplikaci je na `https://services.csbot.cz/apps`. Kazda ma `/docs` se Swaggerem a `/openapi.json` (u .NET aplikaci `/docs/v1/swagger.json`) se strojove citelnym popisem. Katalog v `src/data/services.ts` z toho vychazi. **Neni to totez**: jedna aplikace muze nest vic sluzeb katalogu a nektere sluzby katalogu zatim zadnou aplikaci nemaji. ## Ktera sluzba stoji na cem | Sluzba katalogu | Aplikace (`appId`) | Overeni (`verifyPath`) | | ------------------ | ----------------------------- | ---------------------------------------------- | | iDoklad | `idoklad` | `/account/agenda` | | RAYNET CRM | `raynet` | `/company?limit=1` | | CSOB (PSD2) | `csob` | `/accounts?size=1` | | SAP Business One | `sap-bo` | `/api/system/info` | | PPL CPL | `pplcplapi` | `/customer` | | Microsoft 365 | `microsoft-365-service` | `/status` | | Google Workspace | `google-service` | `/google/drive/files` | | Google Analytics 4 | `analytics` | `/ga/admin/accountSummaries` | | Search Console | `analytics` | `/gsc/sites` | | Google Ads | `analytics` | `/googleads/customers:listAccessibleCustomers` | | Sklik | `analytics` | `/sklik/limits` | | Meta Ads | `meta` | `/ads/me/adaccounts` | | Prepis hovoru | `audio-transcription` | nema, overi se jen dostupnost | | E-mail | SMTP server firmy | prihlaseni na server, nic se neodesila | | OpenAI | mimo nas, `api.openai.com/v1` | `/models` | **Ctyri sluzby na jedne aplikaci.** GA4, Search Console, Google Ads a Sklik bezi v `analytics`, ale kazda ma **jine pristupove udaje** a jine ceny za pristup. Slucovat je do jedne sluzby by znamenalo, ze firma, ktera ma jen Sklik, musi zaroven vyplnit Google. Proto jsou to ctyri sluzby s jednim `appId`. **Prepis hovoru nema overeni udaju.** Jeho jedine volani je prepis, ktery se uctuje. Overeni proto rekne jen "sluzba odpovida" a nahlas dodá, ze udaje overene nejsou - test, ktery projde i se spatnym klicem, by lhal. ## Sluzby, ktere aplikaci zatim nemaji E-shop, WhatsApp, Facebook Messenger, Instagram, SMS, Slack, Voicebot a AI zpracovani textu jsou v katalogu jako popis toho, co chceme umet. Konektor u nich zalozit jde, ale overeni skonci chybou, protoze na te adrese nic nebezi. Meta Ads je **neco jineho nez Facebook Messenger a Instagram**: aplikace `meta` je nad Marketing API, tedy reklamy, a je jen pro cteni. Zpravy ze stranky a prime zpravy ta aplikace neumi, proto se na ni ty dve sluzby nenapojily. ## Co ktera sluzba chce po firme Udaje patri konektoru, ne prostredi. Tajne se z API nikdy nevraci. ### RAYNET CRM | Pole | Hlavicka | Kde to vzit | | ---------------- | ----------------- | ----------------------------- | | API klic | `X-Api-Key` | RAYNET: Nastaveni, Klic k API | | E-mail uzivatele | `X-Raynet-Email` | prihlasovaci e-mail | | Nazev instance | `X-Instance-Name` | subdomena uctu | ### CSOB (PSD2) Nejvic udaju z celeho katalogu, a je to tak spravne: bankovni rozhrani chce certifikat i token. `X-Access-Token` navic **casem vyprsi** a musi se prepsat, jinak konektor prestane fungovat, aniz by se cokoliv jineho zmenilo. Certifikat QWAC se vklada jako PFX zakodovany do Base64. ### SAP Business One `X-SAP-B1-BaseUrl` je adresa Service Layer u zakaznika a musi byt dostupna z internetu. `X-SAP-B1-Reject-Unauthorized` se nastavi na `false` jen tam, kde ma Service Layer self-signed certifikat. ### PPL CPL Client ID a Client Secret z vyvojarskeho portalu. `X-Environment` prepne na testovaci prostredi, ktere **nevytvari skutecne zasilky** - hodi se pri zkousení stromu. ### Microsoft 365 Tenant ID, Client ID a Client Secret registrovane aplikace v Entra ID. Prihlasuje se aplikace, ne clovek, takze kazdy krok rika, **ktere schranky** se tyka. ### Google Workspace Dve cesty, staci jedna: - **JSON klic service accountu** (`X-Google-Service-Account-Json`) plus opravneni (`X-Google-Service-Account-Scopes`). Tohle je cesta pro provoz bez cloveka: sluzba si z klice vystavi token sama. - **Hotovy access token** (`X-Google-Access-Token`). Plati asi hodinu, takze na trvaly provoz to neni. Overeni konektoru cte Disk, takze service account potrebuje aspon scope `https://www.googleapis.com/auth/drive.readonly`. Bez nej test skonci chybou, i kdyz je klic v poradku. ### Google Analytics 4, Search Console, Google Ads Vsechny tri pouzivaji tentyz princip prihlaseni pres Google, jen s jinym prefixem hlavicky. **Jeden service account staci na vsechny tri**, kdyz se mu v kazde sluzbe udeli pristup. Google Ads navic vzdy potrebuje developer token. ### Sklik Jeden token z API Drak. Vygenerovani noveho tokenu **zneplatni ten predchozi**, takze se u sdileneho uctu vyplati vedet, kdo ho generoval naposled. ### Meta Ads Token systemoveho uzivatele z Business Manageru. Uzivatelsky token prestane platit, kdyz clovek odejde z firmy nebo si zmeni heslo, takze se pro server-to-server nehodi. App secret je povinny tam, kde ma aplikace zapnute `appsecret_proof`. ### Prepis hovoru Deepgram i OpenAI klic. Obe sluzby bezi paralelne a treti volani jejich vysledky slucuje, takze bez obou klicu to nefunguje. ### OpenAI Jen API klic. Zadava se **holy**, slovo `Bearer` dopise portal - viz nize. ## OpenAI: sluzba, ktera nebezi u nas Zbytek katalogu jsou nase aplikace za `services.csbot.cz/apps`. OpenAI je cizi domena, se kterou nemuzeme hnout, takze se s ni zachazi jinak na trech mistech. ### Adresa je u sluzby, ne z `appId` `Service` ma nove nepovinne pole `baseUrl` s absolutni adresou. Skladat adresu ze `SERVICES_BASE_URL` by u ni nedavalo smysl - to je zaklad **nasich** aplikaci. Prepsat ji jde dvema zpusoby: | Kudy | Pro koho plati | K cemu | | ------------------ | -------------- | ------------------------------ | | `OPENAI_BASE_URL` | cela instance | brána, napodobenina pri vyvoji | | adresa u konektoru | jedna firma | vlastni Azure OpenAI | Obecne: `_BASE_URL`, kde se z ID sluzby udelaji velka pismena a pomlcka je podtrzitko (`sap-bo` je `SAP_BO_BASE_URL`). ### Klic se zadava holy OpenAI chce `Authorization: Bearer `. Kdyby si mel uzivatel slovo `Bearer` psat sam, byl by to prvni zdroj chyb, ktery **neni videt ani zpetne** - hodnota se z API nevraci, takze preklep v ni uz nikdo nenajde. Pole udaju proto ma nepovinny `prefix` a runtime ho doplni az pri sestaveni hlavicky. Redakce v logu se dela na obojí: na cely retezec i na samotny klic, protoze cizi sluzby vraci v chybe jednou jedno a jednou druhé. ### Co s OpenAI umime | Operace | Endpoint | K cemu | | -------------------- | ---------------------------- | -------------------------------------- | | Zeptat se modelu | `POST /chat/completions` | shrnuti, klasifikace, sepsani odpovedi | | Nahrat soubor | `POST /files` | vrati `fileId` pro dalsi krok | | Zeptat se na soubor | `POST /responses` | vytezeni faktury, smlouvy, fotky | | Prepsat zvuk | `POST /audio/transcriptions` | jeden pruchod prepisem | | Nacist seznam modelu | `GET /models` | co ucet umi, nic nestoji | **Model je volny text s vychozi hodnotou**, ne vyber ze seznamu. Pevny seznam by zestarl pri kazdem vydani noveho modelu a krok stromu by pak odmital hodnotu, kterou ucet umi. Co ucet umi, vrati operace Nacist seznam modelu. **Otazka nad souborem je jiny endpoint nez obycejny dotaz.** Soubor jako vstup umi az Responses API; Chat Completions by prijalo jen text, takze by se obsah PDF musel vlepit rucne - a to nejde. **Nahrani a dotaz jsou dva kroky.** Jednoho souboru se casto pta vic dotazu a nahravat ho pokazde znovu by stalo cas i penize. `temperature` se posila **jen kdyz ji uzivatel vyplni**. Novejsi modely ji odmitaji uplne, takze poslat vychozi hodnotu by krok rozbilo tam, kde o ni nikdo nestal. ## E-mail: sluzba, ktera nejde pres HTTP Zbytek katalogu se vola pres HTTP a operaci vykona skript. SMTP neni HTTP, a skript umi jen `ctx.http` - dat mu sit jinudy by zrusilo pravidlo, ze skript nema jak zavolat ven mimo nas klient. E-mail je proto **vnitrni krok** (`src/runtime/builtinSteps.ts`), stejne jako zalozeni ticketu. Rozdil je jen v tom, kam saha: ticket do naseho uloziste, e-mail na posmovni server firmy. Sluzba to o sobe rika sama, priznakem `transport: 'smtp'`. Podle nej se rozhodne i overeni konektoru. Neni to vlastnost konektoru: jak se sluzba vola, je vlastnost sluzby. ### Co si firma vyplni | Pole | Klic | Poznamka | | ------------------- | ---------- | -------------------------------------------------------- | | SMTP server | `host` | napriklad smtp.seznam.cz | | Port | `port` | 587 pro STARTTLS, 465 pro sifrovane od zacatku | | Sifrovani | `security` | prazdne se ridi portem, prepsat lze ssl, starttls, zadne | | Uzivatel | `user` | obvykle cela adresa | | Heslo | `password` | tajne, z API se nikdy nevraci | | Adresa odesilatele | `from` | server ji musi povolit | | Jmeno odesilatele | `fromName` | co uvidi prijemce misto hole adresy | | Adresa pro odpovedi | `replyTo` | kdyz maji odpovedi chodit jinam | Prazdne sifrovani se ridi portem, protoze to je zvyklost, kterou zna kazdy. Vyslovna hodnota to prebije - jsou servery, ktere to maji jinak. Adresa serveru se hlida stejne jako u HTTP: **nesmi mirit do vnitrni site**. Vyplnuje ji firma, takze je to jedina zabrana proti tomu, aby si nechala navazat spojeni dovnitr. ### Co se vyplnuje v kroku | Pole | Druh | Poznamka | | ------------------- | -------- | ----------------------------------- | | Prijemce | text | adresy oddelene carkou | | Kopie, skryta kopie | text | nepovinne | | Predmet | text | sablona, tedy `Ticket {{ticketId}}` | | Telo zpravy | **html** | pise se jako HTML, vice radku | | Textova verze | longtext | bez vyplneni se vyrobi z HTML | | Adresa pro odpovedi | text | prebije hodnotu z konektoru | Krok vraci `messageId`, `accepted` a `rejected`, takze se za nim da vetvit podminkou na to, jestli server nekoho odmitl. Textova verze neni pridavek. Klient, ktery HTML nezobrazi, by dostal prazdnou zpravu, a filtry nevyzadane posty berou chybejici textovou cast jako priznak spamu. ### HTML telo a dosazovani promennych Druh pole `html` je novy vedle `text` a `longtext` a znamena dve veci: builder ho vykresli jako vysoke pole s neproporcionalnim pismem, a runtime v nem **escapuje dosazene hodnoty**. Escapuje se hodnota, ne sablona. Znacky, ktere napsal autor sablony, jsou zamer; ostre zavorky v hodnote od zakaznika ne. Bez toho by text ticketu s `` prepsal rozvrzeni zpravy a `