diff --git a/docs/email-migration-checklist.md b/docs/email-migration-checklist.md new file mode 100644 index 00000000..e2a0fac2 --- /dev/null +++ b/docs/email-migration-checklist.md @@ -0,0 +1,108 @@ +# Email sidecar migration — test checklist + +For `7c173b4`, which moved the mail store and all 14 routes out of the platform and into +`officer-email`. Each line is a single yes/no. Report as "M3 ok, R2 needs fix". + +**Pre-req:** `pm2 restart officer-email && pm2 restart officer`, then hard-refresh. Both are needed — +the sidecar gained an HTTP listener and the platform became a proxy; restarting only one leaves them +speaking different protocols. + +**Why this needs testing at all:** the routes moved *verbatim*, so the logic should be identical. What +changed underneath is the transport (browser → platform proxy → sidecar HTTP) and where the user +identity comes from (`X-Officer-User` header instead of the platform's `userMiddleware`). Failures will +cluster in those two places, not in mail logic. + +--- + +## M — Messages and folders + +- [ ] **M1** `/email` loads and lists messages. +- [ ] **M2** Folder switching works, and per-folder counts look right (inbox / sent / all). +- [ ] **M3** Paging past the first page works. +- [ ] **M4** `/email/stats` renders — the totals panel is populated, not zeroed. +- [ ] **M5** Labels/folders list is populated. + +## R — Reading a message + +- [ ] **R1** Open a message → body, sender, date, recipients all render. +- [ ] **R2** Open a thread → the whole conversation, in order. +- [ ] **R3** Mark as read → state sticks after a refresh. +- [ ] **R4** Mark a whole thread read. +- [ ] **R5** Delete a message → it disappears and stays gone after a refresh. +- [ ] **R6** Deep-link `/email/:emailId` in a fresh tab opens that message. + +## S — Search and contacts + +- [ ] **S1** Search returns sensible results. +- [ ] **S2** Search with an apostrophe or quote in the term behaves (`it's`, `"x"`), i.e. no 500. +- [ ] **S3** Contacts list populates (used by the compose autocomplete). + +## A — Attachments + +- [ ] **A1** A message with attachments shows them. +- [ ] **A2** Extracting/downloading an attachment works and the file is intact. + *Attachments write under `DATA_PATH`; the sidecar does this now, so a permissions problem would + surface here first.* + +## L — Live updates (the SSE path) + +- [ ] **L1** Leave `/email` open, send yourself mail → it appears without a manual refresh. +- [ ] **L2** No repeated reconnect errors in the browser console while the page sits idle. + *This is the biggest structural change: `email:new` used to travel sidecar → platform → SSE. The + stream is inside the sidecar now and the IDLE watcher pushes to it in-process.* + +## C — Accounts + +- [ ] **C1** The accounts screen lists the account with its status. +- [ ] **C2** Sync status / last-synced is shown and looks current. +- [ ] **C3** Trigger a manual sync → it starts. +- [ ] **C4** Adding an account still validates the IMAP connection (test with a deliberately wrong + password and expect a clear failure, not a hang). + +## X — Sync regression (still platform-side, deliberately) + +The two sync handlers did **not** move; they still run in the platform queue and now import the store +from its new location. So this block is checking nothing *broke*, not that anything improved. + +- [ ] **X1** New mail still arrives — the periodic sync is running. +- [ ] **X2** Email sync jobs still appear in the Jobs list (they will stop doing so in stage 2, by + design — option A). + +## P — Proxy behaviour + +Run from the host. Mint a token the same way the app does, or reuse a browser one. + +- [ ] **P1** A long request survives: trigger a from-scratch resync and confirm it isn't cut off around + 60s. The proxy sets `timeoutSeconds: 1800` for the whole `/api/email` prefix. +- [ ] **P2** `/api/email/messages` returns 503 (not a hang or a 500) while `officer-email` is stopped: + ```bash + pm2 stop officer-email + curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOK" \ + -H 'Host: officer.pastilhas.dev' http://127.0.0.1:9010/api/email/messages # expect 503 + pm2 start officer-email + ``` +- [ ] **P3** Nothing in `pm2 logs officer` mentions email internals any more — the platform should be + logging proxy failures at most, never mail. + +--- + +## Attributing a failure + +The split makes this quick: + +| symptom | look at | +|---|---| +| `503 email sidecar not available` | the sidecar isn't running, or hasn't announced its port — `pm2 logs officer-email` | +| `502 email sidecar unreachable` | it announced a port but died; check for a crash on boot | +| `401 missing X-Officer-User` | the proxy didn't inject the header — a platform-side problem | +| anything about messages, folders, IMAP | the sidecar — `pm2 logs officer-email` | + +`pm2 logs officer-email` should now carry everything mail-related. If mail errors are still appearing +in `pm2 logs officer`, something didn't move. + +## Known-broken already, not caused by this + +`gmail-sync` is hardcoded in all three channel handlers (telegram, discord, whatsapp), but the only +account is `provider=gmail, auth_type=password`, which routes to IMAP. So "sync emails" from a chat +channel was already failing before this change. It gets fixed in stage 2, when the channels stop +opening the mail store directly and ask the sidecar over HTTP.