feat: test SMTP connection on save and retain password on edit
All checks were successful
CI / build (push) Successful in 2m20s
CI / docker (push) Successful in 1m36s

Adds testSmtpConnection() that performs EHLO + auth via JavaMailSender
before persisting to Logto — saves fail fast with a clear error if
SMTP credentials are wrong. Password is now optional when editing:
if left blank the backend fetches the existing password from Logto's
connector config, so users can update host/port/fromEmail without
re-entering the password every time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-26 16:23:42 +02:00
parent 0413a5b882
commit 883e10ba7c
4 changed files with 81 additions and 9 deletions

View File

@@ -15,7 +15,7 @@ export interface SmtpConfigRequest {
host: string;
port: number;
username: string;
password: string;
password?: string;
fromEmail: string;
registrationEnabled?: boolean;
}

View File

@@ -50,8 +50,12 @@ export function EmailConfigPage() {
}
async function handleSave() {
if (!host || !username || !password || !fromEmail) {
toast({ title: 'All fields are required', variant: 'error' });
if (!host || !username || !fromEmail) {
toast({ title: 'Host, username, and from email are required', variant: 'error' });
return;
}
if (!isConfigured && !password) {
toast({ title: 'Password is required for new configuration', variant: 'error' });
return;
}
try {
@@ -59,7 +63,7 @@ export function EmailConfigPage() {
host,
port: parseInt(port, 10) || 587,
username,
password,
password: password || undefined,
fromEmail,
});
toast({ title: 'Email connector saved', variant: 'success' });
@@ -217,10 +221,10 @@ export function EmailConfigPage() {
onChange={(e) => setUsername(e.target.value)}
/>
</FormField>
<FormField label="Password *">
<FormField label={isConfigured ? 'Password' : 'Password *'}>
<Input
type="password"
placeholder={isConfigured ? 'Enter new password' : 'Password'}
placeholder={isConfigured ? 'Leave blank to keep current' : 'Password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>