feat(通知): 加 Bark 格式 + 格式切换时界面提示全部跟着变
## 用户报的第二件事:格式改了,提示没跟着改
弹窗里的 URL 示例/说明、密钥标签、限流说明都写死成企业微信了——选「通用 JSON」时
占位还是 `qyapi.weixin.qq.com`,限流还写着「企微硬限 20」。现在这些文案挂在**适配器**上
(`url_hint/url_help/secret_label/secret_help/limit_help`),由接口随格式下发,切格式即时更新;
限流值在用户没手动改过时也跟着格式的推荐值走(企微 20 / 通用 JSON 60 / Bark 60)。
## 新格式:Bark(iOS 推送)
- `POST {url}`,body `{title, body, markdown, group, level[, device_key]}`
—— `markdown` 传富文本、`body` 传纯文本兜底(老版本 App 不认 markdown 字段时也能看清)
- URL 两种填法都支持:直接粘 Bark 复制的那串(`https://api.day.app/<key>`,key 在路径里),
或填 `https://api.day.app/push` + 把 key 填到「设备 Key」(作为 device_key 发送)
- **成功判定按格式**:Bark 是 `code==200`(企业微信是 `errcode==0`)→ 新增
`BaseAdapter.ok_codes`,`_post_once` 用它判定。少了这一步 Bark 的每次成功都会被误判成失败
- 正文按 2048 字节截断(走 APNs,体量有限)
## 顺带
- 通用 JSON 的 `secret` 现在会作为 `X-Webhook-Secret` 请求头发出(原先填了没用)
- `_formats()` 改为序列化适配器元信息,新增格式只改一处
## 验证
- 格式切换 14 项断言全绿:三种格式的 URL 示例/密钥标签/密钥说明/限流说明/模板区/限流默认值
全部跟着切换;下拉里 Bark 出现在已实现区
- Bark 端到端 7 项:`code=200` 判成功、`code=400` 判失败(含重试)、请求体带
device_key/title/body/markdown/group、URL 里的 key 在接口回显里被打码
- 文档:NOTIFY.md §5 的格式表补 Bark 列(URL 怎么填 / 成功码 / 约束),并说明
"提示文案挂在适配器上,别写死在页面里"
This commit is contained in:
+52
-11
@@ -7,6 +7,8 @@ let _notifyFormats = [];
|
||||
let _notifySettings = {};
|
||||
let _notifyEditingId = null; // null=新建
|
||||
let _notifyPatterns = []; // 编辑中的通配订阅(如 task.*)
|
||||
let _notifyPrevFormat = ''; // 上一个选中格式(切换时判断限流值要不要跟着走)
|
||||
let _notifyRateTouched = false; // 用户是否手动改过限流值(没改过就跟随格式的推荐值)
|
||||
|
||||
// ================== 加载与渲染 ==================
|
||||
function loadNotifyPanel(){
|
||||
@@ -119,20 +121,25 @@ function openNotifyModal(id){
|
||||
+ '<select id="nf-format" class="form-control" onchange="_notifyFormatChanged()">' + fmtOpts + '</select></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="form-group"><label>Webhook URL</label>'
|
||||
+ '<input id="nf-url" class="form-control" placeholder="' + (h.url ? esc(h.url) : 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=…') + '" '
|
||||
+ 'value="' + (id ? '' : '') + '">'
|
||||
+ '<div class="help">' + (h.url ? '已配置:' + esc(h.url) + '(留空=不修改)' : '企业微信:群机器人 → 复制 Webhook 地址') + '</div></div>'
|
||||
// 占位与说明都由**当前格式**决定(见 _notifyFormatChanged),切换格式会跟着变
|
||||
+ '<input id="nf-url" class="form-control" value="">'
|
||||
+ '<div class="help" id="nf-url-help"></div></div>'
|
||||
+ '<div class="form-row">'
|
||||
+ '<div class="form-group"><label>签名密钥(可选)</label>'
|
||||
+ '<input id="nf-secret" type="password" class="form-control" placeholder="' + (h.secret_set ? '已配置,留空不修改' : '钉钉/飞书加签用,可留空') + '"></div>'
|
||||
+ '<div class="form-group"><label id="nf-secret-label">签名密钥(可选)</label>'
|
||||
+ '<input id="nf-secret" type="password" class="form-control" placeholder="'
|
||||
+ (h.secret_set ? '已配置,留空不修改' : '留空即可') + '">'
|
||||
+ '<div class="help" id="nf-secret-help"></div></div>'
|
||||
+ '<div class="form-group" style="max-width:150px"><label>聚合窗口(秒)</label>'
|
||||
+ '<input id="nf-agg" type="number" class="form-control" min="0" max="600" value="'
|
||||
+ (h.agg_window != null ? h.agg_window : (_notifySettings.default_agg_window || 30)) + '">'
|
||||
+ '<div class="help">0=不聚合</div></div>'
|
||||
+ '<div class="form-group" style="max-width:150px"><label>限流(条/分)</label>'
|
||||
+ '<input id="nf-rate" type="number" class="form-control" min="0" max="60" value="'
|
||||
+ (h.rate_limit_per_min != null ? h.rate_limit_per_min : (_notifySettings.default_rate_limit || 18)) + '">'
|
||||
+ '<div class="help">企微硬限 20</div></div>'
|
||||
+ '<input id="nf-rate" type="number" class="form-control" min="0" max="60" '
|
||||
+ 'oninput="_notifyRateTouched=true" value="'
|
||||
+ (h.rate_limit_per_min != null ? h.rate_limit_per_min
|
||||
: ((_notifyFormats.find(x => x.name === (h.format || 'wecom')) || {}).limit_default
|
||||
|| _notifySettings.default_rate_limit || 18)) + '">'
|
||||
+ '<div class="help" id="nf-limit-help"></div></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="form-group"><label>订阅事件(不勾就不推)</label>'
|
||||
+ '<div class="help" style="margin-bottom:6px">按类别分的完整事件目录;'
|
||||
@@ -161,15 +168,49 @@ function openNotifyModal(id){
|
||||
box.querySelector('#nf-body').value = h.body_template || '';
|
||||
renderEventTree(checked);
|
||||
renderNotifyPatterns();
|
||||
_notifyPrevFormat = (h.format || 'wecom');
|
||||
_notifyRateTouched = (h.rate_limit_per_min != null); // 编辑已有配置时视为"已定"
|
||||
_notifyFormatChanged();
|
||||
document.getElementById('notify-modal-overlay').style.display = 'flex';
|
||||
}
|
||||
|
||||
function _notifyFormatChanged(){
|
||||
const f = (document.getElementById('nf-format') || {}).value;
|
||||
// 只有「通用 JSON」才需要自定义请求体模板
|
||||
const name = (document.getElementById('nf-format') || {}).value;
|
||||
const meta = _notifyFormats.find(x => x.name === name) || {};
|
||||
const h = _notifyEditingId
|
||||
? (_notifyHooks.find(x => x.id === _notifyEditingId) || {}) : {};
|
||||
|
||||
// URL:编辑时占位显示现有(打码)地址,新建时显示该格式的示例
|
||||
const url = document.getElementById('nf-url');
|
||||
if(url){
|
||||
url.placeholder = (h.url && h.url !== undefined) ? h.url : (meta.url_hint || 'https://…');
|
||||
}
|
||||
const urlHelp = document.getElementById('nf-url-help');
|
||||
if(urlHelp){
|
||||
urlHelp.innerHTML = (h.url ? ('已配置:' + esc(h.url) + '(留空=不修改)<br>') : '')
|
||||
+ esc(meta.url_help || '');
|
||||
}
|
||||
|
||||
// 密钥:不同格式叫法/用途不同(企业微信不需要、Bark 是设备 Key、通用 JSON 是自定义头)
|
||||
const sl = document.getElementById('nf-secret-label');
|
||||
if(sl) sl.textContent = (h.secret_set ? '🔑 ' : '') + (meta.secret_label || '密钥(可选)');
|
||||
const sh = document.getElementById('nf-secret-help');
|
||||
if(sh) sh.innerHTML = esc(meta.secret_help || '')
|
||||
+ (h.secret_set ? '<br>已配置过:留空=不修改,想清除请点上面的 🔑 后填写新值' : '');
|
||||
|
||||
// 限流:上限因格式而异(企微 20/分、Bark 无硬限)
|
||||
const lh = document.getElementById('nf-limit-help');
|
||||
if(lh) lh.textContent = meta.limit_help || '';
|
||||
// 用户没手动改过限流值时,跟着格式的推荐值走(企微 20 / 通用 JSON 60 / Bark 60)
|
||||
const rate = document.getElementById('nf-rate');
|
||||
if(rate && !_notifyRateTouched && meta.limit_default){
|
||||
rate.value = meta.limit_default;
|
||||
}
|
||||
// 请求体模板:只有需要模板的格式(通用 JSON)才显示
|
||||
const bodyWrap = document.getElementById('nf-body-wrap');
|
||||
if(bodyWrap) bodyWrap.style.display = (f === 'json') ? 'block' : 'none';
|
||||
if(bodyWrap) bodyWrap.style.display = meta.needs_template ? 'block' : 'none';
|
||||
|
||||
_notifyPrevFormat = name;
|
||||
}
|
||||
|
||||
// Esc 关闭弹窗(点空白处关闭在 overlay 的 onclick 上)
|
||||
|
||||
Reference in New Issue
Block a user