feat(mail): 增强邮件认证与审计能力
Docker Release / Check web and api (push) Waiting to run
Docker Release / Resolve release tag (push) Blocked by required conditions
Docker Release / Build and publish all-in-one (push) Blocked by required conditions
Docker Release / Build and publish api (push) Blocked by required conditions
Docker Release / Build and publish web (push) Blocked by required conditions
Docker Release / Build and publish dovecot (push) Blocked by required conditions
Docker Release / Build and publish postfix (push) Blocked by required conditions
Docker Release / Build and publish rspamd (push) Blocked by required conditions
Docker Release / Create GitHub release (push) Blocked by required conditions
Docker Release / Check web and api (push) Waiting to run
Docker Release / Resolve release tag (push) Blocked by required conditions
Docker Release / Build and publish all-in-one (push) Blocked by required conditions
Docker Release / Build and publish api (push) Blocked by required conditions
Docker Release / Build and publish web (push) Blocked by required conditions
Docker Release / Build and publish dovecot (push) Blocked by required conditions
Docker Release / Build and publish postfix (push) Blocked by required conditions
Docker Release / Build and publish rspamd (push) Blocked by required conditions
Docker Release / Create GitHub release (push) Blocked by required conditions
- 新增邮件认证结果解析、存储与前端展示,支持查看 SPF、DKIM、DMARC 及原始头信息。 - 扩展邮件规则条件,支持抄送、附件、大小、日期及嵌套条件组合。 - 增加邮箱容量统计与发送前配额校验,超限时返回明确错误。 - 新增管理端发送审计页面与接口,支持按邮箱、事件、Message-ID 和时间筛选。
This commit is contained in:
@@ -1963,6 +1963,7 @@ function MessageMetaPanel({ message, availableLabels, onAddLabel, onRemoveLabel,
|
||||
<MessageMetaRow label="接收时间">
|
||||
<span>{formatDateTime(message.receivedAt)}</span>
|
||||
</MessageMetaRow>
|
||||
<AuthenticationResultRow message={message} />
|
||||
{availableLabels && onAddLabel && onRemoveLabel && (
|
||||
<MessageMetaRow label="标签">
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
@@ -2024,6 +2025,41 @@ function MessageMetaPanel({ message, availableLabels, onAddLabel, onRemoveLabel,
|
||||
)
|
||||
}
|
||||
|
||||
function AuthenticationResultRow({ message }: { message: MailMessage }) {
|
||||
const auth = message.authentication || { authenticationResults: "", receivedSpf: "", spf: "unknown", dkim: "unknown", dmarc: "unknown" }
|
||||
const title = [auth.authenticationResults, auth.receivedSpf].filter(Boolean).join("\n\n")
|
||||
return (
|
||||
<MessageMetaRow label="Auth">
|
||||
<div className="flex flex-wrap gap-1.5" title={title || undefined}>
|
||||
<AuthStatusBadge label="SPF" value={auth.spf} />
|
||||
<AuthStatusBadge label="DKIM" value={auth.dkim} />
|
||||
<AuthStatusBadge label="DMARC" value={auth.dmarc} />
|
||||
</div>
|
||||
</MessageMetaRow>
|
||||
)
|
||||
}
|
||||
|
||||
function AuthStatusBadge({ label, value }: { label: string; value?: string }) {
|
||||
const status = normalizeAuthStatus(value)
|
||||
return (
|
||||
<Badge variant="outline" className={cn("rounded-md font-mono text-[11px] font-normal", authStatusClassName(status))}>
|
||||
{label}:{status}
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
|
||||
function normalizeAuthStatus(value?: string) {
|
||||
const status = (value || "").trim().toLowerCase()
|
||||
if (["pass", "fail", "softfail", "neutral", "temperror", "permerror", "none"].includes(status)) return status
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
function authStatusClassName(status: string) {
|
||||
if (status === "pass") return "border-emerald-300 bg-emerald-50 text-emerald-700"
|
||||
if (["fail", "softfail", "permerror"].includes(status)) return "border-red-300 bg-red-50 text-red-700"
|
||||
return "border-slate-300 bg-slate-50 text-slate-600"
|
||||
}
|
||||
|
||||
function MessageMetaRow({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="grid gap-1 sm:grid-cols-[5rem_minmax(0,1fr)]">
|
||||
|
||||
Reference in New Issue
Block a user