chore(mail): 优化邮件投递超时与服务配置
- 为 SMTP 连接与前端发信请求增加更明确的超时控制。 - 调整 Postfix 与 Rspamd 代理配置,缩短相关处理超时并统一运行参数。
This commit is contained in:
@@ -16,6 +16,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const smtpSessionTimeout = 45 * time.Second
|
||||||
|
|
||||||
type MIMEMessage struct {
|
type MIMEMessage struct {
|
||||||
From string
|
From string
|
||||||
To []string
|
To []string
|
||||||
@@ -142,10 +144,11 @@ func sendSMTPWithConfig(cfg Config, from string, recipients []string, mimeBytes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendSMTPPlain(addr, host string, auth smtp.Auth, from string, recipients []string, mimeBytes []byte) error {
|
func sendSMTPPlain(addr, host string, auth smtp.Auth, from string, recipients []string, mimeBytes []byte) error {
|
||||||
conn, err := net.Dial("tcp", addr)
|
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_ = conn.SetDeadline(time.Now().Add(smtpSessionTimeout))
|
||||||
client, err := smtp.NewClient(conn, host)
|
client, err := smtp.NewClient(conn, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
@@ -156,10 +159,12 @@ func sendSMTPPlain(addr, host string, auth smtp.Auth, from string, recipients []
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendSMTPImplicitTLS(addr, host string, auth smtp.Auth, from string, recipients []string, mimeBytes []byte) error {
|
func sendSMTPImplicitTLS(addr, host string, auth smtp.Auth, from string, recipients []string, mimeBytes []byte) error {
|
||||||
conn, err := tls.Dial("tcp", addr, &tls.Config{ServerName: host, MinVersion: tls.VersionTLS12})
|
dialer := &net.Dialer{Timeout: 10 * time.Second}
|
||||||
|
conn, err := tls.DialWithDialer(dialer, "tcp", addr, &tls.Config{ServerName: host, MinVersion: tls.VersionTLS12})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_ = conn.SetDeadline(time.Now().Add(smtpSessionTimeout))
|
||||||
client, err := smtp.NewClient(conn, host)
|
client, err := smtp.NewClient(conn, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
@@ -170,10 +175,11 @@ func sendSMTPImplicitTLS(addr, host string, auth smtp.Auth, from string, recipie
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendSMTPStartTLS(addr, host string, auth smtp.Auth, from string, recipients []string, mimeBytes []byte) error {
|
func sendSMTPStartTLS(addr, host string, auth smtp.Auth, from string, recipients []string, mimeBytes []byte) error {
|
||||||
conn, err := net.Dial("tcp", addr)
|
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_ = conn.SetDeadline(time.Now().Add(smtpSessionTimeout))
|
||||||
client, err := smtp.NewClient(conn, host)
|
client, err := smtp.NewClient(conn, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
|||||||
@@ -2,17 +2,19 @@ import type { User, AdminUser, AdminOverview, Domain, Mailbox, Alias, MailFolder
|
|||||||
export * from "./api-types"
|
export * from "./api-types"
|
||||||
|
|
||||||
const REQUEST_TIMEOUT_MS = 15_000
|
const REQUEST_TIMEOUT_MS = 15_000
|
||||||
|
const MAIL_DELIVERY_TIMEOUT_MS = 60_000
|
||||||
|
|
||||||
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
async function request<T>(path: string, init: RequestInit & { timeoutMs?: number } = {}): Promise<T> {
|
||||||
|
const { timeoutMs, ...requestInit } = init
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
const timeout = window.setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS)
|
const timeout = window.setTimeout(() => controller.abort(), timeoutMs || REQUEST_TIMEOUT_MS)
|
||||||
const externalSignal = init.signal
|
const externalSignal = requestInit.signal
|
||||||
if (externalSignal) {
|
if (externalSignal) {
|
||||||
if (externalSignal.aborted) controller.abort()
|
if (externalSignal.aborted) controller.abort()
|
||||||
else externalSignal.addEventListener("abort", () => controller.abort(), { once: true })
|
else externalSignal.addEventListener("abort", () => controller.abort(), { once: true })
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const res = await fetch(path, { credentials: "include", headers: { "Content-Type": "application/json", ...(init.headers || {}) }, ...init, signal: controller.signal })
|
const res = await fetch(path, { credentials: "include", headers: { "Content-Type": "application/json", ...(requestInit.headers || {}) }, ...requestInit, signal: controller.signal })
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
let message = `${res.status} ${res.statusText}`
|
let message = `${res.status} ${res.statusText}`
|
||||||
try { const body = await res.json(); message = body.error || message } catch {}
|
try { const body = await res.json(); message = body.error || message } catch {}
|
||||||
@@ -83,7 +85,7 @@ export const api = {
|
|||||||
adminMessage: (id: string) => request<MailMessage>(`/api/admin/messages/${id}`),
|
adminMessage: (id: string) => request<MailMessage>(`/api/admin/messages/${id}`),
|
||||||
systemSettings: () => request<SystemSettings>("/api/admin/settings"),
|
systemSettings: () => request<SystemSettings>("/api/admin/settings"),
|
||||||
updateSystemSettings: (payload: SystemSettingsPayload) => request<SystemSettings>("/api/admin/settings", { method: "POST", body: JSON.stringify(payload) }),
|
updateSystemSettings: (payload: SystemSettingsPayload) => request<SystemSettings>("/api/admin/settings", { method: "POST", body: JSON.stringify(payload) }),
|
||||||
testSmtp: (to: string) => request<{ ok: boolean }>("/api/admin/settings/test-smtp", { method: "POST", body: JSON.stringify({ to }) }),
|
testSmtp: (to: string) => request<{ ok: boolean }>("/api/admin/settings/test-smtp", { method: "POST", body: JSON.stringify({ to }), timeoutMs: MAIL_DELIVERY_TIMEOUT_MS }),
|
||||||
mailTemplates: () => request<ListResponse<MailTemplate>>("/api/admin/mail-templates"),
|
mailTemplates: () => request<ListResponse<MailTemplate>>("/api/admin/mail-templates"),
|
||||||
updateMailTemplate: (key: string, payload: { subject: string; bodyText: string; bodyHtml: string }) => request<MailTemplate>(`/api/admin/mail-templates/${encodeURIComponent(key)}`, { method: "POST", body: JSON.stringify(payload) }),
|
updateMailTemplate: (key: string, payload: { subject: string; bodyText: string; bodyHtml: string }) => request<MailTemplate>(`/api/admin/mail-templates/${encodeURIComponent(key)}`, { method: "POST", body: JSON.stringify(payload) }),
|
||||||
resetMailTemplate: (key: string) => request<MailTemplate>(`/api/admin/mail-templates/${encodeURIComponent(key)}/reset`, { method: "POST" }),
|
resetMailTemplate: (key: string) => request<MailTemplate>(`/api/admin/mail-templates/${encodeURIComponent(key)}/reset`, { method: "POST" }),
|
||||||
@@ -112,7 +114,7 @@ export const api = {
|
|||||||
return request<ListResponse<MailMessage>>(`/api/mail/starred?${params.toString()}`)
|
return request<ListResponse<MailMessage>>(`/api/mail/starred?${params.toString()}`)
|
||||||
},
|
},
|
||||||
message: (id: string, options: { markRead?: boolean } = {}) => request<MailMessage>(`/api/mail/messages/${id}${options.markRead === false ? "?markRead=0" : ""}`),
|
message: (id: string, options: { markRead?: boolean } = {}) => request<MailMessage>(`/api/mail/messages/${id}${options.markRead === false ? "?markRead=0" : ""}`),
|
||||||
send: (payload: SendPayload) => request<MailMessage>("/api/mail/send", { method: "POST", body: JSON.stringify(payload) }),
|
send: (payload: SendPayload) => request<MailMessage>("/api/mail/send", { method: "POST", body: JSON.stringify(payload), timeoutMs: MAIL_DELIVERY_TIMEOUT_MS }),
|
||||||
markRead: (id: string, read: boolean) => request<{ ok: boolean }>(`/api/mail/messages/${id}/mark-read`, { method: "POST", body: JSON.stringify({ read }) }),
|
markRead: (id: string, read: boolean) => request<{ ok: boolean }>(`/api/mail/messages/${id}/mark-read`, { method: "POST", body: JSON.stringify({ read }) }),
|
||||||
star: (id: string, starred: boolean) => request<{ ok: boolean }>(`/api/mail/messages/${id}/star`, { method: "POST", body: JSON.stringify({ starred }) }),
|
star: (id: string, starred: boolean) => request<{ ok: boolean }>(`/api/mail/messages/${id}/star`, { method: "POST", body: JSON.stringify({ starred }) }),
|
||||||
addLabel: (id: string, payload: { name: string; color?: string }) => request<{ labels: MailLabel[] }>(`/api/mail/messages/${id}/labels`, { method: "POST", body: JSON.stringify(payload) }),
|
addLabel: (id: string, payload: { name: string; color?: string }) => request<{ labels: MailLabel[] }>(`/api/mail/messages/${id}/labels`, { method: "POST", body: JSON.stringify(payload) }),
|
||||||
|
|||||||
+17
-17
@@ -4,26 +4,26 @@ submission inet n - n - - smtpd
|
|||||||
-o smtpd_tls_security_level=may
|
-o smtpd_tls_security_level=may
|
||||||
-o smtpd_sasl_auth_enable=yes
|
-o smtpd_sasl_auth_enable=yes
|
||||||
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
||||||
pickup unix n - y 60 1 pickup
|
pickup unix n - n 60 1 pickup
|
||||||
cleanup unix n - y - 0 cleanup
|
cleanup unix n - n - 0 cleanup
|
||||||
qmgr unix n - n 300 1 qmgr
|
qmgr unix n - n 300 1 qmgr
|
||||||
tlsmgr unix - - y 1000? 1 tlsmgr
|
tlsmgr unix - - n 1000? 1 tlsmgr
|
||||||
rewrite unix - - y - - trivial-rewrite
|
rewrite unix - - n - - trivial-rewrite
|
||||||
bounce unix - - y - 0 bounce
|
bounce unix - - n - 0 bounce
|
||||||
defer unix - - y - 0 bounce
|
defer unix - - n - 0 bounce
|
||||||
trace unix - - y - 0 bounce
|
trace unix - - n - 0 bounce
|
||||||
verify unix - - y - 1 verify
|
verify unix - - n - 1 verify
|
||||||
flush unix n - y 1000? 0 flush
|
flush unix n - y 1000? 0 flush
|
||||||
proxymap unix - - n - - proxymap
|
proxymap unix - - n - - proxymap
|
||||||
proxywrite unix - - n - 1 proxymap
|
proxywrite unix - - n - 1 proxymap
|
||||||
smtp unix - - y - - smtp
|
smtp unix - - n - - smtp
|
||||||
relay unix - - y - - smtp
|
relay unix - - n - - smtp
|
||||||
showq unix n - y - - showq
|
showq unix n - n - - showq
|
||||||
error unix - - y - - error
|
error unix - - n - - error
|
||||||
retry unix - - y - - error
|
retry unix - - n - - error
|
||||||
discard unix - - y - - discard
|
discard unix - - n - - discard
|
||||||
local unix - n n - - local
|
local unix - n n - - local
|
||||||
virtual unix - n n - - virtual
|
virtual unix - n n - - virtual
|
||||||
lmtp unix - - y - - lmtp
|
lmtp unix - - n - - lmtp
|
||||||
anvil unix - - y - 1 anvil
|
anvil unix - - n - 1 anvil
|
||||||
scache unix - - y - 1 scache
|
scache unix - - n - 1 scache
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
bind_socket = "0.0.0.0:11332";
|
bind_socket = "0.0.0.0:11332";
|
||||||
milter = yes;
|
milter = yes;
|
||||||
timeout = 120s;
|
timeout = 10s;
|
||||||
|
|
||||||
upstream "local" {
|
upstream "local" {
|
||||||
default = yes;
|
default = yes;
|
||||||
|
|||||||
Reference in New Issue
Block a user