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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
122 lines
7.7 KiB
Markdown
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)?
|