From 95889a69c9ba6ec9cf0c61c581193615104a9c54 Mon Sep 17 00:00:00 2001 From: Clement Martin Date: Fri, 17 Oct 2025 12:21:27 +0200 Subject: [PATCH] feat(server): Option to configure SMTPS transport (#22833) * feat(server): Option to configure SMTPS transport This commit adds a boolean option in the SMTP transport configuration to enable the so-called "secure" mode. What it does is use SMTP over TLS instead of relying on the more common STARTTLS option over plain SMTP. * Add missing field in dto * Add missing field * Use a switch instead of text field * Add field in spec * chore: regen open-api --------- Co-authored-by: Jason Rasmussen --- i18n/en.json | 2 ++ .../lib/model/system_config_smtp_transport_dto.dart | 10 +++++++++- open-api/immich-openapi-specs.json | 4 ++++ open-api/typescript-sdk/src/fetch-client.ts | 1 + server/src/config.ts | 2 ++ server/src/dtos/system-config.dto.ts | 3 +++ server/src/repositories/email.repository.ts | 1 + server/src/services/notification-admin.service.spec.ts | 1 + server/src/services/notification.service.spec.ts | 1 + server/src/services/system-config.service.spec.ts | 1 + .../admin-settings/NotificationSettings.svelte | 8 ++++++++ 11 files changed, 33 insertions(+), 1 deletion(-) diff --git a/i18n/en.json b/i18n/en.json index e1702611a7..fede3f8a40 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -211,6 +211,8 @@ "notification_email_ignore_certificate_errors_description": "Ignore TLS certificate validation errors (not recommended)", "notification_email_password_description": "Password to use when authenticating with the email server", "notification_email_port_description": "Port of the email server (e.g 25, 465, or 587)", + "notification_email_secure": "SMTPS", + "notification_email_secure_description": "Use SMTPS (SMTP over TLS)", "notification_email_sent_test_email_button": "Send test email and save", "notification_email_setting_description": "Settings for sending email notifications", "notification_email_test_email": "Send test email", diff --git a/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart b/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart index bdaaa426c5..46307046b4 100644 --- a/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart +++ b/mobile/openapi/lib/model/system_config_smtp_transport_dto.dart @@ -17,6 +17,7 @@ class SystemConfigSmtpTransportDto { required this.ignoreCert, required this.password, required this.port, + required this.secure, required this.username, }); @@ -30,6 +31,8 @@ class SystemConfigSmtpTransportDto { /// Maximum value: 65535 num port; + bool secure; + String username; @override @@ -38,6 +41,7 @@ class SystemConfigSmtpTransportDto { other.ignoreCert == ignoreCert && other.password == password && other.port == port && + other.secure == secure && other.username == username; @override @@ -47,10 +51,11 @@ class SystemConfigSmtpTransportDto { (ignoreCert.hashCode) + (password.hashCode) + (port.hashCode) + + (secure.hashCode) + (username.hashCode); @override - String toString() => 'SystemConfigSmtpTransportDto[host=$host, ignoreCert=$ignoreCert, password=$password, port=$port, username=$username]'; + String toString() => 'SystemConfigSmtpTransportDto[host=$host, ignoreCert=$ignoreCert, password=$password, port=$port, secure=$secure, username=$username]'; Map toJson() { final json = {}; @@ -58,6 +63,7 @@ class SystemConfigSmtpTransportDto { json[r'ignoreCert'] = this.ignoreCert; json[r'password'] = this.password; json[r'port'] = this.port; + json[r'secure'] = this.secure; json[r'username'] = this.username; return json; } @@ -75,6 +81,7 @@ class SystemConfigSmtpTransportDto { ignoreCert: mapValueOfType(json, r'ignoreCert')!, password: mapValueOfType(json, r'password')!, port: num.parse('${json[r'port']}'), + secure: mapValueOfType(json, r'secure')!, username: mapValueOfType(json, r'username')!, ); } @@ -127,6 +134,7 @@ class SystemConfigSmtpTransportDto { 'ignoreCert', 'password', 'port', + 'secure', 'username', }; } diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index b9da330ee5..fdfc40eb6c 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -16726,6 +16726,9 @@ "minimum": 0, "type": "number" }, + "secure": { + "type": "boolean" + }, "username": { "type": "string" } @@ -16735,6 +16738,7 @@ "ignoreCert", "password", "port", + "secure", "username" ], "type": "object" diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 60d72fb32b..5c952c30af 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -71,6 +71,7 @@ export type SystemConfigSmtpTransportDto = { ignoreCert: boolean; password: string; port: number; + secure: boolean; username: string; }; export type SystemConfigSmtpDto = { diff --git a/server/src/config.ts b/server/src/config.ts index 66c03450fa..1a88b0a2d3 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -159,6 +159,7 @@ export interface SystemConfig { ignoreCert: boolean; host: string; port: number; + secure: boolean; username: string; password: string; }; @@ -356,6 +357,7 @@ export const defaults = Object.freeze({ ignoreCert: false, host: '', port: 587, + secure: false, username: '', password: '', }, diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index 1facc6c331..f12846fc9a 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -463,6 +463,9 @@ class SystemConfigSmtpTransportDto { @Max(65_535) port!: number; + @ValidateBoolean() + secure!: boolean; + @IsString() username!: string; diff --git a/server/src/repositories/email.repository.ts b/server/src/repositories/email.repository.ts index 78c89b4a9d..1bc4f0981a 100644 --- a/server/src/repositories/email.repository.ts +++ b/server/src/repositories/email.repository.ts @@ -23,6 +23,7 @@ export type SendEmailOptions = { export type SmtpOptions = { host: string; port?: number; + secure?: boolean; username?: string; password?: string; ignoreCert?: boolean; diff --git a/server/src/services/notification-admin.service.spec.ts b/server/src/services/notification-admin.service.spec.ts index 4a747d41a3..c200897719 100644 --- a/server/src/services/notification-admin.service.spec.ts +++ b/server/src/services/notification-admin.service.spec.ts @@ -14,6 +14,7 @@ const smtpTransport = Object.freeze({ ignoreCert: false, host: 'localhost', port: 587, + secure: false, username: 'test', password: 'test', }, diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index a96caf5ac2..403ed44631 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -40,6 +40,7 @@ const configs = { ignoreCert: false, host: 'localhost', port: 587, + secure: false, username: 'test', password: 'test', }, diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 5a9c7f4df3..c2dff1aaed 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -197,6 +197,7 @@ const updatedConfig = Object.freeze({ transport: { host: '', port: 587, + secure: false, username: '', password: '', ignoreCert: false, diff --git a/web/src/lib/components/admin-settings/NotificationSettings.svelte b/web/src/lib/components/admin-settings/NotificationSettings.svelte index d1d698d4fa..65ca6f9bd7 100644 --- a/web/src/lib/components/admin-settings/NotificationSettings.svelte +++ b/web/src/lib/components/admin-settings/NotificationSettings.svelte @@ -45,6 +45,7 @@ transport: { host: config.notifications.smtp.transport.host, port: config.notifications.smtp.transport.port, + secure: config.notifications.smtp.transport.secure, username: config.notifications.smtp.transport.username, password: config.notifications.smtp.transport.password, ignoreCert: config.notifications.smtp.transport.ignoreCert, @@ -128,6 +129,13 @@ savedConfig.notifications.smtp.transport.password} /> + +