Skip to content

新增 Webhook 通知渠道#1799

Open
gongfuture wants to merge 27 commits into
PBH-BTN:devfrom
gongfuture:feat/notification/webhook
Open

新增 Webhook 通知渠道#1799
gongfuture wants to merge 27 commits into
PBH-BTN:devfrom
gongfuture:feat/notification/webhook

Conversation

@gongfuture

@gongfuture gongfuture commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

增加 Webhook 用于没有需要的通知渠道情况下用户自定义

支持 Post/Get,支持json/plaintext
支持自定义消息模板,可用变量 {title} {content} {level} {date} {time} {datetime} {channelName}

截图
json post
ptn_webhook_post_json
ntfy_webhook_post_json

plaintext post
ptn_webhook_post_plaintext
ntfy_webhook_post_plaintext

get (这边懒得写参数了,所以只要一个触发)
ptn_webhook_get
ntfy_webhook_get

截图完成之后跳了一下网页排布和说明,如下
变量tip

Summary by CodeRabbit

  • 新功能
    • 新增 Webhook 推送通道:可配置目标 URL、GET/POST、内容类型、带变量的请求体模板与自定义请求头;提交时会对 URL 与(如为 JSON)变量做合规编码/转义。
  • 改进
    • 推送流程新增通知级别(AlertLevel),并随推送一并传递;Webhook 也可在模板中使用该级别(测试按默认级别触发)。
  • 界面
    • 设置界面新增 Webhook 配置表单与类型选择,并更新推送卡片样式;支持模板编辑与请求头列表管理。
  • 本地化
    • 补充 Webhook 相关中英文文案。

@gongfuture gongfuture requested review from a team as code owners April 27, 2026 14:32
@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

后端新增 Webhook 推送提供者,并将 AlertLevel 贯穿推送调用链;前端新增 Webhook 类型、配置表单、展示映射与多语言文案。

Changes

Webhook 推送契约与调用链

