From 91ab64a0682da9b4e0b6d734060637e63edde6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=B4=80=E1=B4=8D=E1=B4=9B=E1=B4=8F=E1=B4=80=E1=B4=87?= =?UTF-8?q?=CA=80?= Date: Tue, 31 Mar 2026 01:49:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=20webhook=20=E8=AF=B7=E6=B1=82=E7=9A=84=20headers?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0=E8=AF=B4=E6=98=8E=E5=86=85=E5=AE=B9?= =?UTF-8?q?=20(#693)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/bili_sync/src/notifier/mod.rs | 25 +++++-- web/src/lib/types.ts | 1 + web/src/routes/settings/NotifierDialog.svelte | 72 ++++++++++++++++--- 3 files changed, 81 insertions(+), 17 deletions(-) diff --git a/crates/bili_sync/src/notifier/mod.rs b/crates/bili_sync/src/notifier/mod.rs index da2759f..45b15ee 100644 --- a/crates/bili_sync/src/notifier/mod.rs +++ b/crates/bili_sync/src/notifier/mod.rs @@ -1,6 +1,8 @@ mod info; mod message; +use std::collections::HashMap; + use anyhow::Result; use futures::future; pub use info::DownloadNotifyInfo; @@ -20,6 +22,8 @@ pub enum Notifier { Webhook { url: String, template: Option, + #[serde(default)] + headers: Option>, #[serde(skip)] // 一个内部辅助字段,用于决定是否强制渲染当前模板,在测试时使用 ignore_cache: Option<()>, @@ -74,6 +78,7 @@ impl Notifier { Notifier::Webhook { url, template, + headers, ignore_cache, } => { let key = webhook_template_key(url); @@ -82,12 +87,20 @@ impl Notifier { Some(_) => handlebar.render_template(webhook_template_content(template), &message)?, None => handlebar.render(&key, &message)?, }; - client - .post(url) - .header(header::CONTENT_TYPE, "application/json") - .body(payload) - .send() - .await?; + let mut headers_map = header::HeaderMap::new(); + headers_map.insert(header::CONTENT_TYPE, "application/json".try_into()?); + + if let Some(custom_headers) = headers { + for (key, value) in custom_headers { + if let (Ok(key), Ok(value)) = + (header::HeaderName::try_from(key), header::HeaderValue::try_from(value)) + { + headers_map.insert(key, value); + } + } + } + + client.post(url).headers(headers_map).body(payload).send().await?; } } Ok(()) diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts index d99e64e..188a89f 100644 --- a/web/src/lib/types.ts +++ b/web/src/lib/types.ts @@ -311,6 +311,7 @@ export interface WebhookNotifier { type: 'webhook'; url: string; template?: string | null; + headers?: Record | null; } export type Notifier = TelegramNotifier | WebhookNotifier; diff --git a/web/src/routes/settings/NotifierDialog.svelte b/web/src/routes/settings/NotifierDialog.svelte index 22ea5d9..a0cfb66 100644 --- a/web/src/routes/settings/NotifierDialog.svelte +++ b/web/src/routes/settings/NotifierDialog.svelte @@ -5,8 +5,6 @@ import { toast } from 'svelte-sonner'; import type { Notifier } from '$lib/types'; - const jsonExample = '{"text": "您的消息内容"}'; - export let notifier: Notifier | null = null; export let onSave: (notifier: Notifier) => void; export let onCancel: () => void; @@ -16,6 +14,7 @@ let chatId = ''; let webhookUrl = ''; let webhookTemplate = ''; + let webhookHeaders: { key: string; value: string }[] = []; // 初始化表单 $: { @@ -28,6 +27,11 @@ type = 'webhook'; webhookUrl = notifier.url; webhookTemplate = notifier.template || ''; + if (notifier.headers) { + webhookHeaders = Object.entries(notifier.headers).map(([key, value]) => ({ key, value })); + } else { + webhookHeaders = []; + } } } else { type = 'telegram'; @@ -35,11 +39,11 @@ chatId = ''; webhookUrl = ''; webhookTemplate = ''; + webhookHeaders = []; } } function handleSave() { - // 验证表单 if (type === 'telegram') { if (!botToken.trim()) { toast.error('请输入 Bot Token'); @@ -62,7 +66,6 @@ return; } - // 简单的 URL 验证 try { new URL(webhookUrl.trim()); } catch { @@ -70,10 +73,20 @@ return; } + const headers: Record = {}; + for (const { key, value } of webhookHeaders) { + const trimmedKey = key.trim(); + const trimmedValue = value.trim(); + if (trimmedKey && trimmedValue) { + headers[trimmedKey] = trimmedValue; + } + } + const newNotifier: Notifier = { type: 'webhook', url: webhookUrl.trim(), - template: webhookTemplate.trim() || null + template: webhookTemplate.trim() || null, + headers: Object.keys(headers).length > 0 ? headers : null }; onSave(newNotifier); } @@ -111,11 +124,7 @@ {:else if type === 'webhook'}
- -

- 接收通知的 Webhook 地址
- 格式示例:{jsonExample} -

+
@@ -127,7 +136,48 @@ >

用于渲染 Webhook 的 Handlebars 模板。如果不填写,将使用默认模板。
- 可用变量:message(通知内容) + 可用变量:message(通知内容)、image_url(封面图片地址,无图时为 null) +

+
+ +
+
+ + +
+ {#each webhookHeaders as header, index (index)} +
+ + + +
+ {/each} +

+ 添加自定义请求头,例如:Authorization: Bearer your_token

{/if}