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:
hsiegeln
2026-04-25 17:50:47 +02:00
parent 6b77a96d52
commit 9d87f71bc1
2 changed files with 1518 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View 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)