PSA/ee/docs/plans/2025-12-26-imap-inbound-email-service-plan.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

122 lines
7.7 KiB
Markdown

# IMAP Inbound Email Service Plan
Status: Draft
Owner: Email Platform
Last updated: 2025-12-26
## Goals
- Add an IMAP inbound email provider that feeds the same `INBOUND_EMAIL_RECEIVED` event flow as Gmail and Microsoft.
- Provide a resilient, long-running IMAP service that uses IDLE, reconnects automatically, and handles multiple folders per mailbox.
- Expose IMAP as a first-class inbound email provider in the settings UI alongside Google and Microsoft.
## Current State (Research Summary)
- Gmail inbound flow: `server/src/app/api/email/webhooks/google/route.ts` decodes Pub/Sub, loads provider config, fetches full message details via `GmailAdapter`, and publishes `INBOUND_EMAIL_RECEIVED` via `shared/events/publisher.ts`.
- Microsoft inbound flow: `server/src/app/api/email/webhooks/microsoft/route.ts` validates Graph notifications, fetches message details via `MicrosoftGraphAdapter`, then publishes `INBOUND_EMAIL_RECEIVED` (with fallback minimal payload on fetch failure).
- Test flow: `server/src/services/email/MailHogPollingService.ts` emits `INBOUND_EMAIL_RECEIVED` directly via the server EventBus.
- Workflow trigger: `services/workflow-worker/src/WorkflowWorker.ts` consumes workflow events (Redis stream) and starts `shared/workflow/workflows/system-email-processing-workflow.ts`, which waits for the `INBOUND_EMAIL_RECEIVED` payload defined in `shared/workflow/streams/eventBusSchema.ts`.
## Proposed Architecture
### High-level flow
1) IMAP service connects to configured mailboxes and folders.
2) IDLE receives new mail notifications (or polling fallback if IDLE unsupported).
3) IMAP service fetches the full RFC822/body + headers for new messages.
4) Parse MIME into `EmailMessageDetails` and publish `INBOUND_EMAIL_RECEIVED` using `shared/events/publisher.ts`.
5) Workflow worker consumes and runs the existing system email processing workflow (no change).
### Service placement
- New service in `services/email-service` (Node + TS, similar to `services/workflow-worker`).
- Uses shared DB access and shared event publisher to avoid duplicating queue/event logic.
- Deploy as a long-running worker alongside existing services (server, workflow-worker, temporal-worker).
## Data Model & Configuration
### Provider types
- Extend all inbound email unions to include `imap`:
- `shared/interfaces/inbound-email.interfaces.ts`
- `server/src/interfaces/email.interfaces.ts`
- `server/src/components/EmailProviderConfiguration.tsx` (`providerType` union)
- Any API/action payloads that validate provider type
### New vendor config table
Create `imap_email_provider_config` with fields (initial draft):
- `email_provider_id`, `tenant`
- Connection: `host`, `port`, `secure` (TLS), `allow_starttls`, `auth_type` (password | oauth2)
- Auth: `username`, `password` (store encrypted or via tenant secret provider), `oauth_access_token`, `oauth_refresh_token`, `oauth_expires_at`
- Processing: `auto_process_emails`, `max_emails_per_sync`, `folder_filters` (jsonb array)
- State: `last_uid`, `uid_validity`, `last_seen_at`, `last_error`, `last_sync_at`
### Provider CRUD + validation
- Update `server/src/services/email/EmailProviderService.ts` and `server/src/lib/actions/email-actions/emailProviderActions.ts` to support IMAP configs.
- Add validation in `server/src/services/email/EmailProviderValidator.ts` for IMAP host/port/auth requirements.
- Add secret storage integration via `getSecretProviderInstance()` for IMAP passwords (and optional OAuth tokens) instead of plain DB storage.
## IMAP Service Design
### Connection lifecycle
- Maintain one IMAP connection per provider + folder (or multiplex folders on a single connection if library allows).
- On startup, load active IMAP providers and connect to all configured folders.
- Use IDLE to listen for `EXISTS`/`RECENT` events; on disconnect or IDLE timeout, re-enter IDLE.
- Implement exponential backoff with jitter on reconnect.
### Message tracking + dedupe
- Track `uidvalidity` and `last_uid` per provider/folder; on mismatch, resync (search unseen from scratch).
- Persist state to `imap_email_provider_config` to survive restarts.
- Use `Message-ID` header plus providerId as a secondary dedupe key (insert into `email_processed_messages` or a new `imap_processed_messages` table) to avoid double-publishing.
### MIME parsing
- Fetch full RFC822 or `BODY.PEEK[]` and parse with a MIME parser (e.g., `mailparser`).
- Map into the existing `EmailMessageDetails` structure:
- `subject`, `from`, `to`, `cc`, `receivedAt`
- `body.text` and `body.html`
- `attachments` metadata (id, name, contentType, size)
- `threadId`, `references`, `inReplyTo`, and `headers`
### Event publishing
- Publish `INBOUND_EMAIL_RECEIVED` using `shared/events/publisher.ts` with payload:
- `tenantId`, `tenant`, `providerId`, and the full `emailData` object
- Reuse existing workflow pipeline and ticket creation logic without changes.
## UI/UX Updates (Inbound Email Settings)
- Add an IMAP card to `server/src/components/EmailProviderSelector.tsx` and `ee/server/src/components/EmailProviderSelector.tsx`.
- Add an IMAP provider form in both OSS and EE components:
- Fields: mailbox, host, port, TLS/starttls, username, password/app-password, folder filters, auto-process, max emails per sync, inbound ticket defaults.
- Update provider list cards to show IMAP status and connection errors.
## API + Service Integration
- New IMAP service reads configs via DB (not via server API) to avoid API coupling.
- Optional admin endpoints (future): manual reconnect, force resync, or pause provider.
- Add Docker Compose entry for `email-service` (dev + prod) with required env vars.
## Observability
- Log structured events per provider: connect, disconnect, idle start, new message count, publish success/failure.
- Update `email_providers.status`, `error_message`, `last_sync_at` from the IMAP service on state changes.
- Add a lightweight health check endpoint or readiness log for monitoring.
## Testing Strategy
- Unit tests for IMAP MIME parsing into `EmailMessageDetails`.
- Integration tests with a local IMAP server (e.g., dockerized test server) validating:
- IDLE + reconnect
- Folder filters
- Deduplication by UID + Message-ID
- Published `INBOUND_EMAIL_RECEIVED` payload
- E2E: simulate incoming email and verify ticket creation via workflow.
## Implementation Plan (Tasks)
- [ ] Add `imap` provider type across shared/server interfaces and validation.
- [ ] Create `imap_email_provider_config` migration + indices + Citus distribution.
- [ ] Extend provider CRUD/actions to read/write IMAP config and secrets.
- [ ] Build IMAP adapter/parser (message fetch + MIME parsing) and map to `EmailMessageDetails`.
- [ ] Build `services/email-service` with connection manager, IDLE loop, reconnect strategy, and publisher.
- [ ] Add UI forms and selector card for IMAP in both OSS and EE bundles.
- [ ] Wire Docker Compose for the new service (dev + prod) and document env vars.
- [ ] Add tests (unit + integration) and update inbound email docs to include IMAP.
## Risks & Mitigations
- **IMAP server variance**: IDLE support and folder semantics differ. Mitigate with polling fallback and robust folder selection logic.
- **Duplicate events**: IMAP can replay on reconnect. Mitigate with UID + Message-ID dedupe and persisted state.
- **Credential handling**: Storing passwords requires secure storage; use tenant secret provider and avoid logging secrets.
- **Large attachments**: IMAP fetch size can be heavy; fetch metadata first and defer content where possible.
## Open Questions
- Do we want OAuth2 for IMAP (XOAUTH2) in v1 or password-only?
- Should IMAP providers share the same inbound defaults schema as Google/Microsoft (yes), or add IMAP-specific defaults?
- Preferred IMAP test server for CI (e.g., Dovecot, GreenMail, or MailHog alternative)?