Reply Pilot Google: Gmail
Tato stranka popisuje modul reply-pilot-google/.
Role modulu
- vlastni technicky Gmail OAuth i per-user Google OAuth a Calendar/Gmail API integraci
- vystavuje interni HTTP API pro
reply-pilot-be - vytvari mailbox snapshot payloady a zapisuje Gmail-owned mailbox cache
- vlastni emailovou runtime cache pro inbox read model, attachment bytes a weekly-count reporty pouzivane Jira Reports
- umi inkrementalni sync pres Gmail
historyIdpro vychozi inbox/sent cache - umi udrzovat Gmail
watchstav a prijmout Pub/Sub notifikaci o zmene mailboxu - vytvari Gmail reply drafty
Runtime
- bezi jako samostatny Java 21 Spring Boot/Maven kontejner
- zapisuje runtime stav do
reply-pilot-google/data; loguje jen na stdout/stderr conf/zustava read-only mount- na interni siti ma jediny podporovany alias
reply-pilot-google reply-pilot-beho vola preshttp://reply-pilot-google:5000reply-pilot-jira-reportspouzivahttp://reply-pilot-google:5000/api/reports/email-weekly-counts- Gmail token se drzi v
data/gmail_token.json, aby refresh flow mohl zapisovat GMAIL_TOKEN_FILEzustava token technicke sdilene schranky; per-user Google granty maji samostatnou identitu, Calendar a Gmail scopes a pouzivaji sifrovane credential envelope ulozene backendem- watch stav se drzi v
data/gmail-watch.json, aby se dal bezpecne obnovovat mezi restarty - Gmail mailbox cache se drzi pod
data/accounts/<GMAIL_CACHE_ACCOUNT_ID>/emails/; vychozi account id jedefaultpo safe path sanitizaci reply-pilot-google/data/je runtime-only adresar a nesmi byt verzovany v gitu
Endpointy
GET /healthz- kontroluje jen lokalni runtime modulu a pritomnost/citelnost Gmail token souboru
- neprovadi zadny primy call do Gmail API; dostupnost Gmailu se projevi az pri syncu nebo tvorbe draftu
GET /api/metaGET /api/rate-limit/statusGET /api/gmail/rate-limit/statusGET /api/watchPOST /api/watch/ensurePOST /api/watch/stopPOST /api/watch/notifyPOST /api/watch/pullPOST /api/watch/ackPOST /api/mailbox/snapshotPOST /api/mailbox/historyGET /api/reports/email-weekly-countsGET /api/cache/statusGET /api/cache/inboxGET /api/cache/emailsGET /api/cache/emails/<email_id>GET /api/cache/emails/<email_id>/attachmentsGET /api/cache/emails/<email_id>/attachments/<filename>GET /api/emails/<email_id>GET /api/emails/<email_id>/attachmentsPOST /api/emails/<email_id>/drafts/replyPOST /api/emails/drafts/outboundPOST /api/emails/<email_id>/send/replyPOST /api/emails/send/outboundPOST /api/emails/<email_id>/send/forward- Calendar adapter pro backend:
POST /api/google-calendar/oauth/status,/oauth/authorization-url,/oauth/exchange,/oauth/disconnect,/api/google-calendar/calendars,/weeka/events/{get,create,update,delete} - Per-user Gmail adapter pro backend:
POST /api/user-gmail/profile,/api/user-gmail/snapshot,/api/user-gmail/history,/api/user-gmail/watch,/api/user-gmail/watch/ensure,/api/user-gmail/watch/initial-sync,/api/user-gmail/watch/processed,/api/user-gmail/watch/pulla/api/user-gmail/watch/ack; plaintext access token se v odpovedi nevraci
Reply draft/send endpointy a outbound send endpoint podporuji JSON bez priloh a multipart/form-data s textovymi poli recipient, subject, body_text, volitelne reply_to_message_id a opakovanym file polem attachments. Limit je 10 souboru, 10 MB na soubor a 20 MB celkem.
Import podle spolecnosti
party_organization.import_email_communication ma default TRUE a edituje se
na formulari firmy se stejnymi opravnenimi jako ostatni firemni udaje. Backend
preskoci cele vlakno, pokud vsechny jeho kanonicky navazane firmy maji FALSE;
jakakoli firma s TRUE povoluje cele vlakno. Import policy zahrnuje i skryte
firmy; osobni Gmail dal vyzaduje aspon jednu viditelnou firemni vazbu jako dosud.
Sdilene i povolene osobni schranky
pouzivaji stejnou podminku. Gmail snapshot/history dal stahuje a cachuje data.
Dosavadni DB historie i prilohy zustavaji dostupne, odesilani se tim nezakazuje.
Backend eviduje preskocena vlakna v email_import_skipped_thread. Samostatny
worker job email_import_backfill vola POST /api/mailbox/import/backfill a po
opetovnem zapnuti importu firmy prehrava cele cached vlakno, vcetne chybějících
zprav existujiciho vlakna. Jedna davka fronty obsahuje nejvyse 10 vlaken; chyba se odlozi
o pet minut a zaznam zustava ve fronte. Opakovany import nezdvojuje zpravy.
Pro osobni cache slouzi bearer-protected POST /api/user-gmail/cache/thread:
{"app_user_id": 7, "mailbox_email": "owner@example.test", "thread_id": "thread-id"}
Odpoved obsahuje gmail_page.emails (jedno cele vlakno) a
gmail_page.attachments_by_email (ulozena metadata). Endpoint nevola Google API,
nevyzaduje OAuth envelope a kontroluje shodu mailbox_email s account cache.
Backend pred volanim vyzaduje sync_inbox. Sdilena cache pouziva stavajici
GET /api/cache/emails/<email_id> a attachment endpoint. Nekompletni cache se
neimportuje; replay funguje i po ztrate Gmail pristupu, pokud cache obsahuje
cele vlakno. Backend nedostava filesystem mount ani plaintext Google token.
Stari osobnich konverzaci a opakovany import
BE nastaveni PERSONAL_GMAIL_IMPORT_DAYS je spolecne pro vsechny osobni Google
ucty. Default je 15; 0 vypina limit stari. Zaporne nebo necelociselne hodnoty
jsou chyba konfigurace. Source of truth je
secrets/local/reply-pilot-be.env a secrets/prod/reply-pilot-be.env; po zmene
je nutny restart/redeploy BE. Sdilena schranka nema tento limit.
Rozhoduje datum nejnovejsi zpravy celeho vlakna, prevedene do APP_TIMEZONE
(default Europe/Prague). Prijatelne datum je dnesni lokalni datum minus pocet
dni nebo novejsi, vcetne celeho hranicniho dne. Napriklad 15. 9. 2026 pri limitu
15 projde vlakno s nejnovejsi zpravou 31. 8. 2026 v libovolny cas; 30. 8. uz ne.
Jde o kalendarni dny, ne nasobky 24 hodin; zmena letniho casu hranici neposune.
Osobni Gmail snapshot i incremental sync cachuji plnou historii vybranou pres
{in:inbox in:sent}, bez limitu stari. Samostatna archivovana, Spam a Trash
vlakna se nevybiraji. Vybrane vlakno se uchova cele, vcetne starsich zprav a
priloh. DB import navic vyzaduje aktualni kanonickou firemni vazbu alespon na
jednu viditelnou firmu a firemni import policy popsanou vyse. Vazby se hledaji
pres from/to/cc/bcc ve vsech zpravach: presna aktivni firemni emailova adresa,
kontakt osoby pres CONTACT_FOR nebo presna rucne ulozena firemni DOMAIN.
Skryte firmy zustavaji soucasti rozhodnuti o import_email_communication.
Projde-li cele vlakno, importuji se vsechny jeho zpravy a prilohy, i starsi nez
limit. Stejna kontrola plati pro snapshot, incremental import, replay i aktualizaci
existujiciho DB vlakna. Zkraceni okna, plynuti casu, vypnuti firmy nebo odpojeni
Gmailu dosavadni DB historii ani prilohy nemaze.
Stavajici worker job email_import_backfill navic pri kazdem behu prehodnoti
nejvyse 10 vlaken z inventory jedne osobni cache. Cyklicky strida uzivatele s
sync_inbox a po konci jejich inventory zacina znovu. Tim zachyti rozsireni
okna, 0, nove kontakty/domeny i opetovne povoleni firem bez nove Gmail udalosti.
OAuth grant neni potreba, pokud je cela cache stale dostupna. Opakovani
nezdvojuje zpravy ani prilohy; chybejici/nekompletni vlakno se neimportuje,
ostatni pokracuji a chybne vlakno se zkusi pri dalsim pruchodu.
Vychozi interval workeru je 30 sekund (WORKER_IMPORT_POLL_INTERVAL_SECONDS),
job bezi pri WORKER_IMPORT_ENABLED nebo WORKER_GMAIL_WATCH_PULL_ENABLED.
Prehodnoceni neni okamzite: jedna schranka s 1 000 vlakny potrebuje pri tomto
intervalu priblizne 50 minut plus cas zpracovani; ostatni povolene schranky
pruchod prodluzuji. BE drzi kurzory v pameti; restart zacne pruchod od zacatku.
Predchozi DB fronta pro firemni zakazy zustava zachovana, ale vekem nebo chybejici
vazbou odmitnuta vlakna se najdou primo v cache inventory.
Bearer-protected inventory endpoint POST /api/user-gmail/cache/threads:
{"app_user_id": 7, "mailbox_email": "owner@example.test", "after_thread_id": "", "limit": 10}
{"status": "ok", "gmail_cache": {"mailbox_email": "owner@example.test", "thread_ids": ["thread-id"], "next_after_thread_id": ""}}
limit ma default 10 a maximum 50. Stabilni lexikograficke razeni ID zabranuje
posouvani stranek pri nove zprave ve starem vlakne. Prazdny dalsi kurzor znamena
konec lokalni inventory, nezavisle na vzdalenem Gmail strankovani. BE predava
aktualni Google adresu jako ocekavanou identitu; pokud uzivatel grant odpojil,
adresu vynecha a pouzije identitu svazanou s jeho app-user-<id> cache. Neshoda
neprazdne ocekavane identity vraci 409. Nasledne cteni celeho vlakna vzdy
predava vracenou mailbox identitu. Google API ani OAuth se pri inventory nevola.
Gmail-Owned Mailbox Cache
Detail importovaneho osobniho vlakna v aplikaci cte ulozene zpravy z PostgreSQL
pres autentizovany GET /api/email-threads/<thread_id>/messages, ne ze sdilene
Gmail cache. Odkazy z detailu firmy a zpravy predavaji activity_id, podle
ktereho se vybere stejny provider/mailbox. Bez tohoto parametru lze precist jen
jednoznacne vlakno; vice provideru se stejnym thread ID vraci 409. Zpravy si
zachovaji vlastni activity detail a prilohy pouzivaji activity-scoped endpointy
Google modulu. Tento archivni pohled nezobrazuje shared-mailbox reply/forward
akce. Sdilena vlakna pouzivaji dosavadni cache pohled, pri chybejici cache lze
zobrazit importovanou DB historii. Pri nasazeni teto zmeny nasad nejprve BE,
potom web app; DB migrace ani novy download schranky nejsou potreba.
reply-pilot-google je jediny modul, ktery smi drzet Gmail OAuth material,
Google client dependencies, Gmail watch/rate-limit stav a fyzicke mailbox cache
soubory. reply-pilot-be pouziva jen interni HTTP endpointy Gmail modulu.
Stejna hranice plati pro per-user Google ucet: OAuth client secret, envelope
encryption key, token exchange/refresh/revoke a Google Calendar/Gmail HTTP
volani patri pouze do reply-pilot-google. Backend smi drzet jen opaque
sifrovany envelope a volat interni adapter pres GOOGLE_API_BASE_URL. Osobni
Gmail cache pouziva account id app-user-<id> a backend volani navic strazi
dovednosti sync_inbox.
Fyzicky layout:
/app/data/accounts/<GMAIL_CACHE_ACCOUNT_ID>/emails/index.json/app/data/accounts/<GMAIL_CACHE_ACCOUNT_ID>/emails/threads/*.json/app/data/accounts/<GMAIL_CACHE_ACCOUNT_ID>/emails/attachments/<thread>/<file>
GMAIL_CACHE_ACCOUNT_ID defaultuje na default a runtime jej sanitizuje pro
bezpecnou filesystem komponentu. Attachment relative_path v cache a HTTP
payloadu zustava logicka hodnota emails/attachments/<thread>/<file>. Pri
importu osobni schranky backend ulozi do DB metadata prefix
accounts/app-user-<id>/; tim se globalne unikatni cesta nesrazi se stejnym
Gmail threadId a nazvem souboru v jine schrance. Fyzicke cteni osobni prilohy
se presto provadi jen account-scoped Google API cestou.
Jednorazovy presun stare BE cache:
cd /Users/jan/projects/reply-pilot/reply-pilot-google
scripts/gmail-migrate-cache.sh --account-id default
Script kopiruje vychozi ../reply-pilot-be/data/emails do
${HOST_DATA_DIR}/accounts/<account-id>/emails a odmita ne-prazdny target,
pokud neni explicitne zadane --force.
Osobni Gmail snapshot pouziva samostatny account id app-user-<id>. Jeho
strankovani cache pouze doplnuje a pri prvni strance nerestartuje index, aby
preruseny opakovany import nezneviditelnil drive ulozena vlakna. Drive ulozene
osobni prilohy jsou append-only: novy snapshot je neprepise ani neodstrani.
Mazani podle Gmail history se pro osobni schranky nesmi zapnout, dokud serverova
kopie a attachment read path nejsou prokazatelne nezavisle na zdrojove schrance.
Autentizovany backend endpoint POST /api/google-calendar/gmail/sync-step
zpracuje prave jednu snapshot stranku prihlaseneho uzivatele. Nejprve ji Google
modul ulozi vcetne priloh do per-user cache, potom backend importuje odpovidajici
vlakna a jejich persistovana attachment metadata v jedne DB transakci. Az po
uspesnem commitu odpoved obsahuje next_page_token a history_id; stejny vstupni
page_token je proto mozne po chybe bezpecne zopakovat. Endpoint sam zadny kurzor
neuklada.
Autentizovane backend endpointy GET /api/google-calendar/gmail/watch a
POST /api/google-calendar/gmail/watch/ensure umi precist a obnovit oddeleny
watch stav povoleneho uzivatele.
Worker pri zapnutem Gmail watch pullu vola take interni backend endpoint
POST /api/mailbox/personal-gmail/watch/pull. Pokud nema zadny uzivatel
explicitne zapnutou dovednost sync_inbox, endpoint neobnovuje watch, necte
osobni subscription a nevola Gmail API. Pro kazdeho povoleneho uzivatele nejprve
obnovi watch a po jedne strance importuje cely pocatecni snapshot. Resume token
stranky ulozi az po uspesnem DB commitu; po chybe se proto stejna stranka
idempotentne zopakuje. Pub/Sub notifikace konkretni schranky se potvrdi az po
dokonceni pocatecniho snapshotu, uspesnem importu vsech stranek Gmail historie a
ulozeni processed_history_id. Vypnuty znamy ucet a notifikace jineho prostredi
se v dane environment-specific subscription potvrdi bez importu.
Watch stav je svazan s normalizovanou e-mailovou adresou. Pokud by stejny
app_user_id zacal ukazovat na jinou Gmail schranku, automaticky sync skonci
konfliktem a nepouzije stary cursor ani cache pro novy ucet.
Rate limit a pozastaveni osobni synchronizace
Google modul pri Gmail rate limitu uchova Google error.message,
error.errors[].reason a puvodni HTTP status. Interni HTTP 429 odpoved vraci
reason, google_http_status, google_error_reason, retry_at,
retry_after_seconds a retry_source. Zprava je omezena na 1024 znaku a bez
ridicich znaku; nevraci se cely Google response body ani request headers.
Deadline ma prednost z Retry-After (retry_source: header), jinak z casu
Retry after <ISO timestamp> v Google zprave (body). Bez pouzitelneho budouciho
deadlinu zustava dosavadni 900sekundovy cooldown (default_cooldown). Efektivni
deadline obsahuje uz prvni odpoved, ktera cooldown zalozila.
Google modul uklada tyto udaje do mailbox-owned gmail-rate-limit.json, takze
preziji restart. Stary zaznam bez podrobnosti zustava citelny; puvodni Google
zpravu k nemu nelze zpetne doplnit. Nove podrobnosti pribudou pri dalsim skutecnem
odmitnuti Google API, ne pouhym ctenim aktivniho cooldownu.
BE loguje novy cooldown jednou jako WARN Personal Gmail sync paused until=...
s Google duvodem a zdrojem deadlinu. Do expirace pri pravidelnem watch pullu
preskakuje pouze dotceneho uzivatele; dalsi uzivatele synchronizuje dal.
Odpoved POST /api/mailbox/personal-gmail/watch/pull obsahuje paused_user_count
a paused_users s duvodem a retry udaji; cooldown se nepocita do failed_count.
Worker pridava pocet pozastavenych uzivatelu do sveho souhrnneho logu.
Po deadlinu nasledujici pruchod znovu pouzije ulozeny cursor; notifikace
nedokoncene synchronizace se nepotvrzuji. BE drzi tento odklad v pameti a po
restartu jej pri dalsim volani znovu prevezme z Google modulu.
Postupne obnoveni osobniho stahovani
Osobni snapshot i history download zacinaji oknem 1 (nejvyse jedno vlakno snapshotu nebo jeden zaznam history na stranku). Po kazdych trech uspesnych davkach, vcetne ulozeneho dilciho stahovani, se okno zvetsi o 1, nejvyse na 10. Novy Google rate limit, chyba stahovani nebo restart s nedokoncenym pozadavkem vrati okno na 1. Drive ulozeny Google retry deadline ma vzdy prednost.
Jedna davka stahne nejvyse 4 * okno novych priloh. Nove Gmail requesty
spousti v ramci 20sekundoveho rozpoctu, s odstupem nejmene 1000 / okno ms
a pripadne delsimi rozestupy podle vah metod, GMAIL_QUOTA_PER_USER_PER_MINUTE
a GMAIL_QUOTA_OPERATIONAL_HEADROOM_PERCENT. HTTP read timeout pouziva zbyvajici
cas davky, connect timeout nejvyse 5 sekund; vnitrni HTTP retries jsou vypnute.
Jde o rozpoctovani dalsich volani, ne o tvrdy limit celkoveho casu prenosu a
zapisu cache. Lokalni pacing nezna spotrebu jinych klientu stejne schranky a
nemuze zarucit, ze Google dalsi pozadavek prijme.
Progress a velikost okna jsou v account-owned gmail-download-recovery.json.
Rozpracovana metadata stranek/vlaken a jednotlive prilohy se atomicky ukladaji
do gmail-downloads/ vedle emails/. Dalsi pokus i restart pouzije tyto soubory;
velke vlakno proto nemusi znovu stahovat vsechny prilohy od zacatku. Prilohy
z dokoncene cache se znovu pouzivaji podle dvojice Gmail message ID a attachment
ID. Starsi cache bez attachment ID jej dostane pri pristim stazeni pouze po
overeni shody zpravy a obsahu. Existujici soubory priloh se neprepisuji.
Nedokoncena davka vraci interni HTTP 409, code: gmail_sync_pending,
retry_at, retry_after_seconds a retry_source: download_budget s odkladem
20 sekund. BE ji vykaze v paused_users, loguje jako INFO a pokracuje ostatnimi
uzivateli; nezvysi failed_count. Pri pristim worker pruchodu po deadlinu navaze.
Dokud neni cela stranka v cache a BE nepotvrdi DB import, neposouva se checkpoint
ani nepotvrzuji notifikace. Potvrzeni checkpointu uklidi rozpracovane soubory;
hotove emaily a prilohy zustanou v cache.
Pri nasazeni teto zmeny nasad nejprve reply-pilot-be (rozpoznani HTTP 409),
pote reply-pilot-google (adaptivni stahovani). Neni potreba menit env ani SQL
schema. Sdilena schranka a interaktivni operace tento download rezim nepouzivaji.
Watch workflow
POST /api/watch/ensurevola Gmailusers.watchjen kdyz existujici watch nema dostatecnou rezervu do expirace- vychozi watch labels jsou
INBOX,SENT, aby incremental sync zachytil prijata vlakna i sent-only odchozi vlakna - worker muze watch pravidelne obnovovat pres
WORKER_GMAIL_WATCH_*promenne bez dalsiho full scanu mailboxu POST /api/watch/pullcte Gmail Pub/Sub Pull subscription a vraci dekodovane notifikace vcetneack_idPOST /api/watch/notifyulozi metadata prijate Pub/Sub notifikace a vraci, zda je potreba spustit navazujici import workflow- pull flow drzi
ack_idv Gmail-ownedgmail-watch.jsona potvrdi je az po uspesnem zapisu mailbox-wide full snapshotu nebo incremental history cursoru; cilene backfill snapshoty pending watch notifikace nepotvrzuji POST /api/watch/ackzustava explicitni admin operace; pri sync/cache chybe automaticky flow ack neposila, aby doslo k redelivery- doporuceny provoz je zapnout watch renewal a workerovy
gmail_synchronizer, ktery spojuje watch pull i mailbox import do jedne pipeline
Gmail OAuth Token Lifecycle
Gmail OAuth token je uzivatelsky token pro Gmail API a je odlisny od Pub/Sub service account JSON. Runtime soubor je:
- host:
${HOST_DATA_DIR}/gmail_token.json - kontejner:
/app/data/gmail_token.json - env:
GMAIL_TOKEN_FILE=/app/data/gmail_token.json
Sifrovany source of truth:
secrets/local/reply-pilot-google-token.jsonpro lokalni vyvojovy mailboxsecrets/prod/reply-pilot-google-token.jsonpro produkcni mailbox
Start/deploy workflow materializuje token do reply-pilot-google/data/, pokud
odpovidajici SOPS secret existuje. Token se nema kopirovat do Docker image a
reply-pilot-google/data/ se necommituje.
Vygenerovani tokenu:
cd /Users/jan/projects/reply-pilot/reply-pilot-google
scripts/gmail-generate-token.sh \
--token-file /tmp/reply-pilot-google-token.json \
--client-id '...apps.googleusercontent.com' \
--client-secret 'GOCSPX-...'
Zasifrovani lokalniho tokenu:
sops encrypt \
--input-type json \
--output-type json \
--filename-override ../secrets/local/reply-pilot-google-token.json \
/tmp/reply-pilot-google-token.json \
> ../secrets/local/reply-pilot-google-token.json
rm -f /tmp/reply-pilot-google-token.json
Pro produkci pouzij ../secrets/prod/reply-pilot-google-token.json. Lokalni
vyvojovy token ma patrit osobnimu nebo testovacimu mailboxu; produkcni token se
lokalne nepouziva kvuli Gmail API kvotam a riziku zasahu do produkcni schranky.
Pole expiry v JSONu je expirace kratkodobeho access tokenu. Dlouhodobe
opravneni drzi refresh_token; aplikace pri expiraci vola Google token endpoint
a ziska novy access token.
Pub/Sub Runtime Config
GMAIL_WATCH_TOPIC_NAMEje plne jmeno Gmail watch topicu, naprikladprojects/<project-id>/topics/gmail-watchGMAIL_WATCH_SUBSCRIPTION_NAMEje plne jmeno environment-specific Pull subscription, naprikladprojects/<project-id>/subscriptions/reply-pilot-dev-subGMAIL_PERSONAL_WATCH_TOPIC_NAMEje samostatny topic osobnich schranek; pri chybejici hodnote se osobni watch neregistruje do sdileneho topicuGMAIL_PERSONAL_WATCH_SUBSCRIPTION_NAMEje samostatna subscription osobnich schranek a musi byt jina pro local a productionGMAIL_PUBSUB_CREDENTIALS_FILEmuze ukazovat na service account JSON pod/app/conf/...; kdyz chybi, modul zkusi Application Default Credentials- doporuceny sifrovany source of truth pro Pub/Sub service account JSON je
secrets/<environment>/reply-pilot-google-pubsub.json; start/deploy workflow ho materializuje doreply-pilot-google/conf/reply-pilot-pubsub.json - lokalni a produkcni instance maji mit vlastni subscription; obe ale mohou cilit na stejny topic
- lokalni a produkcni instance nemaji sdilet stejnou Pull subscription, pokud maji bezet soucasne
- pokud lokalne netestujes Pub/Sub, vypni workerove
WORKER_GMAIL_WATCH_*aWORKER_GMAIL_WATCH_PULL_*prepinace
Oznaceni vlaken jako prectenych z Reply Pilotu (RP-7598)
Po uspesnem prechodu Email Thread Reply z Reply Pilotu do Waiting for Reply
nebo Done BE ihned zkusi oznacit vlakno jako prectene. Spoustecem je explicitni
edit se zmenou stavu, akce presunu do cekani po odeslani/forwardu nebo uzavreni
emailoveho tasku. Samotny refresh Jira cache, import zpravy a automaticka
synchronizace tasku tuto akci nespousteji. Ostatni typy tasku se neoznacuji.
BE nacte (provider, external_thread_id) z vazby tasku. V Google modulu nejprve
nacte RFC Message-ID hlavicky vsech zprav zdrojoveho vlakna a oznaci zdrojove
vlakno. V ostatnich schrankach vyhleda skutecna Gmail thread ID pres presne
rfc822msgid dotazy; Gmail thread ID se mezi ucty nekopiruje a shoda predmetu
se nepouziva. Vyhledavani zahrnuje i archiv, Spam a Trash, strankuje vysledky a
deduplikuje vlakna. Chybejici Message-ID nevede k sirsi heuristice.
Cilem je sdilena schranka a vsechny osobni schranky s explicitne zapnutou
dovednosti sync_inbox a dostupnym Google grantem, nezavisle na autorovi zmeny
nebo aktualnim assignee tasku. Google modul odstrani pouze label UNREAD pres
Gmail threads.modify.
Nearchivuje ani nemaze zpravy. Lokalni cache se obnovi beznym Gmail history
syncem; vysledkem akce je zmena primo v Gmailu.
Interni endpointy jsou chranene GOOGLE_API_TOKEN:
POST /api/threads/mark-readpro sdilenou schrankuPOST /api/user-gmail/threads/mark-readpro osobni schranku
Payload obsahuje bud thread_id pro zdrojovou schranku, nebo
rfc822_message_ids pro dohledani kopii. Osobni varianta navic predava
app_user_id, mailbox_email a opaque encrypted_credentials; Google modul
overi vazbu identity schranky a vraci pripadny credential_update. Odpoved
gmail_read obsahuje rfc822_message_ids, matched_thread_count,
marked_thread_count a failed_thread_count.
Provedeni je synchronni best-effort pokus po Jira prechodu, bez nove fronty nebo periodickeho worker jobu. Selhani konkretni schranky/brany se zapise jako INFO na stdout/stderr, nezrusi Jira prechod a neblokuje pokus v dalsich schrankach. Pokud nelze nacist identifikatory zdrojoveho vlakna, nelze bezpecne vyhledat jeho kopie a zaznamena se INFO. Selhani samotneho oznaceni zdroje zachova nactene Message-ID pro ostatni schranky. Automaticky retry se neplanuje.
Nasazeni: nejprve reply-pilot-google, potom reply-pilot-be; DB migrace neni
potreba. Nove OAuth autorizace vyzaduji take gmail.modify. Existujici granty
se samy nerozsiri: osobni uzivatele musi znovu autorizovat Google ucet;
sdileny token je potreba znovu vygenerovat pres gmail-generate-token.sh a ulozit
do prislusneho SOPS token secretu podle lifecycle postupu vyse. Dosavadni
read/compose pristupy dal fungují; bez gmail.modify Gmail oznaceni odmitne a
chyba se zaloguje.
Jira tasky osobnich schranek
Nove zpravy importovane do aplikace spousti reply-task automatiku. Pro kazdou
schranku a Gmail thread existuje samostatne vyhledavany Email Thread Reply task.
Nejnovejsi importovana zprava urcuje stav Drafting Reply (prichozi) nebo
Waiting for Reply (odchozi). Assignee je vzdy Jira ucet vlastnika schranky,
nikoli vychozi obchodnik firmy; bez dohledatelne Jira identity zustava pozadavek
ve stavajici retry fronte s chybou (nejvyse tri pokusy). Import emailu tim neselze.
Queue doplni i chybejici tasky pro drive importovana osobni vlakna z DB.
Existujici navazane tasky se beze zmen ve vlakne neaktualizuji.
Zmena vyzaduje DB expand migraci 0087, nasledne BE, App a Search. Cteni tasku,
firemni vazby a vyhledavani rozlisuji provider i thread ID. Osobni odesilani tato
zmena nepridava; odpoved odeslana z Gmailu aktualizuje task po synchronizaci.