+
{T.deliveryEventLabel[d.event] ?? T.eventLabel[d.event] ?? d.event}
{d.error ?? (d.http_status ? `HTTP ${d.http_status}` : '')}
-
- {d.status === 'ok' ? '已送达' : d.status === 'failed' ? '失败' : d.status}
+ {/* 邮件只知道交给了邮件服务器,进没进收件箱不知道(退信不追踪):不写「已送达」 */}
+
+ {d.status === 'ok' ? (d.format === 'email' ? M.handoff : '已送达') : d.status === 'failed' ? '失败' : d.status}
@@ -138,14 +165,16 @@ function Deliveries() {
)
}
-function ChannelEditor({ row, onClose, onSaved }: {
- row: NotifyChannelRow | null; onClose: () => void; onSaved: (name: string) => void
+function ChannelEditor({ row, mail, onClose, onSaved }: {
+ row: NotifyChannelRow | null; mail: NotifyMailState; onClose: () => void; onSaved: (name: string) => void
}) {
const admin = useAtLeast('admin')
const builder = useAtLeast('builder')
const [form, setForm] = useState({
name: row?.name ?? '', scope: row?.scope ?? (admin ? 'global' : 'user'), format: row?.format ?? 'generic',
url: '', secret: '', events: row?.events ?? ['task.opened', 'action.unknown'],
+ // 全局、应用的邮件渠道:收件地址整份重填(服务端照原样给,编辑时带出来)
+ recipients: row?.format === 'email' && row.scope !== 'user' ? (row.mail_to ?? []).join('\n') : '',
})
const [busy, setBusy] = useState(false)
// 列哪些事件由服务端说(notify/service.EVENTS 的 scopes):这种范围的渠道收得到的才列,登记了还没推送的不列(勾了收不到)。
@@ -155,18 +184,28 @@ function ChannelEditor({ row, onClose, onSaved }: {
useEffect(() => { api.notify.events().then(setRegistry).catch(() => setRegistryFailed(true)) }, [])
const offered = EVENTS.filter((ev) => form.events.includes(ev) || row?.events.includes(ev)
|| registry?.some((r) => r.event === ev && r.scopes.includes(form.scope)))
+ const email = form.format === 'email'
+ // 邮件服务没启用:不列「邮件」(建了也发不出去);原来就是邮件渠道的照列,能改名、改订阅、换回 Webhook
+ const formats = [...WEBHOOK_FORMATS, ...(mail.enabled || row?.format === 'email' ? ['email' as const] : [])]
+ const personalMail = email && form.scope === 'user'
+ const recipientsChanged = form.recipients.trim() !== (row?.format === 'email' ? (row.mail_to ?? []).join('\n') : '')
+ const missingTarget = email ? !personalMail && !form.recipients.trim()
+ : !row || row.format === 'email' ? !form.url.trim() : false
const save = async () => {
setBusy(true)
try {
+ const target = email
+ ? (personalMail ? {} : recipientsChanged || row?.format !== 'email' ? { url: form.recipients.trim() } : {})
+ : form.url.trim() ? { url: form.url.trim() } : {}
if (row) {
await api.notify.updateChannel(row.id, {
name: form.name.trim(), scope: form.scope, format: form.format, events: form.events,
// 地址、密钥只回主机名:不填就是保持原值
- ...(form.url.trim() ? { url: form.url.trim() } : {}), ...(form.secret ? { secret: form.secret } : {}),
+ ...target, ...(!email && form.secret ? { secret: form.secret } : {}),
})
} else {
- await api.notify.createChannel({ name: form.name.trim(), scope: form.scope, format: form.format, url: form.url.trim(),
- secret: form.secret || null, events: form.events })
+ await api.notify.createChannel({ name: form.name.trim(), scope: form.scope, format: form.format, ...target,
+ secret: !email && form.secret ? form.secret : null, events: form.events })
}
onSaved(form.name.trim())
} catch (e) {
@@ -179,7 +218,7 @@ function ChannelEditor({ row, onClose, onSaved }: {
-
@@ -191,9 +230,9 @@ function ChannelEditor({ row, onClose, onSaved }: {
{(p) => (
-
)}
@@ -207,14 +246,32 @@ function ChannelEditor({ row, onClose, onSaved }: {
)}