feat: add unified email/messaging management system#80
Conversation
Adds Messages page with Gmail, Outlook, and Teams account management, sync coordination with per-account caching, draft queue with AI generation stub, and Playwright selector configuration for DOM scraping.
There was a problem hiding this comment.
Pull request overview
Introduces a new Messages feature that unifies email/messaging management (Gmail/Outlook/Teams) with a new UI page and a server-side API for accounts, inbox caching, drafts, sync, and selector configuration.
Changes:
- Adds
/api/messagesroutes + JSON-file-backed services for message accounts, cached inbox, drafts, sync dispatching, and selector storage. - Adds a new client
/messages/:tabpage with Inbox/Accounts/Drafts/Sync tabs and Socket.IO-driven sync status UI. - Registers Messages navigation + routing, and extends shared PATHS/data locations for messages storage.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| server/services/messageSync.js | Implements JSON cache load/save, inbox aggregation/search, and per-account sync dispatch. |
| server/services/messageSender.js | Draft sending dispatcher that updates draft status and emits socket events. |
| server/services/messagePlaywrightSync.js | Selector CRUD + Playwright sync/send/test stubs. |
| server/services/messageGmailSync.js | Gmail MCP sync/send stubs. |
| server/services/messageDrafts.js | JSON-file draft CRUD + basic status updates. |
| server/services/messageAccounts.js | JSON-file account CRUD + sync metadata. |
| server/routes/messages.js | Express API endpoints with Zod validation for accounts/inbox/drafts/sync/selectors. |
| server/lib/fileUtils.js | Adds PATHS.messages data directory path. |
| server/index.js | Registers /api/messages route mount. |
| client/src/services/api.js | Adds message API helpers (accounts, inbox, drafts, sync, selectors). |
| client/src/pages/Messages.jsx | New Messages page with tab routing and account prefetch. |
| client/src/components/messages/InboxTab.jsx | Inbox list + search/filter + detail view. |
| client/src/components/messages/MessageDetail.jsx | Message view + draft generation/saving actions. |
| client/src/components/messages/AccountsTab.jsx | Account create/toggle/delete UI. |
| client/src/components/messages/DraftsTab.jsx | Draft review/approve/send/delete UI. |
| client/src/components/messages/SyncTab.jsx | Sync triggers + selector editor + socket listeners for sync status. |
| client/src/components/Layout.jsx | Adds Messages nav item + full-width layout for messages routes. |
| client/src/App.jsx | Adds /messages/:tab routes and lazy-loads Messages page. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…t validation - AccountsTab: handle API errors with .catch() to reset UI state - Rename getSyncStatus to getMessageSyncStatus for consistency - Determine sendVia from account provider in draft generation - Validate/clamp limit and offset query params in inbox route - Guard deduplication against undefined externalId - Annotate messages with accountId when aggregating across accounts - Coerce invalid tab params to inbox in Messages page - Wrap provider sync in error handler with status update and event - Wrap send dispatch in error handler to prevent stuck draft status
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
- Validate accountId as UUID in inbox and message detail routes - Add UUID guard in loadCache/saveCache to prevent path traversal - Fix delete handler: 204 returns null, use .then(() => true) pattern - Add fallback branch in sender for !result.success without error field
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…pollution - Fix empty query string appended to inbox/drafts API URLs - Allow empty string in email field (fix Zod .email() vs default '') - Move /:accountId/:messageId after draft routes to prevent collision - Validate UUID on account PUT/DELETE params - Restrict selector provider to allowed values (outlook, teams) - Derive sendVia from account when not specified in draft creation
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…elete - Add 300ms debounce to inbox search to avoid per-keystroke fetches - Validate accountId exists before creating drafts in POST /drafts - Delete message cache and drafts when account is deleted - Add deleteCache() and deleteDraftsByAccountId() helpers
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Adds real CDP WebSocket integration for scraping messages from Outlook and Teams via the portos-browser instance. Includes Launch Browser button for Playwright accounts, auth detection (Okta/Microsoft login pages), DOM evaluation via Runtime.evaluate, and selector testing against live pages.
…ener, tests, quality fixes - Allow empty string in updateAccountSchema email field for parity with create - Add UUID validation to sync endpoints for consistent 400 responses - Add messages:sync:failed socket listener in SyncTab to clear spinner - Create messages.test.js with 49 tests covering routes/validation/errors - Extract duplicated search filter in messageSync, consolidate sender error handling - Add UUID validation to draft and launch param routes - Add error guards to client async handlers
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
… validation errors - Skip non-UUID cache filenames in inbox aggregation to prevent stray files breaking /inbox - Use JSON.stringify for selector interpolation in CDP Runtime.evaluate to prevent JS injection - Close WebSocket in evaluateOnPage error handler to prevent resource leaks - Switch from schema.parse() to validateRequest() for proper 400 responses on invalid input
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
- Add in-memory per-account lock to prevent concurrent syncs (returns 409)
- Move cache read inside lock scope to prevent interleaved read-write
- Support structured provider result { messages, status } for accurate sync status
- Only mark sync as 'success' when provider truly succeeds
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
- Always emit messages:sync:completed with status so client clears syncing state - Replace onRefresh() with reactive setAccounts() in AccountsTab for local state updates
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
When selectors.json doesn't exist (fresh install), the SyncTab rendered no selector editor cards because it only mapped Object.entries of an empty object. Define DEFAULT_SELECTORS for outlook/teams and merge them with fetched data so the editor cards always appear. Addresses review thread PRRT_kwDOQx8jQ85ym5Zk
…messageSync tests
- Return { error, status: 502 } instead of throwing in syncAccount for consistent error handling
- Remove duplicate error toasts in AccountsTab and DraftsTab (shared request helper already handles)
- Add 42 unit tests for messageSync covering cache IO, dedup, trimming, lock behavior, and error paths
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…t refactor, VITE_PORT comment - Update CLAUDE.md to acknowledge simple re-entrancy guards are acceptable - Remove unused onRefresh prop from AccountsTab - Fix deleteCache to only log success on actual unlink, differentiate ENOENT - Replace inline test copies with tests through real exported functions - Add clarifying comment for VITE_PORT in pm2Standardizer template
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…onal sync toast - Detect Vite processes by VITE_PORT env var in addition to script name regex - Rename PORTS.WEB to PORTS.UI in pm2Standardizer template for consistency - Only show sync success toast when status is actually 'success'
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
… duplicate field, deterministic test - Update pm2Standardizer guidance to accept PORTS.UI for Vite dev ports - Fix streamingDetect config-file heuristics to set devUiPort instead of uiPort for Vite - Remove redundant isViteProcess field (identical to usesVite) - Replace setTimeout with setImmediate in sync lock test for determinism
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Summary
/messages/:tab) with unified email/messaging management across Gmail, Outlook, and TeamsNew Files (18)
Server (7):
routes/messages.js,services/messageAccounts.js,messageDrafts.js,messageSync.js,messageGmailSync.js,messagePlaywrightSync.js,messageSender.jsClient (6):
pages/Messages.jsx,components/messages/InboxTab.jsx,AccountsTab.jsx,DraftsTab.jsx,SyncTab.jsx,MessageDetail.jsxModified Files (5)
server/index.js— register/api/messagesroutesserver/lib/fileUtils.js— addmessagespath to PATHSclient/src/App.jsx— add/messages/:tabroute (lazy loaded)client/src/components/Layout.jsx— add Messages nav item, full-width layoutclient/src/services/api.js— add 18 message API functionsTest plan
/messages/inbox— empty state shows correctly