Mockup se k databazi nedostane, takze pribyl treti rezim: JSON soubor. Prezije restart procesu i containeru, ale ne redeploy - filesystem containeru je docasny. Je to mezistupen, ne nahrada databaze, a tak je to i napsane v portalu. | Rezim | Kdy | Restart | Redeploy | | -------- | --------------------------------- | ------- | -------- | | postgres | DATABASE_URL i SECRETS_KEY | prezije | prezije | | file | neni DB, ale je DATA_DIR | prezije | ne | | memory | ani jedno, nebo nejde zapsat | ne | ne | Rozhodnuti zustava na jednom miste (src/data/connectorStore.ts). Pridano: - src/data/snapshot.ts: atomicky zapis (.tmp a prejmenovani), slucovani zapisu a dokonceni rozepsaneho zapisu pri SIGTERM. Bez atomickeho zapisu by pad uprostred nechal polovicni JSON, ktery se pri startu nenacte. Rozbity soubor se prejmenuje na .broken a jede se dal - aplikace, ktera nenastartuje, je pro AppFactory nefunkcni sluzba - src/data/connectors/local.ts: jeden kod pro pamet i soubor, lisi se jen tim, kam se zapisuje. Nahrazuje memory.ts, dve implementace by se casem rozesly - klic k sifrovani se mimo databazi vygeneruje do DATA_DIR/secrets.key s pravy 0600, takze sifrovani funguje bez nastaveni. Chrani to proti nahodnemu precteni JSONu, ne proti pristupu k disku - klic lezi vedle dat a je to tak napsane i v portalu. U databaze se negeneruje vubec: kdo ma zalohu tabulky, ma i klic ze stejneho stroje - DATA_DIR v konfiguraci, data/ v .gitignore a .dockerignore - hlaska v portalu rozlisuje tri nasledky: pamet, soubor a databaze Overeno bez databaze: konektor s vyplnenymi udaji prezil restart, v JSONu jsou hodnoty sifrovane a plaintext v nem neni. Pote s databazi: rezim postgres funguje dal a klic vedle dat se nevygeneroval. Kontejner i data/ po overeni smazany. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
19 KiB
99 - Zaznam zmen
Nejnovejsi nahore.
2026-08-12 - soubor jako uloziste bez databaze
Mockup se k databazi nedostane, takze pribyl treti rezim: JSON soubor. Prezije restart procesu i containeru, ale ne redeploy.
Pridano
src/data/snapshot.ts: atomicky zapis (.tmpa prejmenovani), slucovani zapisu a dokonceni rozepsaneho zapisu priSIGTERM. Rozbity soubor se prejmenuje na.broken, zaloguje a jede se s prazdnymi daty - aplikace, ktera nenastartuje, je pro AppFactory nefunkcni sluzba.src/data/connectors/local.ts: jeden kod pro pamet i soubor, lisi se jen tim, kam se zapisuje. Nahrazujememory.ts- dve implementace by se casem rozesly.- Klic k sifrovani se mimo databazi vygeneruje do
DATA_DIR/secrets.key, takze sifrovani funguje bez nastaveni. U databaze se negeneruje: kdo ma zalohu tabulky, ma i klic ze stejneho stroje. DATA_DIRv konfiguraci,data/v.gitignorea.dockerignore.- Hlaska v portalu rozlisuje tri nasledky: pamet (ztrata pri restartu), soubor (ztrata pri redeployi) a databaze (bez ztraty, hlaska se nezobrazuje).
Overeno
Bez databaze: konektor s vyplnenymi udaji prezil restart, v JSONu jsou hodnoty
sifrovane a plaintext v nem neni. S databazi: rezim postgres funguje dal
a klic vedle dat se nevygeneroval.
2026-08-12 - databaze pro konektory
Konektory se ukladaji do Postgresu, pristupove udaje sifrovane. Popis v 14-databaze.md.
Pridano
pgjako zavislost, pool vsrc/db/pool.tsvcetne transakci adbFor(tenantId)jako sev pro budouci oddelenou databazi jednoho klienta.- Migrace ze souboru
src/db/migrations/*.sql, pousti se pri startu podpg_advisory_lock- pri rolling deployi je jinak pusti vsechny instance naraz. Jeden soubor je jedna transakce, takze pri chybe nevznikne rozdelane schema. - Sifrovani pristupovych udaju (
src/db/secretBox.ts), AES-256-GCM s nahodnym IV a verzi klice. Nerozsifrovatelna hodnota nepada, chova se jako nevyplnena a zaloguje se - jeden rozbity konektor nesmi shodit seznam ostatnich. - Dve implementace uloziste konektoru za jednim rozhranim (
memory,postgres). Rozhodnuti je jen na jednom miste, vsrc/data/connectorStore.ts. /health/readys pingem do databaze./healthna databazi zamerne nezavisi: kratky vypadek DB by jinak vedl k restartovani containeru.GET /api/dashboard/storagea hlaska na strance Konektory o tom, ze data jsou jen v pameti. Bez toho se clovek divi, kam se podely jeho konektory.- Jediny vychozi konektor na firmu a sluzbu hlida castecny unikatni index, ne jen kod. Dva soubezne zapisy by jinak udelaly dva vychozi.
Zmeneno
- Cteni i zapis konektoru je asynchronni, vcetne validace stromu.
- Databaze je volitelna. Bez
DATABASE_URLneboSECRETS_KEYse jede v pameti a rekne se to v logu i v portalu. Container, ktery nenastartuje, je pro AppFactory nefunkcni sluzba. - Kdyz jsou migrace nastavene a selzou, jede se dal v pameti. Psat do rozbiteho schematu je horsi nez neukladat.
Overeno
Proti Postgresu 16 v kontejneru: migrace, sifrovani v tabulce, preziti restartu,
rozsifrovani spravnym klicem, degradace pri spatnem klici, PATCH bez tajneho
pole, prepnuti a smazani vychoziho konektoru, a pametovy rezim bez DATABASE_URL.
2026-08-12 - transformace dat a oprava konektoru
Transformace dat popsana v 13-transformace-dat.md.
Pridano
- Kroky si predavaji i cele struktury.
FieldTypemaobjectalist. Do sablony se nedosazuji, predavaji se jako celek dalsimu kroku - proto je u nich v builderu vyber a ne textove pole. Z podminek nad nimi ma smysl jen "prisla / neprisla". - Strop na velikost struktury (
SCRIPT_MAX_VALUE_BYTES, vychozi 256 kB). Radek s vystupem kroku je nejrychleji rostouci tabulka v systemu. src/scripts/mapping.ts: cesty, prevody, pravidla a sablona JSON. Skripty ho dostanou nactx.utiljakoget,applyRulesafillJson.- Dva rezimy transformace:
transform.map-fields(pole na pole s prevody) atransform.to-json(sablona cileveho objektu s${cesta}). V sablone je marker${...}zamerne jiny nez{{...}}: sablony kroku se dosazuji driv, nez krok bezi, a stihly by ji prepsat. - Prevod
mappro seznamy. Bez nej by slo prevest hlavicku dokladu, ale ne polozky objednavky, a doklad by byl na nulu. idoklad.create-invoice-from-object: druha polovina prikladu, bere hotove telo dokladu z transformace.- Spoustec e-shopu predava celou objednavku jako objekt a polozky jako seznam.
- Klikaci editor pravidel v builderu vcetne rezimu JSON pro vnorena pravidla.
- Kontrola JSONu a tvaru pravidel uz pri ulozeni stromu. Preklep je nedodelek, ne chyba ukladani - rozdelana prace se nezahazuje.
Opraveno
- Konektor neukladal pristupove udaje. Server byl v poradku, chyba byla
v prohlizeci: u pole
type="password"prohlizec ignorujeautocomplete="off"a dosazuje ulozene prihlaseni. Uzivatel pak videl jednu hodnotu, React drzel jinou, a ulozilo se to, co drzel React, tedy nic. Resi toautocompletenew-password, jmena poli, ktera nepripominaji heslo, a prepinac zobrazeni, aby slo overit, co je opravdu zapsane. - Zakladani a uprava konektoru se presunuly do dialogu. Na strance jsou jen male karty - formulare rozlozene po strance byly u vic konektoru neprehledne.
2026-08-12 - sluzby a konektory
Rozdeleni na sluzbu a konektor. Popis v 12-sluzby-a-konektory.md.
Slovo "konektor" driv v kodu znamenalo katalog toho, co umime. Ted znamena napojeni jedne firmy, tedy to, co tim mysli i uzivatel.
Pridano
src/data/services.ts: sluzba nesegeneral,appId,visibility,credentialsaverifyPath. Kategorieobecnesdruzuje veci, ktere ma kazdy a nepotrebuji konektor: webhook, planovac, tickety, transformace dat, HTTP pozadavek, pauza, log.- Viditelnost sluzby: vsichni, jen uvedene firmy a lide, nebo jen spravce platformy. Neviditelna sluzba se z API nevraci vubec.
src/data/connectorStore.ts: konektory za firmu vcetne hodnot pristupovych udaju. Hodnoty se z API nikdy nevraci, jenfilledamissing.FlowStep.connectorId: krok rika, pod kterym napojenim se ma volat.null= vychozi konektor firmy, takze vzorovy strom je prenositelny.- Overeni konektoru pres
verifyPath, tedy cteci volani vyzadujici autorizaci. U sluzby bez nej se overi jen dostupnost a odpoved to rekne nahlas. - Stranky
/dashboard/sluzbya/dashboard/konektoryvcetne formularu udaju. - Endpointy
/api/dashboard/servicesa CRUD/api/dashboard/connectorsvcetne Swaggeru. - Predvyplnene prihlaseni spravcem platformy a prepinac demo uctu na login strance. Kvuli testovani prototypu, pred ostrym pouzitim odebrat.
Zmeneno
- Pristupove udaje se prestaly cist z environment variables. Cela instance
by mela jedny udaje spolecne a dve firmy by fakturovaly z jednoho uctu.
Z prostredi zustava jen
SERVICES_BASE_URL. - Stav "napojeno" se prestal cist z katalogu a zacal pocitat z konektoru firmy.
Sluzba ma jen
availableneboplanned. - Prejmenovani napric kodem:
ConnectornaService,FlowStep.connectorIdnaserviceId,GET /connectorsnaGET /services,connectorIconsnaserviceIcons, stranka Konektory (katalog) na Sluzby. Prevodni tabulka je v dokumentu 12. - Validace stromu overuje i konektor. Cizi konektor je chyba, chybejici napojeni nedodelek.
2026-08-12 - skripty konektoru
Naprogramovana vykonna cast konektoru. Popis je v 11-skripty-konektoru.md.
Pridano
scripts/se skripty konektoru. Jeden soubor nese manifest (vstupni a vystupni parametry) i kod. Obycejny JavaScript, aby se nemusel prekladat.- Hot reload podle casu zmeny souboru. Uprava v portalu i rucni uprava souboru se projevi bez restartu.
- Kontrola vstupu i vystupu proti manifestu, jedna funkce pro obe strany. Chybejici povinny vystup je chyba skriptu, ne uzivatele.
ctxpredavany skriptu:httpnad adresou napojeni,util,log,config,idempotencyKey,failaretry. Skript nedostane pristupove udaje.- Rozliseni opakovatelne a koncove chyby. Runner nikdy nevyhodi vyjimku, vzdy
vraci vysledek vcetne
retryable. - Redakce tajnych hodnot pred zapisem do logu.
- Napojeni z environment variables (
src/scripts/connections.ts) vcetne iDokladu. - Sest ukazkovych skriptu pro iDoklad proti skutecnemu API sluzby
services.csbot.cz/apps/idoklad, kazdy na jiny vzor. - Stranka
/dashboard/skripty: seznam, manifest, editor, zkusebni spusteni. Formular testu se sklada z manifestu, nepise se pro kazdy skript. - Endpointy
/api/dashboard/scripts,/:id,PUT /:id,/:id/testa/reload. Vse ve Swaggeru.
Zmeneno
- Katalog konektoru uz neni jen staticky seznam. Akce ze skriptu se domeruji
prekryvem v
src/data/connectors.ts, takze se naraz objevi ve validaci stromu, ve vypoctu scope i v sablonach. Pri stejnem ID operace vyhrava skript. ConnectorOperationmaimplementationascriptId. Katalog v portalu operace se skriptem oznacuje ikonou.ApiErrorna klientovi nese cele telo odpovedi a umi z nej vytahnoutissues.- Dockerfile kopiruje
scripts/do vysledneho image.
Vedome neudelano
Skripty bezi v procesu serveru, ne v sandboxu. Jsou nase a prosly gitem. Zakaznicke skripty budou potrebovat izolovany engine ve vlastnim vlakne, duvod je v 10-runtime-a-kapacita.md.
Ulozeni z portalu zapisuje do souboru v containeru. Bez trvaleho svazku ho redeploy vrati na verzi z gitu.
2026-08-12 - navrhy
Pridany 09-navrh-rozsireni.md a 10-runtime-a-kapacita.md.
09 popisuje datove modely: akce navazane na typ nebo tag ticketu s telem jako operaci, vlastnim stromem nebo skriptem, typy a tagy ticketu, role a prava jako data misto unionu, zalozky a zpristupneni konektoru za firmu, konektory rozdelene na definici, zpristupneni a napojeni, cekaci krok, sablony zprav, vlastni widgety se seskupovanim a prevod na Postgres. Soucasti je kontrola navrhu proti celemu prikladu se dvema firmami jednoho cloveka.
10 popisuje vykonnou cast: cestu udalosti od webhooku pres inbox a dispatcher
k workeru, frontu v Postgresu se SKIP LOCKED, davkovy odber, spravedlnost mezi
klienty, idempotenci, retence a rozpocet na 150 klientu ve dvou scenarich objemu.
Nic z toho neni naprogramovane, oba dokumenty jsou navrh k rozhodnuti. Kod se nemenil.
2026-08-03
Tickety predelane na plnohodnotny konektor. Prestavaji byt polozkou v seznamu a stavaji se prichozim pozadavkem, ktery ma sveho cloveka a dohledatelny prubeh. Popis modelu je v 06-tickety.md.
Pridano
- Kanaly do ticketu: WhatsApp jako novy konektor, e-mail a hlasova linka jako plnohodnotne spoustece.
- Konektor Tickety presunut do nove kategorie
servicedesk, rozsiren o spoustececreated,unknown-customer,assigned,status-changeda akceassign,set-status,link-customer. - Resitele (
src/data/people.ts) oddelene od uzivatelu portalu, spojka e-mailem. - Log ticketu ve strome vcetne toho, co ktera volana sluzba vratila.
- Prehled vytizeni tymu, kdo co ma u sebe, zaroven jako filtr seznamu.
- Detail ticketu
/dashboard/tickety/:id: prubeh a log, prirazeni, stav, zakaznik, komentare. - Filtry seznamu ticketu na serveru: resitel (vcetne
meaunassigned), stav, kanal. providedFieldsv katalogu konektoru: parametry, ktere spoustec predava sam.- Udalost
ticket.assignedna sbernici i v portalu. - Endpointy
/api/dashboard/people,/tickets/workload,/tickets/:ida POST varianty pro assign, status a comment. Vse ve Swaggeru.
Zmeneno
Ticketma misto volnehorequesterstrukturovanehocustomers nullableidfirmy v CRM, k tomuchannel,assigneejako odkaz na cloveka aautomationId.customer.id === nullje nosna informace, ne chybejici udaj. Prave na ni se pta podminka "mame zakaznika?" ve strome automatizace.- Simulace ticketu bere kanal a prepinac, jestli se zakaznik dohleda. Zakladany ticket dostane cely realisticky log.
- Builder ukazuje parametry od sluzby jen ke cteni. Server je pri ulozeni vzdy dosadi z katalogu, a to jeste pred validaci stromu.
Vedome neudelano
Bugs a wishes zustavaji mimo. Vyvojarska agenda ma jiny zivotni cyklus a slucovat ji s tickety by znamenalo, ze ani jedna evidence nefunguje poradne.
Doplneno pote
Puvodni verze mela diru: ticket nemel zadny obsah a krok "Zalozit ticket" nesel nastavit. Slo tedy rict "z WhatsApp udelej ticket", ale ne uz co se ma kam ulozit.
Ticket.bodyaTicket.sourceRef. Predmet je shrnuti, telo je cely text pozadavku.bodyvystaveno i ve spoustecichcreatedaunknown-customer, takze na obsah ticketu jde udelat podminka v navazne automatizaci.- Nastavitelna pole akci (
OperationFieldaFlowStep.inputs). Ticket, e-mail a WhatsApp maji skutecna pole misto pouhe napovedy. - Sablony
{{parametr}}v hodnotach poli (src/data/templates.ts) vcetne nabidky parametru, ktera je vklada na pozici kurzoru. - Vyber resitele u akci se plni ze seznamu lidi, ne z rucne psaneho ID.
- Nevyplnene povinne pole a odkaz na neexistujici parametr se hlasi jako nedodelek. Nastaveni pole, ktere akce nema, je chyba 400.
- Akce bez
inputsto v builderu napisou primo na karte kroku.
Vystupy kroku a predvalidace
Druha dira: kroky slo vkladat kamkoliv, ale podminka videla jen parametry spoustece. Slo tedy pridat krok "zeptej se CRM", ale ne se vetvit podle toho, co vratil. Bez toho byla predvalidace k nicemu.
outputFieldsv katalogu: co akce vrati dalsim krokum. Ma je "Dohledat firmu" (customerKnown,companyId,companyName), "Zaradit do kategorie", "Zalozit obchodni pripad" i "Zalozit ticket".- Nova akce RAYNET "Dohledat firmu". Nic nezaklada, jen odpovi, jestli odesilatele zname. Presne pro predvalidaci.
src/data/flowScope.tspocita, co je videt v kterem miste stromu. Krok vidi spoustec plus vystupy kroku pred nim. Vetev nepridava nic do sekvence za podminkou, protoze nemusela probehnout.- Builder nabizi v podmince i v polich akce presne ty parametry, ktere v danem miste doopravdy jsou.
- Odkaz na parametr, ktery ve strome neni, je chyba 400. Odkaz na parametr, ktery vznika az pozdeji, je nedodelek s radou posunout podminku niz.
- Duplicitni jmeno parametru ve scope je nedodelek. V sablone by nesl poznat, ktery se dosadi.
Kanaly a vzorove automatizace
- Konektory Facebook Messenger a Instagram, kanaly
facebookainstagramu ticketu. - Ctyri nove vzorove automatizace v rozdeleni, ktere odpovida zameru: jedna
na kanal pro prijem, jedna spolecna pro smerovani na resitele.
Prijmove zamerne neprirazuji, smerovani si ticket prevezme a podminkou
assigned neni splnenoneprepise rucni rozhodnuti.
Firmy a prava
Treti a nejvazneji dira: portal nemel zadnou tenanci. Kterykoliv prihlaseny
uzivatel videl vsechny tickety vsech firem a cely seznam resitelu, requireRole
se nikde nevolal. Popis v 07-firmy-a-prava.md.
- Tenant jako hranice viditelnosti.
tenantIdna ticketu, resiteli i automatizaci. - Uzivatel muze patrit do vic firem, v kazde s jinou roli (
memberships). Pristup napric firmami je zvlast jakoplatformAdmin. - Tri pohledy na tickety:
all,tenant,mine. Admin mezi nimi prepina, vcetne vyberu firmy. src/data/access.tsjako jedine misto, kde se rozhoduje o pravech.GET /api/dashboard/accessrekne klientovi, co smi kreslit.- Filtr na firmu je v ulozistich povinny argument. Zapomenuty filtr neznamena "vse", ale nezkompiluje se.
- Nikdy tise nezuzujeme. Cizi firma vraci 403 nebo 404.
- Prirazeni jen v ramci firmy. Prehazovat praci mezi lidmi smi jen admin, agent si smi vzit ticket na sebe.
requireRolenahrazenrequirePlatformAdmin. Prava uvnitr firmy resiaccess.ts, protoze zavisi na tom, ktera firma pozadavek zajima.- Demo ucty pokryvaji vsechny tri situace vcetne cloveka ve dvou firmach.
Nastavitelny dashboard
Prehled byl pevne dany. Ted si ho kazdy sklada sam, popis v 08-dashboard-widgety.md.
- Katalog widgetu na serveru vcetne toho, ktere sirky ktery widget unese. Graf v tretine sloupce se necte, proto se tam ani nenabizi.
- Rezim uprav na
/dashboard: pridani z nabidky, vyber sirky, poradi sipkami, odebrani. Ulozi se az tlacitkem, Zrusit vrati puvodni stav. - Rozlozeni se uklada za dvojici uzivatel a firma, ne jen za uzivatele.
- Data nacita prehled a rozdava je widgetum. Deset dlazdic tak neznamena deset stejnych dotazu.
- Server rozlozeni overuje proti katalogu. Neznamy widget nebo nepodporovana sirka se neulozi, widget zmizely z katalogu se v prehledu ukaze jako chyba, ne ze tise zmizi.
Zapsano jako otevrene rozhodnuti
Vsechny automatizace se stejnym spoustecem se spusti. Doporucene rozdeleni na to nenarazi, ale az se bude psat runtime, musi se to rozhodnout vedome. Varianty a doporuceni v 06-tickety.md.
2026-07-31
Prvni nasazeni aplikace do repozitare csbot-prototype.
Puvodni sablona byla holy Express s endpointy / a /health. Nahradil ji
kompletni web a klientsky portal.
Pridano
- Verejny web: homepage se sekcemi, sluzby, o nas, kontakt s formularem, 404.
- Prihlaseni pres JWT s demo ucty.
- Klientsky portal: prehled s grafem, tickety, incidenty, automatizace, konektory, nastaveni.
- Builder automatizaci: strom akci, spoustec, vetveni podminkou.
- Katalog 25 konektoru v 8 kategoriich.
- Webhook s registrovanou adresou, token generuje server.
- Zivy dashboard pres SSE, vcetne indikatoru spojeni a bublin s udalostmi.
- Simulace provozu pod tlacitkem v postrannim menu portalu.
- Swagger UI na
/docsa OpenAPI definice na/openapi.json. - Dokumentace ve slozce
documentation/. .gitignore, ktery drzinode_modulesadistmimo repozitar.
Zmeneno oproti sablone
- Aplikace prepnuta na ESM (
"type": "module") amodule: NodeNext. - Jeden container obsluhuje API i zbuildovanou React aplikaci z
dist/public. - Dockerfile buildu je server i web, vysledny image dostava jen
dist. - Port zustava 3000, naslouchani na
0.0.0.0beze zmeny.
Reseni reverse proxy
ROOT_PATHse cte z prostredi, nikde neni hardcoded.- Aplikace se mountuje na koren i na prefix, funguje tedy at Caddy prefix odstrani nebo ne.
- Server vklada do
index.htmlznacku<base>awindow.__BASE_PATH__, aby SPA nasla soubory i na vnorenych cestach. - OpenAPI
serversobsahuje prefix, takze Swagger Try it out vola spravnou adresu. - Router ma
strict: true, jinak by se presmerovani/docsna/docs/zacyklilo.
Overeno lokalne
S ROOT_PATH=/apps/csbot-prototype:
/apps/csbot-prototype/healthi/healthvraci 200,/apps/csbot-prototype/docspresmeruje na/docs/, ta vraci Swagger UI,swagger-ui.cssse nacte pres prefix,- OpenAPI
serversobsahuje/apps/csbot-prototype, index.htmlna vnorene ceste obsahuje spravny<base>,- prihlaseni pres prefix vraci token,
- neexistujici cesta pod
/apivraci JSON, ne HTML aplikace.
Znama omezeni
Data jsou v pameti, restart je vrati do vychoziho stavu. Obsah webu je ukazkovy. Ulozeny strom automatizace se nevykonava.