From 85cce0044e94a0f0d7566ed69fdf716fd9e2edcf Mon Sep 17 00:00:00 2001 From: smail Date: Mon, 14 Sep 2026 12:58:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E6=B8=85=E7=90=86?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=A5=E5=BF=97=E8=B0=83=E7=94=A8=E6=A0=88?= =?UTF-8?q?=E7=BB=9D=E5=AF=B9=E8=B7=AF=E5=BE=84=EF=BC=8C=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E5=A4=8DSMTP=E4=B8=AD=E7=BB=A7=E9=85=8D=E7=BD=AE=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/config/config.go | 2 +- backend/config/manager_v2.go | 14 +++++++++++ backend/hooks/framework/framework.go | 3 ++- backend/utils/runtimepath/layout.go | 23 +++++++++++++++++++ .../ConfigSettings/ConfigForm/ConfigForm.js | 4 ++-- .../ConfigSettings/ConfigSettings.js | 5 +++- 6 files changed, 46 insertions(+), 5 deletions(-) diff --git a/backend/config/config.go b/backend/config/config.go index 7bb4079..afc2dda 100644 --- a/backend/config/config.go +++ b/backend/config/config.go @@ -155,7 +155,7 @@ func (l *logFormatter) Format(entry *log.Entry) ([]byte, error) { } } if entry.Caller != nil { - b.WriteString(fmt.Sprintf("[%s:%d]", entry.Caller.File, entry.Caller.Line)) + b.WriteString(fmt.Sprintf("[%s:%d]", runtimepath.CleanCallerPath(entry.Caller.File), entry.Caller.Line)) } b.WriteString(entry.Message) // 错误字段也是有效内容,不能在消息为空时被格式器丢弃。 diff --git a/backend/config/manager_v2.go b/backend/config/manager_v2.go index e669049..28ee8bd 100644 --- a/backend/config/manager_v2.go +++ b/backend/config/manager_v2.go @@ -883,6 +883,13 @@ func applySectionPayload(current RuntimeConfig, section Section, payload json.Ra return RuntimeConfig{}, ErrInvalidSecretChange } value.SMTPSectionConfig.Relay.Password = current.SMTP.Relay.Password + if smtpRelayConfigured(value.SMTPSectionConfig.Relay) && value.SMTPSectionConfig.Relay.Security == "" { + if ResolveMailSecurity(value.SMTPSectionConfig.Listener) == MailSecurityImplicitTLS { + value.SMTPSectionConfig.Relay.Security = MailSecurityImplicitTLS + } else { + value.SMTPSectionConfig.Relay.Security = MailSecuritySTARTTLS + } + } candidate.SMTP = value.SMTPSectionConfig candidate.Email.Policy.LoginVerificationPolicy = candidate.SMTP.Relay.LoginPolicy case SectionEmail: @@ -904,6 +911,13 @@ func applySectionPayload(current RuntimeConfig, section Section, payload json.Ra return RuntimeConfig{}, ErrInvalidSecretChange } value.MailSectionConfig.Relay.Password = current.SMTP.Relay.Password + if smtpRelayConfigured(value.Relay) && value.Relay.Security == "" { + if ResolveMailSecurity(value.Listener) == MailSecurityImplicitTLS { + value.Relay.Security = MailSecurityImplicitTLS + } else { + value.Relay.Security = MailSecuritySTARTTLS + } + } candidate.SMTP = SMTPSectionConfig{ Meta: value.Meta, Listener: value.Listener, diff --git a/backend/hooks/framework/framework.go b/backend/hooks/framework/framework.go index 156eac2..99ae370 100644 --- a/backend/hooks/framework/framework.go +++ b/backend/hooks/framework/framework.go @@ -1,6 +1,7 @@ package framework import ( + "git.pcux.cn/xuans/Smail/utils/runtimepath" "bytes" "crypto/subtle" "encoding/json" @@ -141,7 +142,7 @@ func (l *logFormatter) Format(entry *log.Entry) ([]byte, error) { b.WriteString(fmt.Sprintf("[%s]", ctx.GetValue(context.LogID))) } } - b.WriteString(fmt.Sprintf("[%s:%d]", entry.Caller.File, entry.Caller.Line)) + b.WriteString(fmt.Sprintf("[%s:%d]", runtimepath.CleanCallerPath(entry.Caller.File), entry.Caller.Line)) b.WriteString(entry.Message) b.WriteString("\n") diff --git a/backend/utils/runtimepath/layout.go b/backend/utils/runtimepath/layout.go index fb4123e..fee9125 100644 --- a/backend/utils/runtimepath/layout.go +++ b/backend/utils/runtimepath/layout.go @@ -270,3 +270,26 @@ func migrate(root string, rename func(string, string) error) ([]Move, error) { } return completed, nil } + +// CleanCallerPath 清洗调用者代码路径,去除本地盘符和宿主机绝对目录,只保留模块相对路径。 +func CleanCallerPath(path string) string { + if strings.TrimSpace(path) == "" { + return "" + } + normalized := strings.ReplaceAll(path, string(filepath.Separator), "/") + normalized = strings.ReplaceAll(normalized, "\\", "/") + for _, target := range []string{"backend/", "frontend/", "plugins/"} { + if index := strings.Index(normalized, target); index != -1 { + return normalized[index:] + } + } + marker := "smail/" + lower := strings.ToLower(normalized) + if index := strings.Index(lower, marker); index != -1 { + return normalized[index+len(marker):] + } + if len(normalized) >= 2 && normalized[1] == ':' { + return strings.TrimLeft(normalized[2:], "/") + } + return normalized +} diff --git a/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js b/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js index e296ba1..082ddc6 100644 --- a/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js +++ b/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js @@ -11,10 +11,10 @@ const mailSecurity = config => { const value = normalizedMailSecurity(config.listener?.security) return value || 'both' } -const relaySecurityForMail = config => mailSecurity(config) === 'implicit_tls' ? 'implicit_tls' : 'starttls' +export const relaySecurityForMail = config => mailSecurity(config) === 'implicit_tls' ? 'implicit_tls' : 'starttls' const startTLSMail = config => mailSecurity(config) === 'starttls' const implicitTLSMail = config => mailSecurity(config) === 'implicit_tls' -const relayConfigured = config => !!( +export const relayConfigured = config => !!( config.relay?.relayHost || config.relay?.username || config.relay?.password || config.relay?.from ) diff --git a/frontend/src/views/components/ConfigSettings/ConfigSettings.js b/frontend/src/views/components/ConfigSettings/ConfigSettings.js index c451de2..9b2bc24 100644 --- a/frontend/src/views/components/ConfigSettings/ConfigSettings.js +++ b/frontend/src/views/components/ConfigSettings/ConfigSettings.js @@ -3,7 +3,7 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { http } from '@/utils/axios' import { useGlobalStatusStore } from '@/stores/useGlobalStatusStore' import { isRecentAuthenticationCancelled, requestWithRecentAuthentication } from '@/utils/recentAuthentication' -import { readFormConfig, updateFormConfig, validateFormConfig } from './ConfigForm/ConfigForm.js' +import { readFormConfig, updateFormConfig, validateFormConfig, relayConfigured, relaySecurityForMail } from './ConfigForm/ConfigForm.js' const sections = [ { value: 'base', label: '基础配置' }, @@ -493,6 +493,9 @@ export function usePage(pageKey = 'base') { delete payload.relay } } + if (payload.relay && relayConfigured(payload) && !payload.relay.security) { + payload.relay.security = relaySecurityForMail(payload) + } } if (apiSection() === 'plugins' && payload.plugins && typeof payload.plugins === 'object') { Object.values(payload.plugins).forEach(plugin => {