Layer / File(s) Summary
推送契约与调用链
src/main/java/com/ghostchu/peerbanhelper/util/push/PushProvider.java, src/main/java/com/ghostchu/peerbanhelper/util/push/PushManager.java, src/main/java/com/ghostchu/peerbanhelper/util/push/PushManagerImpl.java, src/main/java/com/ghostchu/peerbanhelper/alert/AlertManagerImpl.java, src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/PBHPushController.java, src/main/java/com/ghostchu/peerbanhelper/util/push/impl/*
推送方法统一增加 AlertLevel 参数,管理器接入 webhook,告警发布和测试接口传递级别,现有 provider 同步更新签名。
Webhook 提供者实现
src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java
新增 JSON/YAML 配置读写、默认值处理、模板渲染、GET/POST 请求构建、自定义 headers 和错误处理。

前端 Webhook 配置

Layer / File(s) Summary
类型模型、表单与展示
webui/src/api/model/push.ts, webui/src/views/settings/components/config/components/push/editPush.vue, webui/src/views/settings/components/config/components/push/forms/webhookForm.vue, webui/src/views/settings/components/config/components/push/pushCard.vue
新增 Webhook 配置类型、类型选择与动态表单,支持 URL、请求方法、内容类型、模板和 headers 配置。
多语言文案
webui/src/views/settings/components/config/locale/en-US.ts, webui/src/views/settings/components/config/locale/zh-CN.ts, webui/src/views/settings/components/config/locale/zh-TW.ts
新增 Webhook 类型、字段、校验、占位符和提示文案。

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AlertManagerImpl
  participant PushManagerImpl
  participant WebhookPushProvider
  participant HTTPUtil
  AlertManagerImpl->>PushManagerImpl: pushMessage(title, description, level)
  PushManagerImpl->>WebhookPushProvider: push(title, description, level)
  WebhookPushProvider->>HTTPUtil: 执行渲染后的 GET/POST 请求
Loading

Possibly related PRs

Suggested reviewers: Gaojianli, Ghost-chu

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了本次 PR 的核心变更:新增 Webhook 通知渠道。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added enhancement 功能增强 java Pull requests that update Java code javascript Pull requests that update Javascript code backend This is a backend related webui This is a webui related labels Apr 27, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Webhook push provider, enabling notifications to be sent to custom endpoints via GET or POST requests with configurable body templates and headers. The changes include the backend implementation for request execution and template rendering, alongside a frontend configuration interface with multi-language support. Review feedback suggests extending template rendering to the URL for dynamic GET parameters, removing redundant null checks for OkHttp response bodies, and implementing JSON escaping within templates to prevent malformed payloads when special characters are present.

Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java (1)

189-202: DateTimeFormatter 可缓存为静态常量。

每次推送都会通过 DateTimeFormatter.ofPattern(...) 重新构造三个实例,对热路径而言完全可避免。DateTimeFormatter 是线程安全的,建议提取为类级常量。

♻️ 建议
+    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
+    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@@
-        OffsetDateTime now = OffsetDateTime.now();
-        String date = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
-        String time = now.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
-        String datetime = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+        OffsetDateTime now = OffsetDateTime.now();
+        String date = now.format(DATE_FORMATTER);
+        String time = now.format(TIME_FORMATTER);
+        String datetime = now.format(DATETIME_FORMATTER);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`
around lines 189 - 202, renderTemplate currently constructs three
DateTimeFormatter instances on every call; extract them as reusable class-level
constants (e.g. private static final DateTimeFormatter DATE_FMT =
DateTimeFormatter.ofPattern("yyyy-MM-dd"), TIME_FMT =
DateTimeFormatter.ofPattern("HH:mm:ss"), DATETIME_FMT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) and replace the inline
ofPattern(...) calls in renderTemplate with these constants (class
WebhookPushProvider, method renderTemplate) to avoid repeated allocation and
leverage DateTimeFormatter's thread-safety.
webui/src/views/settings/components/config/components/push/forms/webhookForm.vue (2)

142-153: JSON.stringify 比较对键序敏感,可能导致不必要的行重建。

JSON.stringify(fromRows)JSON.stringify(next) 在键内容相同但顺序不同时仍会判定为不相等,从而触发 headerRows 重建。这会丢失用户当前的行顺序与可能正在编辑中的焦点状态。常见触发场景:外部回填时后端返回的 headers 键序与本地 rowsToHeaders 按行顺序产出的键序不一致。

可考虑改为按键集合 + 值的语义比较:

♻️ 建议改写
-watch(
-  () => model.value.headers,
-  (headers) => {
-    const fromRows = rowsToHeaders(headerRows.value)
-    const next = headers ?? {}
-    if (JSON.stringify(fromRows) === JSON.stringify(next)) {
-      return
-    }
-    headerRows.value = headersToRows(next)
-  }
-)
+const headersEqual = (a: Record<string, string>, b: Record<string, string>) => {
+  const ak = Object.keys(a)
+  const bk = Object.keys(b)
+  if (ak.length !== bk.length) return false
+  return ak.every((k) => Object.prototype.hasOwnProperty.call(b, k) && a[k] === b[k])
+}
+
+watch(
+  () => model.value.headers,
+  (headers) => {
+    const fromRows = rowsToHeaders(headerRows.value)
+    const next = headers ?? {}
+    if (headersEqual(fromRows, next)) return
+    headerRows.value = headersToRows(next)
+  }
+)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@webui/src/views/settings/components/config/components/push/forms/webhookForm.vue`
around lines 142 - 153, The current watch uses JSON.stringify to compare
fromRows and next which is order-sensitive and can trigger unnecessary
headerRows resets; instead implement an order-insensitive deep equality check
(e.g., a headersEqual(from, next) that compares key sets and value equality for
each key) and use that in the watch before deciding to reassign
headerRows.value; locate the watch block that references model.value.headers,
rowsToHeaders(headerRows.value) and headersToRows(next) and replace the
JSON.stringify comparison with this headersEqual check to avoid rebuilding rows
when only key order differs.

19-19: 在选项中使用枚举值作为标签和值。

Object.values(WebhookMethod)Object.values(WebhookContentType) 都返回字符串数组。ArcoDesign 的 a-select 组件接受字符串数组,并将字符串同时用作标签和值展示。由于 GET/POSTapplication/json/text/plain 都是通用术语,当前的用户体验是可以接受的。

如果后续需要本地化或显示更友好的描述(例如"JSON (application/json)"),建议改为使用显式的 { label, value } 数组格式,但这可以在未来的优化中处理,不是当前 PR 必需的改动。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@webui/src/views/settings/components/config/components/push/forms/webhookForm.vue`
at line 19, 当前在 a-select 中直接使用 Object.values(WebhookMethod) 和
Object.values(WebhookContentType),它们返回字符串数组并被用作标签和值,这在现有场景可接受;若将来需要本地化或更友好的展示,请把这两个枚举映射为显式的
{ label, value } 数组(例如在组件或一个 helper 中将 WebhookMethod/WebhookContentType 转为 {
label, value } 列表),并将结果传给 a-select 的 :options(绑定到 model.method
等字段),以便后续替换为更友好的文本而不改动绑定逻辑。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`:
- Around line 130-138: The current try/catch in WebhookPushProvider wraps an
IllegalStateException thrown for non-success HTTP responses with a generic
"Failed to send..." message, losing the original HTTP failure message; change
the error handling so HTTP-check exceptions are not double-wrapped — e.g., in
the method where you call
httpUtil.newBuilder().build().newCall(request).execute() keep the
response.isSuccessful() check as-is but replace the broad catch (Exception e)
with a narrower catch (IOException e) (or explicitly rethrow
IllegalStateException: if (e instanceof IllegalStateException) throw e;), so
network IO errors are still handled while the original IllegalStateException
containing the HTTP body/message bubbles up unmodified from WebhookPushProvider.
- Around line 189-202: The renderTemplate method currently inserts title/content
directly and can break JSON payloads; update renderTemplate (or its caller) to
accept or read the contentType and, when contentType equals "application/json",
JSON-escape values used in replacements (title, content, channelName returned by
name, and any other inserted fields), then perform the .replace calls with the
escaped strings (keep extractLevel(title) but pass an escaped title if it's also
placed into JSON); implement escaping that handles backslashes, double quotes,
control chars (newline, \r, tabs) and Unicode as needed so the produced JSON
remains valid.
- Around line 78-93: loadFromJson currently assumes
JsonUtil.getGson().fromJson(json, Config.class) returns a non-null Config; add a
null-check right after deserialization in loadFromJson and throw a clear
IllegalArgumentException (or custom config exception) stating the provider name
and that the config JSON is invalid so callers like
PushManagerImpl#createPushProvider receive a precise error instead of an NPE;
reference the Config class and return path to WebhookPushProvider only after the
config is validated and defaults (method, contentType, bodyTemplate, headers)
are applied.
- Around line 159-170: The Content-Type format isn't validated so
MediaType.parse(contentType) can return null and an invalid header gets sent;
update normalizeContentType to validate the incoming contentType by calling
MediaType.parse(contentType) and return a normalized valid string (or
null/empty) when parse fails, or alternatively modify createRequestBody to
handle a null MediaType: call MediaType.parse(contentType) there, and if it
returns null use a safe default MediaType (e.g., application/json or
application/octet-stream) and avoid setting an invalid header in
applyContentType; reference normalizeContentType, createRequestBody,
applyContentType, RequestBody.create and MediaType.parse when making the change.

In `@webui/src/views/settings/components/config/locale/en-US.ts`:
- Around line 171-172: The placeholder value for the localization key
'page.settings.tab.config.push.form.webhook.body_template.placeholder' uses a
CJK delimiter "、"; replace it with ASCII commas and spaces between variables
(e.g. "{l}title{r}, {l}content{r}, {l}level{r}, {l}date{r}, {l}time{r},
{l}datetime{r}, {l}channelName{r}") so the English UI uses proper comma+space
separation.

---

Nitpick comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`:
- Around line 189-202: renderTemplate currently constructs three
DateTimeFormatter instances on every call; extract them as reusable class-level
constants (e.g. private static final DateTimeFormatter DATE_FMT =
DateTimeFormatter.ofPattern("yyyy-MM-dd"), TIME_FMT =
DateTimeFormatter.ofPattern("HH:mm:ss"), DATETIME_FMT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) and replace the inline
ofPattern(...) calls in renderTemplate with these constants (class
WebhookPushProvider, method renderTemplate) to avoid repeated allocation and
leverage DateTimeFormatter's thread-safety.

In
`@webui/src/views/settings/components/config/components/push/forms/webhookForm.vue`:
- Around line 142-153: The current watch uses JSON.stringify to compare fromRows
and next which is order-sensitive and can trigger unnecessary headerRows resets;
instead implement an order-insensitive deep equality check (e.g., a
headersEqual(from, next) that compares key sets and value equality for each key)
and use that in the watch before deciding to reassign headerRows.value; locate
the watch block that references model.value.headers,
rowsToHeaders(headerRows.value) and headersToRows(next) and replace the
JSON.stringify comparison with this headersEqual check to avoid rebuilding rows
when only key order differs.
- Line 19: 当前在 a-select 中直接使用 Object.values(WebhookMethod) 和
Object.values(WebhookContentType),它们返回字符串数组并被用作标签和值,这在现有场景可接受;若将来需要本地化或更友好的展示,请把这两个枚举映射为显式的
{ label, value } 数组(例如在组件或一个 helper 中将 WebhookMethod/WebhookContentType 转为 {
label, value } 列表),并将结果传给 a-select 的 :options(绑定到 model.method
等字段),以便后续替换为更友好的文本而不改动绑定逻辑。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 41d2e1eb-5fd8-42c7-abef-e092336f8281

📥 Commits

Reviewing files that changed from the base of the PR and between 1cab3ac and 824359c.

📒 Files selected for processing (9)
  • src/main/java/com/ghostchu/peerbanhelper/util/push/PushManagerImpl.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java
  • webui/src/api/model/push.ts
  • webui/src/views/settings/components/config/components/push/editPush.vue
  • webui/src/views/settings/components/config/components/push/forms/webhookForm.vue
  • webui/src/views/settings/components/config/components/push/pushCard.vue
  • webui/src/views/settings/components/config/locale/en-US.ts
  • webui/src/views/settings/components/config/locale/zh-CN.ts
  • webui/src/views/settings/components/config/locale/zh-TW.ts

Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread webui/src/views/settings/components/config/locale/en-US.ts Outdated

@Ghost-chu Ghost-chu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

您好👋,感谢提交拉取请求。总的来说后端部分的代码质量相当不错,我已完成后端部分的代码审阅,仍有几个小问题需要更改,需要更改的部分已在 Review 中标出,还麻烦您查看。

要合并到主线中,前端部分仍需前端团队审阅。相关部分的更改将由 @Gaojianli 老师进行审阅。

此外,如果您愿意也可以将自己的 GitHub 用户名添加到 credit.txt 的 Contributors 部分中。再次感谢您的无私贡献 ;)

Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java (1)

185-194: Content-Type 顺序:自定义 header 会覆盖默认 Content-Type,请确认是预期行为。

第 134 行先调用 applyContentType,再调用 applyCustomHeaders;OkHttp 的 Request.Builder.header(name, value) 是覆盖语义,因此用户在 headers 中显式设置 Content-Type 会覆盖根据 RequestBody 推断的值(包括大小写归一化后的值,OkHttp 头匹配大小写不敏感)。一般这是合理的“用户优先”行为,但如果意图是“用户的自定义头不允许覆盖 body 实际的 Content-Type”,则需要在 applyCustomHeaders 中过滤掉 Content-Type。建议在文档/UI 上说明,或在代码中按需调整。

OkHttp Request.Builder.header vs addHeader case-insensitive override behavior
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`
around lines 185 - 194, The current flow calls applyContentType(...) before
applyCustomHeaders(...), so custom headers can override the Content-Type
inferred from the RequestBody via Request.Builder.header(...) — if you want to
prevent user headers from replacing the body-derived Content-Type, update
applyCustomHeaders to ignore any header whose name equals "Content-Type"
(case-insensitive, use equalsIgnoreCase) and leave DEFAULT_CONTENT_TYPE and
mediaType logic in applyContentType unchanged; alternatively, if you prefer
“user wins”, document this behavior or move applyCustomHeaders to run after
applyContentType — pick one approach and implement the corresponding change
inside applyCustomHeaders or by swapping the call order that references
applyContentType and applyCustomHeaders.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`:
- Around line 208-211: 删除未使用的 OffsetDateTime now 声明并将三次调用的
System.currentTimeMillis() 缓存为一个局部 long 变量(例如 millis),然后用该 millis 依次调用
TimeUtil.formatDateOnly, TimeUtil.formatTimeOnly, TimeUtil.formatDateTime 来初始化
date、time 和 datetime,确保在方法/块中不再引用 OffsetDateTime 并避免跨秒导致的不一致。
- Around line 130-131: The URL template replacements in WebhookPushProvider are
not URL-encoding variables (renderTemplate(config.getUrl(), ... , null)), which
breaks GET/query-string URLs; fix by adding a URL-encoding template renderer
(e.g., renderUrlTemplate or extend renderTemplate with a urlEncode mode) and use
URLEncoder.encode(value, StandardCharsets.UTF_8) for
{title},{content},{level},{date},{time},{datetime},{channelName} when building
renderedUrl (or whenever contentType==null/when method is GET), then replace the
current call in WebhookPushProvider to call that URL-encoding renderer so the
final Request.Builder.url(renderedUrl) receives a valid encoded URL.

---

Nitpick comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`:
- Around line 185-194: The current flow calls applyContentType(...) before
applyCustomHeaders(...), so custom headers can override the Content-Type
inferred from the RequestBody via Request.Builder.header(...) — if you want to
prevent user headers from replacing the body-derived Content-Type, update
applyCustomHeaders to ignore any header whose name equals "Content-Type"
(case-insensitive, use equalsIgnoreCase) and leave DEFAULT_CONTENT_TYPE and
mediaType logic in applyContentType unchanged; alternatively, if you prefer
“user wins”, document this behavior or move applyCustomHeaders to run after
applyContentType — pick one approach and implement the corresponding change
inside applyCustomHeaders or by swapping the call order that references
applyContentType and applyCustomHeaders.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d7990e4b-b823-4da0-b7e9-6bdbc2b1aaaf

📥 Commits

Reviewing files that changed from the base of the PR and between 824359c and 85a1bc8.

📒 Files selected for processing (3)
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java
  • src/main/resources/assets/credit.txt
  • webui/src/views/settings/components/config/locale/en-US.ts
✅ Files skipped from review due to trivial changes (2)
  • src/main/resources/assets/credit.txt
  • webui/src/views/settings/components/config/locale/en-US.ts

Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java (1)

165-180: createRequestBody 中存在不可达分支。

normalizeMethod(Lines 146–155)已将 method 限制为 "GET""POST",因此 Line 166 已处理 GET,Line 174 之后 method 必为 "POST",Line 177 的 return null 永远不会执行。可以简化逻辑,避免后续维护误读。

♻️ 建议简化
     private RequestBody createRequestBody(String method, String contentType, String bodyContent) {
         if ("GET".equals(method)) {
             return null;
         }
         MediaType mediaType = MediaType.parse(contentType);
         if (mediaType == null) {
             mediaType = MediaType.parse(DEFAULT_CONTENT_TYPE);
         }
-        if (bodyContent.isEmpty()) {
-            if ("POST".equals(method)) {
-                return RequestBody.create("", mediaType);
-            }
-            return null;
-        }
         return RequestBody.create(bodyContent, mediaType);
     }

RequestBody.create("", mediaType)RequestBody.create(bodyContent, mediaType)bodyContent 为空字符串时等价,无需额外分支。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`
around lines 165 - 180, createRequestBody contains an unreachable branch because
normalizeMethod already restricts method to "GET" or "POST"; remove the
redundant empty-body conditional and the unreachable return null (the branch
after checking POST) and simplify to: if method is "GET" return null, resolve
mediaType with MediaType.parse(contentType) fallback to DEFAULT_CONTENT_TYPE,
then always return RequestBody.create(bodyContent, mediaType) for POST
(RequestBody.create("", mediaType) is equivalent when bodyContent is empty).
Update the method createRequestBody accordingly and keep references to
DEFAULT_CONTENT_TYPE and normalizeMethod intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`:
- Around line 226-237: The method renderUrlTemplate currently references a
non-existent variable `template` and uses URLEncoder/StandardCharsets without
imports; change the returned expression to operate on the `urlTemplate`
parameter (i.e., use urlTemplate.replace(...)), add imports for
java.net.URLEncoder and java.nio.charset.StandardCharsets, and update the
encoder UnaryOperator in renderUrlTemplate to convert null->"" then
URLEncoder.encode(..., StandardCharsets.UTF_8) and replace "+" with "%20" to
avoid space->'+' issues; keep the existing uses of extractLevel(title),
TimeUtil.formatDateOnly/formatTimeOnly/formatDateTime(now) and name.

---

Nitpick comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java`:
- Around line 165-180: createRequestBody contains an unreachable branch because
normalizeMethod already restricts method to "GET" or "POST"; remove the
redundant empty-body conditional and the unreachable return null (the branch
after checking POST) and simplify to: if method is "GET" return null, resolve
mediaType with MediaType.parse(contentType) fallback to DEFAULT_CONTENT_TYPE,
then always return RequestBody.create(bodyContent, mediaType) for POST
(RequestBody.create("", mediaType) is equivalent when bodyContent is empty).
Update the method createRequestBody accordingly and keep references to
DEFAULT_CONTENT_TYPE and normalizeMethod intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9652fefc-4405-4ce8-9d33-bc31b79d798b

📥 Commits

Reviewing files that changed from the base of the PR and between 85a1bc8 and f99b105.

📒 Files selected for processing (1)
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java

Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java Outdated
gongfuture added 3 commits May 9, 2026 21:44
自定义请求头添加按钮修改
提示tooltip化
textarea改monaco editor
@gongfuture gongfuture requested a review from Gaojianli May 12, 2026 13:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@webui/src/views/settings/components/config/components/push/forms/webhookForm.vue`:
- Around line 148-163: headerRows is only synced one-way into model.headers via
the existing watch, so when the parent replaces model (e.g., switching channels)
headerRows won't update and subsequent edits will overwrite the new model with
old headers; add a second watcher that observes model.value.headers (or
model.value) and rebuilds headerRows from Object.entries(model.value.headers ??
{}) mapping to { key, value } (similar to the initial ref creation) to keep the
two-way sync in sync; reference headerRows, model, and the existing watch
callback to implement the new watcher and ensure it does not create an infinite
loop (simply replace headerRows.value with the mapped array when model.headers
changes).
- Line 65: The v-for in the template iterates headerRows with :key="index",
which causes DOM reuse issues when rows are added/removed; change the key to a
stable unique id (e.g., :key="header.id") and ensure each header object in
headerRows gets a persistent id when created/added—update the row-creation logic
(e.g., addHeaderRow / any method that pushes to headerRows) to assign a UUID or
incrementing id, and keep removeHeaderRow unchanged so the key remains stable
across reorders/deletes; adjust any places that construct headerRows items to
include the id property.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 65558f27-ffbb-4747-afe1-c9a3059269d9

📥 Commits

Reviewing files that changed from the base of the PR and between bdeaff2 and 1d86506.

📒 Files selected for processing (6)
  • webui/src/api/model/push.ts
  • webui/src/views/settings/components/config/components/push/forms/webhookForm.vue
  • webui/src/views/settings/components/config/components/push/pushCard.vue
  • webui/src/views/settings/components/config/locale/en-US.ts
  • webui/src/views/settings/components/config/locale/zh-CN.ts
  • webui/src/views/settings/components/config/locale/zh-TW.ts
✅ Files skipped from review due to trivial changes (3)
  • webui/src/views/settings/components/config/locale/zh-TW.ts
  • webui/src/views/settings/components/config/locale/zh-CN.ts
  • webui/src/views/settings/components/config/locale/en-US.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • webui/src/api/model/push.ts
  • webui/src/views/settings/components/config/components/push/pushCard.vue


<a-form-item :label="t('page.settings.tab.config.push.form.webhook.headers')">
<a-space direction="vertical" style="width: 100%">
<div v-for="(header, index) in headerRows" :key="index" class="header-row">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

v-for 使用索引作为 key 会导致表单行错位复用

这里是可增删行的输入表单,Line 65:key="index" 容易在删除中间行后复用错误 DOM,出现输入值“串行”问题。建议给每行一个稳定 id 作为 key。

建议修改
-      <div v-for="(header, index) in headerRows" :key="index" class="header-row">
+      <div v-for="(header, index) in headerRows" :key="header.id" class="header-row">
-const headerRows = ref<{ key: string; value: string }[]>(
-  Object.entries(model.value.headers ?? {}).map(([key, value]) => ({ key, value }))
+const headerRows = ref<{ id: string; key: string; value: string }[]>(
+  Object.entries(model.value.headers ?? {}).map(([key, value]) => ({
+    id: crypto.randomUUID(),
+    key,
+    value
+  }))
 )
@@
 const addHeader = () => {
-  headerRows.value.push({ key: '', value: '' })
+  headerRows.value.push({ id: crypto.randomUUID(), key: '', value: '' })
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@webui/src/views/settings/components/config/components/push/forms/webhookForm.vue`
at line 65, The v-for in the template iterates headerRows with :key="index",
which causes DOM reuse issues when rows are added/removed; change the key to a
stable unique id (e.g., :key="header.id") and ensure each header object in
headerRows gets a persistent id when created/added—update the row-creation logic
(e.g., addHeaderRow / any method that pushes to headerRows) to assign a UUID or
incrementing id, and keep removeHeaderRow unchanged so the key remains stable
across reorders/deletes; adjust any places that construct headerRows items to
include the id property.

Comment thread webui/src/views/settings/components/config/components/push/forms/webhookForm.vue Outdated
@gongfuture

Copy link
Copy Markdown
Contributor Author

@Ghost-chu 修改了一下level的代码,请您审阅 7b3a099

@coderabbitai coderabbitai Bot added the priority: medium 中优先级 label May 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/main/java/com/ghostchu/peerbanhelper/util/push/impl/GotifyPushProvider.java (1)

68-93: 💤 Low value

考虑使用 AlertLevel 动态设置优先级。

当前实现接受了 level 参数但未使用,仍然使用配置中的静态 priority 值。您可以考虑将 AlertLevel 映射到 Gotify 的优先级(类似 NtfyPushProvider 的做法),以便根据消息的严重程度动态调整优先级。

♻️ 可选的实现建议
 public boolean push(String title, String content, AlertLevel level) {
     Map<String, Object> map = new HashMap<>();
     map.put("title", title);
     map.put("message", content);

     FormBody.Builder formBody = new FormBody.Builder();

+    int priority = mapLevelToPriority(level);
     var form = formBody.add("title", title)
             .add("message", content)
-            .add("priority", String.valueOf(config.getPriority())).build();
+            .add("priority", String.valueOf(priority)).build();

然后添加映射方法:

private int mapLevelToPriority(AlertLevel level) {
    return switch (level) {
        case TIP -> 0;
        case INFO -> 5;
        case WARN -> 7;
        case ERROR -> 9;
        case FATAL -> 10;
    };
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/GotifyPushProvider.java`
around lines 68 - 93, The push method in GotifyPushProvider currently ignores
the AlertLevel parameter and always uses config.getPriority(); change it to
compute priority from the passed AlertLevel (e.g., add a private
mapLevelToPriority(AlertLevel) helper mirroring NtfyPushProvider) and use that
value when building the form body instead of config.getPriority(); update
references in push (method name: push, class: GotifyPushProvider) to call
mapLevelToPriority(level) and pass its result as the "priority" form field.
src/main/java/com/ghostchu/peerbanhelper/util/push/impl/BarkPushProvider.java (1)

71-98: 💤 Low value

考虑使用 AlertLevel 设置 Bark 通知级别。

Bark API 支持可选的 level 参数用于控制通知的展示方式(如 activetimeSensitivepassive)。当前实现接受了 level 参数但未使用,您可以考虑将 AlertLevel 映射到 Bark 的通知级别。

♻️ 可选的实现建议
 public boolean push(String title, String content, AlertLevel level) {
     Map<String, Object> map = new HashMap<>();
     map.put("title", title);
     map.put("body", content);
     map.put("device_key", config.getDeviceKey());
     map.put("group", config.getMessageGroup());
     map.put("icon", "https://raw.githubusercontent.com/PBH-BTN/PeerBanHelper/refs/heads/master/src/main/resources/assets/icon.png");
+    map.put("level", mapLevelToBarkLevel(level));

然后添加映射方法:

private String mapLevelToBarkLevel(AlertLevel level) {
    return switch (level) {
        case TIP, INFO -> "passive";
        case WARN -> "active";
        case ERROR, FATAL -> "timeSensitive";
    };
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/BarkPushProvider.java`
around lines 71 - 98, The push method in BarkPushProvider currently ignores the
AlertLevel parameter; add a mapping from AlertLevel to Bark's "level" string
(e.g., via a private helper mapLevelToBarkLevel(AlertLevel level) that returns
"passive"/"active"/"timeSensitive" for TIP/INFO, WARN, ERROR/FATAL respectively)
and include the returned value in the payload map under the "level" key before
serializing; update push(String title, String content, AlertLevel level) to call
mapLevelToBarkLevel(level) and put that result into the map so Bark receives the
notification priority.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/ghostchu/peerbanhelper/util/push/PushManagerImpl.java`:
- Around line 51-52: The YAML loader currently assigns null for unknown provider
types in the switch inside PushManagerImpl (the branch that calls
WebhookPushProvider.loadFromYaml), which later causes NPEs when reloadConfig()
adds entries to providerList; change the default branch so it throws a
descriptive IllegalArgumentException (or a custom ConfigParseException) that
includes the unknown "type" and the provider "name" instead of returning null,
so reloadConfig()/pushProvider handling fails fast with a clear error message
rather than allowing nulls into providerList.

---

Nitpick comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/BarkPushProvider.java`:
- Around line 71-98: The push method in BarkPushProvider currently ignores the
AlertLevel parameter; add a mapping from AlertLevel to Bark's "level" string
(e.g., via a private helper mapLevelToBarkLevel(AlertLevel level) that returns
"passive"/"active"/"timeSensitive" for TIP/INFO, WARN, ERROR/FATAL respectively)
and include the returned value in the payload map under the "level" key before
serializing; update push(String title, String content, AlertLevel level) to call
mapLevelToBarkLevel(level) and put that result into the map so Bark receives the
notification priority.

In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/GotifyPushProvider.java`:
- Around line 68-93: The push method in GotifyPushProvider currently ignores the
AlertLevel parameter and always uses config.getPriority(); change it to compute
priority from the passed AlertLevel (e.g., add a private
mapLevelToPriority(AlertLevel) helper mirroring NtfyPushProvider) and use that
value when building the form body instead of config.getPriority(); update
references in push (method name: push, class: GotifyPushProvider) to call
mapLevelToPriority(level) and pass its result as the "priority" form field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a3a97fb6-aa0e-448f-bd1d-459d728412bd

📥 Commits

Reviewing files that changed from the base of the PR and between 1d86506 and 7b3a099.

📒 Files selected for processing (14)
  • src/main/java/com/ghostchu/peerbanhelper/alert/AlertManagerImpl.java
  • src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/PBHPushController.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/PushManager.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/PushManagerImpl.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/PushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/BarkPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/GotifyPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/NtfyPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/PushDeerPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/PushPlusPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/ServerChanPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/SmtpPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/TelegramPushProvider.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/WebhookPushProvider.java

Comment on lines +51 to +52
case "webhook" -> provider = WebhookPushProvider.loadFromYaml(name, section, httpUtil);
default -> provider = null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

YAML 加载分支不要把未知类型降级成 null

reloadConfig() 会把这里的返回值直接加入 providerList。配置里只要出现拼写错误或遗留类型,后续列出渠道、保存配置或批量推送时都会在解引用 pushProvider 时触发 NPE。这里应直接抛出带 type 的异常,或在加载阶段显式跳过 null

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/ghostchu/peerbanhelper/util/push/PushManagerImpl.java`
around lines 51 - 52, The YAML loader currently assigns null for unknown
provider types in the switch inside PushManagerImpl (the branch that calls
WebhookPushProvider.loadFromYaml), which later causes NPEs when reloadConfig()
adds entries to providerList; change the default branch so it throws a
descriptive IllegalArgumentException (or a custom ConfigParseException) that
includes the unknown "type" and the provider "name" instead of returning null,
so reloadConfig()/pushProvider handling fails fast with a clear error message
rather than allowing nulls into providerList.

config.setBodyTemplate(DEFAULT_BODY_TEMPLATE);
}
if (config.getHeaders() == null) {
config.setHeaders(new HashMap<>());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collections.emptyMap()

String time = TimeUtil.formatTimeOnly(currentTime);
String datetime = TimeUtil.formatDateTime(currentTime);
boolean json = contentType != null && contentType.toLowerCase(Locale.ROOT).contains("json");
java.util.function.UnaryOperator<String> transform = v -> {

@Ghost-chu Ghost-chu May 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这块儿的写法我还是感觉太狂野了 (UnaryOperator)

@Ghost-chu

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread webui/src/views/settings/components/config/components/push/forms/webhookForm.vue Outdated
Comment thread webui/src/views/settings/components/config/components/push/forms/webhookForm.vue Outdated
@Ghost-chu Ghost-chu added the waiting-reply 等待Issue/PR所有者回复 label Jun 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/main/java/com/ghostchu/peerbanhelper/util/push/impl/NtfyPushProvider.java (1)

75-118: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

mapLevelToPriority 已成为死代码,level 参数未被实际使用。

push 方法新增了 AlertLevel level 参数(Line 75),但方法体内并未使用它——Priority 请求头现在直接取自 config.getPriority()(Line 88),不再调用 mapLevelToPriority(level)。这导致 Lines 109-118 定义的 mapLevelToPriority 私有方法完全没有调用点,成为死代码,同时也让 level 参数形同虚设,容易让后续维护者误以为 Priority 是由告警级别决定的。

建议移除未使用的 mapLevelToPriority 方法,或者恢复其调用以让 Priority 头真正反映 level(并明确此配置项 prioritylevel 的关系)。

♻️ 建议移除死代码
-    private int mapLevelToPriority(AlertLevel level) {
-        return switch (level) {
-            case TIP -> 1;
-            case INFO -> 2;
-            case WARN -> 3;
-            case ERROR -> 4;
-            case FATAL -> 5;
-        };
-    }
-
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/NtfyPushProvider.java`
around lines 75 - 118, The `push` method in `NtfyPushProvider` now ignores its
`AlertLevel level` argument and uses `config.getPriority()` directly, so
`mapLevelToPriority` is unreachable dead code. Either remove the unused
`mapLevelToPriority(AlertLevel level)` helper and the now-misleading `level`
parameter from `push`, or wire `push` back to use `mapLevelToPriority(level)`
for the `Priority` header so the alert level actually determines priority. Make
sure the final implementation clearly reflects whether priority comes from the
config or from `AlertLevel`, and keep only the code path that is truly used.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@src/main/java/com/ghostchu/peerbanhelper/util/push/impl/NtfyPushProvider.java`:
- Around line 75-118: The `push` method in `NtfyPushProvider` now ignores its
`AlertLevel level` argument and uses `config.getPriority()` directly, so
`mapLevelToPriority` is unreachable dead code. Either remove the unused
`mapLevelToPriority(AlertLevel level)` helper and the now-misleading `level`
parameter from `push`, or wire `push` back to use `mapLevelToPriority(level)`
for the `Priority` header so the alert level actually determines priority. Make
sure the final implementation clearly reflects whether priority comes from the
config or from `AlertLevel`, and keep only the code path that is truly used.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5293b00b-fe6c-4be9-88fd-c41a9d68c631

📥 Commits

Reviewing files that changed from the base of the PR and between dd94aa0 and eaaa274.

📒 Files selected for processing (2)
  • src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/PBHPushController.java
  • src/main/java/com/ghostchu/peerbanhelper/util/push/impl/NtfyPushProvider.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/PBHPushController.java

@gongfuture

Copy link
Copy Markdown
Contributor Author

还没好,前几天期末刚结束,这两天才捡回来继续写代码ww

Comment thread src/main/java/com/ghostchu/peerbanhelper/util/push/impl/NtfyPushProvider.java Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend This is a backend related enhancement 功能增强 java Pull requests that update Java code javascript Pull requests that update Javascript code priority: medium 中优先级 waiting-reply 等待Issue/PR所有者回复 webui This is a webui related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] 建议增加对ntfy推送和自定义webhook的支持

4 participants