Files
cameleer-saas/docs/superpowers/specs/2026-04-25-email-connector-ui-design.md
hsiegeln 9d87f71bc1 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>
2026-04-25 17:50:47 +02:00

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