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>
7.8 KiB
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
.envgeneration - Remove SMTP vars from
cameleer.confpersistence
Installer — PowerShell (installer/install.ps1)
- Remove env var reads (lines 95-99):
$_ENV_SMTP_HOSTthrough$_ENV_SMTP_FROM_EMAIL - Remove config file parsing (lines 305-309):
smtp_hostthroughsmtp_from_emailcases - Remove env fallback merging (lines 342-346):
if (-not $c.SmtpHost)blocks - Remove SMTP prompt block (lines 516-523)
- Remove SMTP
.envoutput (lines 778-782, 789) - Remove SMTP
cameleer.confoutput (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-factoriesand/api/connectors - Modify Phase 8c (lines 657-682): Change
signInModefrom"SignInAndRegister"to"SignIn". RemovesignUp.identifiers: ["email"]andsignUp.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#C6820Ebrand 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-factorieslistConnectors()—GET /api/connectorscreateConnector(factoryId, config)—POST /api/connectorsupdateConnector(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-expgetSignInExperience()—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-connectoruseSaveEmailConnector()—POST /api/vendor/email-connectoruseDeleteEmailConnector()—DELETE /api/vendor/email-connectoruseTestEmailConnector()—POST /api/vendor/email-connector/test
Router (router.tsx)
- Add
/vendor/emailroute inside the vendorRequireScopeguard
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):
- Backend: Add a request DTO and a mapping method in
EmailConnectorServicethat maps to the provider's Logto config schema and returns the correct factory ID - Frontend: Add a
SesConfigForm.tsxcomponent and a new option in the provider selector dropdown onEmailConfigPage
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)