From 8e392cfeab8d26593147c6c5a4458d0d26aa8c44 Mon Sep 17 00:00:00 2001 From: Rostislav Dugin Date: Sun, 8 Mar 2026 22:48:28 +0300 Subject: [PATCH] FEATURE (email): Add skipping TLS for email notifier --- .../notifiers/models/email_notifier/model.go | 26 ++++++--- ...nsecure_skip_verify_to_email_notifiers.sql | 11 ++++ .../notifiers/models/email/EmailNotifier.ts | 1 + .../ui/edit/EditNotifierComponent.tsx | 1 + .../notifiers/EditEmailNotifierComponent.tsx | 55 ++++++++++++++++++- .../notifier/ShowEmailNotifierComponent.tsx | 7 +++ 6 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 backend/migrations/20260308120000_add_insecure_skip_verify_to_email_notifiers.sql diff --git a/backend/internal/features/notifiers/models/email_notifier/model.go b/backend/internal/features/notifiers/models/email_notifier/model.go index 55e1648..eeb9368 100644 --- a/backend/internal/features/notifiers/models/email_notifier/model.go +++ b/backend/internal/features/notifiers/models/email_notifier/model.go @@ -23,13 +23,14 @@ const ( ) type EmailNotifier struct { - NotifierID uuid.UUID `json:"notifierId" gorm:"primaryKey;type:uuid;column:notifier_id"` - TargetEmail string `json:"targetEmail" gorm:"not null;type:varchar(255);column:target_email"` - SMTPHost string `json:"smtpHost" gorm:"not null;type:varchar(255);column:smtp_host"` - SMTPPort int `json:"smtpPort" gorm:"not null;column:smtp_port"` - SMTPUser string `json:"smtpUser" gorm:"type:varchar(255);column:smtp_user"` - SMTPPassword string `json:"smtpPassword" gorm:"type:varchar(255);column:smtp_password"` - From string `json:"from" gorm:"type:varchar(255);column:from_email"` + NotifierID uuid.UUID `json:"notifierId" gorm:"primaryKey;type:uuid;column:notifier_id"` + TargetEmail string `json:"targetEmail" gorm:"not null;type:varchar(255);column:target_email"` + SMTPHost string `json:"smtpHost" gorm:"not null;type:varchar(255);column:smtp_host"` + SMTPPort int `json:"smtpPort" gorm:"not null;column:smtp_port"` + SMTPUser string `json:"smtpUser" gorm:"type:varchar(255);column:smtp_user"` + SMTPPassword string `json:"smtpPassword" gorm:"type:varchar(255);column:smtp_password"` + From string `json:"from" gorm:"type:varchar(255);column:from_email"` + IsInsecureSkipVerify bool `json:"isInsecureSkipVerify" gorm:"default:false;column:is_insecure_skip_verify"` } func (e *EmailNotifier) TableName() string { @@ -99,6 +100,7 @@ func (e *EmailNotifier) Update(incoming *EmailNotifier) { e.SMTPPort = incoming.SMTPPort e.SMTPUser = incoming.SMTPUser e.From = incoming.From + e.IsInsecureSkipVerify = incoming.IsInsecureSkipVerify if incoming.SMTPPassword != "" { e.SMTPPassword = incoming.SMTPPassword @@ -198,7 +200,10 @@ func (e *EmailNotifier) sendStartTLS( func (e *EmailNotifier) createImplicitTLSClient() (*smtp.Client, func(), error) { addr := net.JoinHostPort(e.SMTPHost, fmt.Sprintf("%d", e.SMTPPort)) - tlsConfig := &tls.Config{ServerName: e.SMTPHost} + tlsConfig := &tls.Config{ + ServerName: e.SMTPHost, + InsecureSkipVerify: e.IsInsecureSkipVerify, + } dialer := &net.Dialer{Timeout: DefaultTimeout} conn, err := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig) @@ -237,7 +242,10 @@ func (e *EmailNotifier) createStartTLSClient() (*smtp.Client, func(), error) { } if ok, _ := client.Extension("STARTTLS"); ok { - if err := client.StartTLS(&tls.Config{ServerName: e.SMTPHost}); err != nil { + if err := client.StartTLS(&tls.Config{ + ServerName: e.SMTPHost, + InsecureSkipVerify: e.IsInsecureSkipVerify, + }); err != nil { _ = client.Quit() _ = conn.Close() return nil, nil, fmt.Errorf("STARTTLS failed: %w", err) diff --git a/backend/migrations/20260308120000_add_insecure_skip_verify_to_email_notifiers.sql b/backend/migrations/20260308120000_add_insecure_skip_verify_to_email_notifiers.sql new file mode 100644 index 0000000..f3baa92 --- /dev/null +++ b/backend/migrations/20260308120000_add_insecure_skip_verify_to_email_notifiers.sql @@ -0,0 +1,11 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE email_notifiers + ADD COLUMN is_insecure_skip_verify BOOLEAN NOT NULL DEFAULT FALSE; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE email_notifiers + DROP COLUMN is_insecure_skip_verify; +-- +goose StatementEnd diff --git a/frontend/src/entity/notifiers/models/email/EmailNotifier.ts b/frontend/src/entity/notifiers/models/email/EmailNotifier.ts index 5803973..91e17ca 100644 --- a/frontend/src/entity/notifiers/models/email/EmailNotifier.ts +++ b/frontend/src/entity/notifiers/models/email/EmailNotifier.ts @@ -5,4 +5,5 @@ export interface EmailNotifier { smtpUser: string; smtpPassword: string; from: string; + isInsecureSkipVerify: boolean; } diff --git a/frontend/src/features/notifiers/ui/edit/EditNotifierComponent.tsx b/frontend/src/features/notifiers/ui/edit/EditNotifierComponent.tsx index 9bacc56..11ea56f 100644 --- a/frontend/src/features/notifiers/ui/edit/EditNotifierComponent.tsx +++ b/frontend/src/features/notifiers/ui/edit/EditNotifierComponent.tsx @@ -112,6 +112,7 @@ export function EditNotifierComponent({ smtpUser: '', smtpPassword: '', from: '', + isInsecureSkipVerify: false, }; } diff --git a/frontend/src/features/notifiers/ui/edit/notifiers/EditEmailNotifierComponent.tsx b/frontend/src/features/notifiers/ui/edit/notifiers/EditEmailNotifierComponent.tsx index 278e136..e0d7c0a 100644 --- a/frontend/src/features/notifiers/ui/edit/notifiers/EditEmailNotifierComponent.tsx +++ b/frontend/src/features/notifiers/ui/edit/notifiers/EditEmailNotifierComponent.tsx @@ -1,5 +1,6 @@ -import { InfoCircleOutlined } from '@ant-design/icons'; -import { Input, Tooltip } from 'antd'; +import { DownOutlined, InfoCircleOutlined, UpOutlined } from '@ant-design/icons'; +import { Checkbox, Input, Tooltip } from 'antd'; +import { useState } from 'react'; import type { Notifier } from '../../../../../entity/notifiers'; @@ -10,6 +11,9 @@ interface Props { } export function EditEmailNotifierComponent({ notifier, setNotifier, setUnsaved }: Props) { + const hasAdvancedValues = !!notifier?.emailNotifier?.isInsecureSkipVerify; + const [showAdvanced, setShowAdvanced] = useState(hasAdvancedValues); + return ( <>
@@ -163,6 +167,53 @@ export function EditEmailNotifierComponent({ notifier, setNotifier, setUnsaved }
+ +
+
setShowAdvanced(!showAdvanced)} + > + Advanced settings + + {showAdvanced ? ( + + ) : ( + + )} +
+
+ + {showAdvanced && ( +
+
Skip TLS verify
+
+ { + if (!notifier?.emailNotifier) return; + + setNotifier({ + ...notifier, + emailNotifier: { + ...notifier.emailNotifier, + isInsecureSkipVerify: e.target.checked, + }, + }); + setUnsaved(); + }} + > + Skip TLS + + + + + +
+
+ )} ); } diff --git a/frontend/src/features/notifiers/ui/show/notifier/ShowEmailNotifierComponent.tsx b/frontend/src/features/notifiers/ui/show/notifier/ShowEmailNotifierComponent.tsx index 4b1ed23..4739d11 100644 --- a/frontend/src/features/notifiers/ui/show/notifier/ShowEmailNotifierComponent.tsx +++ b/frontend/src/features/notifiers/ui/show/notifier/ShowEmailNotifierComponent.tsx @@ -36,6 +36,13 @@ export function ShowEmailNotifierComponent({ notifier }: Props) {
From
{notifier?.emailNotifier?.from || '(auto)'} + + {notifier?.emailNotifier?.isInsecureSkipVerify && ( +
+
Skip TLS
+ Enabled +
+ )} ); }