docs: add email connector UI design spec and implementation plan
Move email connector configuration from installer/bootstrap into the vendor admin UI for runtime control over SMTP delivery and self-service registration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1371
docs/superpowers/plans/2026-04-25-email-connector-ui-plan.md
Normal file
1371
docs/superpowers/plans/2026-04-25-email-connector-ui-plan.md
Normal file
File diff suppressed because it is too large
Load Diff
147
docs/superpowers/specs/2026-04-25-email-connector-ui-design.md
Normal file
147
docs/superpowers/specs/2026-04-25-email-connector-ui-design.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Email Connector UI Configuration
|
||||
|
||||
Move email connector setup from the one-shot installer/bootstrap into the vendor admin UI, giving platform admins runtime control over email delivery and self-service registration.
|
||||
|
||||
## Context
|
||||
|
||||
The current flow bakes SMTP configuration into the installer prompts and the Logto bootstrap script. This has two problems: (1) the bootstrap factory selection regex doesn't match the actual Logto SMTP factory ID (`simple-mail-transfer-protocol`), causing it to pick the wrong factory and fail silently; (2) bootstrap is a one-shot — if SMTP is added or changed after first boot, the connector is never created or updated.
|
||||
|
||||
Moving configuration to the UI fixes both issues and gives admins the ability to configure, test, change, or remove email delivery at any time.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
- **SMTP only for now**, but the architecture supports adding other providers (SES, SendGrid, Mailgun, etc.) with one form component and one service method per provider.
|
||||
- **Registration is disabled by default** until email is configured. Admins get a toggle to enable/disable registration independently once email works.
|
||||
- **Test email sends a real email** to a recipient address the admin provides, proving end-to-end delivery.
|
||||
- **Email templates are hardcoded** — four Cameleer-branded HTML templates (Register, SignIn, ForgotPassword, Generic) attached automatically when saving config.
|
||||
- **Email config lives under an expandable "Identity" sidebar section**, replacing the flat external Logto link. The section contains "Email Connector" and "Logto Console" (external link).
|
||||
|
||||
## Section 1: Removal
|
||||
|
||||
### Installer — bash (`installer/install.sh`)
|
||||
|
||||
- Remove SMTP prompt block (~lines 499-509): `prompt_yesno`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM_EMAIL`
|
||||
- Remove SMTP vars from `.env` generation
|
||||
- Remove SMTP vars from `cameleer.conf` persistence
|
||||
|
||||
### Installer — PowerShell (`installer/install.ps1`)
|
||||
|
||||
- Remove env var reads (lines 95-99): `$_ENV_SMTP_HOST` through `$_ENV_SMTP_FROM_EMAIL`
|
||||
- Remove config file parsing (lines 305-309): `smtp_host` through `smtp_from_email` cases
|
||||
- Remove env fallback merging (lines 342-346): `if (-not $c.SmtpHost)` blocks
|
||||
- Remove SMTP prompt block (lines 516-523)
|
||||
- Remove SMTP `.env` output (lines 778-782, 789)
|
||||
- Remove SMTP `cameleer.conf` output (lines 1028-1031, 1036)
|
||||
|
||||
### Compose template (`installer/templates/docker-compose.saas.yml`)
|
||||
|
||||
- Remove the 5 SMTP env vars from the cameleer-logto service (lines 30-35): `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM_EMAIL`
|
||||
|
||||
### Bootstrap (`docker/logto-bootstrap.sh`)
|
||||
|
||||
- Remove Phase 8b entirely (lines 568-649): SMTP connector creation via `/api/connector-factories` and `/api/connectors`
|
||||
- Modify Phase 8c (lines 657-682): Change `signInMode` from `"SignInAndRegister"` to `"SignIn"`. Remove `signUp.identifiers: ["email"]` and `signUp.verify: true`. Keep username+password sign-in method for the admin user. Registration gets enabled by the backend when the admin configures email.
|
||||
|
||||
## Section 2: Backend — Email Connector API
|
||||
|
||||
### New controller: `EmailConnectorController`
|
||||
|
||||
Location: `src/main/java/net/siegeln/cameleer/saas/vendor/EmailConnectorController.java`
|
||||
|
||||
`@RestController`, `@RequestMapping("/api/vendor/email-connector")`, `@PreAuthorize("hasAuthority('SCOPE_platform:admin')")`
|
||||
|
||||
Endpoints:
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| GET | `/api/vendor/email-connector` | Returns current email connector config (password masked) + registration enabled state. 404 if none configured. |
|
||||
| POST | `/api/vendor/email-connector` | Creates or updates connector. Accepts SMTP config + optional `registrationEnabled` boolean. Attaches branded templates. Enables registration on first save unless explicitly set to false. |
|
||||
| DELETE | `/api/vendor/email-connector` | Removes connector, force-disables registration. |
|
||||
| POST | `/api/vendor/email-connector/test` | Accepts `{to: "email"}`, sends test email through configured connector, returns success/failure with message. |
|
||||
|
||||
### New service: `EmailConnectorService`
|
||||
|
||||
Location: `src/main/java/net/siegeln/cameleer/saas/vendor/EmailConnectorService.java`
|
||||
|
||||
Responsibilities:
|
||||
- Maps provider-specific DTOs to Logto connector config format
|
||||
- Selects the correct Logto factory ID per provider (SMTP = `simple-mail-transfer-protocol`)
|
||||
- Hardcodes the four Cameleer-branded HTML email templates (Register, SignIn, ForgotPassword, Generic) with `{{code}}` placeholder and `#C6820E` brand color
|
||||
- Manages sign-in experience toggle via `PATCH /api/sign-in-exp`
|
||||
- Handles test email flow
|
||||
|
||||
### New methods on `LogtoManagementClient`
|
||||
|
||||
Location: `src/main/java/net/siegeln/cameleer/saas/identity/LogtoManagementClient.java`
|
||||
|
||||
Following the existing SSO connector method patterns:
|
||||
|
||||
- `listConnectorFactories()` — `GET /api/connector-factories`
|
||||
- `listConnectors()` — `GET /api/connectors`
|
||||
- `createConnector(factoryId, config)` — `POST /api/connectors`
|
||||
- `updateConnector(connectorId, config)` — `PATCH /api/connectors/{id}`
|
||||
- `deleteConnector(connectorId)` — `DELETE /api/connectors/{id}`
|
||||
- `testConnector(factoryId, email, config)` — `POST /api/connectors/{factoryId}/test` (Logto's built-in test endpoint; sends a real email with the provided config without needing to save first)
|
||||
- `updateSignInExperience(config)` — `PATCH /api/sign-in-exp`
|
||||
- `getSignInExperience()` — `GET /api/sign-in-exp`
|
||||
|
||||
## Section 3: Frontend — Email Configuration Page
|
||||
|
||||
### New page: `EmailConfigPage.tsx`
|
||||
|
||||
Location: `ui/src/pages/vendor/EmailConfigPage.tsx`
|
||||
|
||||
Follows the CertificatesPage pattern (Card layout, form fields, mutation hooks, toast notifications).
|
||||
|
||||
**Three UI states:**
|
||||
|
||||
| Email configured | Registration toggle | signInMode |
|
||||
|---|---|---|
|
||||
| No | Disabled, off | `SignIn` |
|
||||
| Yes | On (default after first save) | `SignInAndRegister` |
|
||||
| Yes | Off (admin chose to disable) | `SignIn` |
|
||||
|
||||
**Unconfigured state:**
|
||||
- Info alert: "Email delivery is not configured. Self-service registration is disabled."
|
||||
- SMTP form: Host (text), Port (number, default 587), Username (text), Password (password), From Email (email). All required.
|
||||
- Save button.
|
||||
|
||||
**Configured state:**
|
||||
- Card showing current config: host, port, username, from-email. Password masked as `••••••••`.
|
||||
- Registration toggle (switch) with label "Enable self-service registration".
|
||||
- Edit button to modify config, Delete button with confirmation dialog warning that removal disables registration.
|
||||
- "Send Test Email" section: text input for recipient + Send button. Success/failure shown inline.
|
||||
|
||||
### New hooks: `email-connector-hooks.ts`
|
||||
|
||||
Location: `ui/src/api/email-connector-hooks.ts`
|
||||
|
||||
Following the certificate-hooks pattern:
|
||||
|
||||
- `useEmailConnector()` — `GET /api/vendor/email-connector`
|
||||
- `useSaveEmailConnector()` — `POST /api/vendor/email-connector`
|
||||
- `useDeleteEmailConnector()` — `DELETE /api/vendor/email-connector`
|
||||
- `useTestEmailConnector()` — `POST /api/vendor/email-connector/test`
|
||||
|
||||
### Router (`router.tsx`)
|
||||
|
||||
- Add `/vendor/email` route inside the vendor `RequireScope` guard
|
||||
|
||||
### Sidebar (`Layout.tsx`)
|
||||
|
||||
- Replace the flat "Identity (Logto)" external link with an expandable "Identity" section
|
||||
- Items: "Email Connector" (internal link to `/vendor/email`), "Logto Console" (external link, preserved)
|
||||
|
||||
## Section 4: Extensibility — Adding Future Providers
|
||||
|
||||
To add a new email provider (e.g. AWS SES):
|
||||
|
||||
1. **Backend**: Add a request DTO and a mapping method in `EmailConnectorService` that maps to the provider's Logto config schema and returns the correct factory ID
|
||||
2. **Frontend**: Add a `SesConfigForm.tsx` component and a new option in the provider selector dropdown on `EmailConfigPage`
|
||||
|
||||
No changes needed to:
|
||||
- `EmailConnectorController` (provider-agnostic endpoints)
|
||||
- `LogtoManagementClient` (works with any factory/connector)
|
||||
- Email templates (shared across providers)
|
||||
- Registration toggle logic (shared across providers)
|
||||
- React Query hooks (provider-agnostic)
|
||||
Reference in New Issue
Block a user