HookBridge 提供 23 个 REST 端点与 1 个 WebSocket 通道,覆盖健康检查、请求管理、重放、Mock、签名验证、转发记录与统计。
- 基础路径:
http(s)://<host>:<port> - 默认端口:
9876 - 响应格式:
application/json; charset=utf-8 - 时间字段:Unix 毫秒(整数)或 RFC3339(仅展示字段)
HookBridge 管理 API 采用 Bearer Token + IP 白名单 双层鉴权。
在 hookbridge.yaml 配置:
security:
secret: "your-long-random-bearer-token"调用管理 API 时携带:
Authorization: Bearer your-long-random-bearer-token
secret 为空时跳过 Token 校验(仅本地调试推荐)。
security:
ip_whitelist:
- 10.0.0.0/8
- 192.168.0.0/16
- 203.0.113.42/32支持 CIDR 语法。纯 IP 会自动补 /32(IPv4)或 /128(IPv6)。白名单为空时允许任意 IP。
| 路径前缀 | 鉴权 | 说明 |
|---|---|---|
/api/* |
是 | 所有管理 API |
/health |
否 | 健康检查(公开,供 LB 探活) |
/metrics |
否 | Prometheus 指标(公开,按需收紧) |
/ws |
否 | WebSocket(默认公开,建议反代时收紧) |
| 其它任意路径 | 否 | WebHook 接收路径,需对上游平台开放 |
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{
"error": "unauthorized"
}健康检查(公开,供负载均衡探活)。
- 鉴权:否
- 请求参数:无
- 说明:轻量探活,不查数据库,仅返回进程存活状态。
HTTP/1.1 200 OK
Content-Type: application/json
{
"ok": true,
"version": "v1.0.0-final"
}Prometheus 格式指标(公开)。
- 鉴权:否
- 请求参数:无
- 说明:返回 Prometheus 文本格式指标,包含请求数、延迟分位、存储行数、WebSocket 客户端数等。
HTTP/1.1 200 OK
Content-Type: text/plain; version=0.0.4
# HELP hookbridge_requests_total Total received webhooks
# TYPE hookbridge_requests_total counter
hookbridge_requests_total 10234
hookbridge_requests_total{template="GitHub"} 3120
hookbridge_requests_total{template="Stripe"} 892
# HELP hookbridge_request_duration_seconds Request duration
# TYPE hookbridge_request_duration_seconds histogram
hookbridge_request_duration_seconds_bucket{le="0.001"} 8900
hookbridge_request_duration_seconds_bucket{le="0.01"} 10100
hookbridge_request_duration_seconds_bucket{le="0.1"} 10234
hookbridge_request_duration_seconds_bucket{le="+Inf"} 10234
# HELP hookbridge_storage_rows SQLite rows
# TYPE hookbridge_storage_rows gauge
hookbridge_storage_rows{table="requests"} 9821
hookbridge_storage_rows{table="replay_records"} 412
# HELP hookbridge_ws_clients WebSocket connected clients
# TYPE hookbridge_ws_clients gauge
hookbridge_ws_clients 3
API 健康检查(需鉴权),返回版本与公网地址。
- 鉴权:是
- 请求参数:无
HTTP/1.1 200 OK
{
"ok": true,
"public": "https://hook.example.com",
"version": "v1.0.0-final"
}HTTP/1.1 401 Unauthorized
{
"error": "unauthorized"
}获取所有可用协议模板(内置 + 自定义)。
- 鉴权:是
- 请求参数:无
HTTP/1.1 200 OK
{
"templates": [
{
"name": "Stripe",
"description": "Stripe 支付回调(含 Stripe-Signature 头)"
},
{
"name": "GitHub",
"description": "GitHub WebHook(X-GitHub-Event 头)"
},
{
"name": "GitLab",
"description": "GitLab WebHook(X-Gitlab-Event 头)"
},
{
"name": "Gitee",
"description": "Gitee WebHook(X-Gitee-Token 头)"
},
{
"name": "Alipay",
"description": "支付宝回调(Body 含 trade_status / notify_id)"
},
{
"name": "WeChatPay",
"description": "微信支付回调(XML 体含 appid/mch_id)"
},
{
"name": "DingTalk",
"description": "钉钉事件订阅(Body 含 EventType)"
},
{
"name": "Feishu",
"description": "飞书事件订阅(Header 含 X-Lark-Request-Timestamp)"
},
{
"name": "WeCom",
"description": "企业微信回调(XML 体含 ToUserName/Encrypt)"
},
{
"name": "GB28181",
"description": "GB28181 平台推流通知(自定义路径或 JSON 体)"
},
{
"name": "HikvisionISAPI",
"description": "海康 ISAPI 告警(XML 体含 EventNotificationAlert)"
},
{
"name": "DahuaDSS",
"description": "大华 DSS 回调(自定义路径或 JSON 体含 deviceId)"
}
]
}请求列表查询,支持分页与多维度过滤。
- 鉴权:是
- 请求参数(Query):
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
page |
int | 1 | 页码(与 offset 二选一,page 优先) |
size |
int | 50 | 每页条数,最大 500 |
offset |
int | 0 | 偏移量(与 page 二选一) |
search |
string | 空 | URL / Header / Body 模糊搜索 |
path |
string | 空 | 精确匹配 path |
method |
string | 空 | 精确匹配 HTTP 方法(自动转大写) |
content_type |
string | 空 | 前缀匹配 Content-Type |
ip |
string | 空 | 精确匹配 RemoteIP |
template |
string | 空 | 精确匹配协议模板名 |
start |
int | 0 | 起始时间(Unix 毫秒),0 表示不限制 |
end |
int | 0 | 结束时间(Unix 毫秒),0 表示不限制 |
HTTP/1.1 200 OK
{
"total": 10234,
"items": [
{
"id": "7c8f9a2b-1234-5678-9abc-def012345678",
"method": "POST",
"url": "/webhook",
"path": "/webhook",
"headers": {
"Content-Type": "application/json",
"X-GitHub-Event": "push"
},
"query": {},
"body": "{\"ref\":\"refs/heads/main\",\"repo\":\"hookbridge/demo\"}",
"content_type": "application/json",
"remote_ip": "192.30.252.1",
"user_agent": "GitHub-Hookshot/abc123",
"created_at": "2026-07-26T10:23:45.123Z",
"protocol_template": "GitHub",
"status_code": 200,
"response_body": "{\"ok\":true,\"msg\":\"HookBridge received\"}",
"signature": "a1b2c3d4e5f6...",
"duration_ms": 1
}
],
"limit": 50,
"offset": 0
}HTTP/1.1 500 Internal Server Error
{
"error": "query: database is closed"
}请求详情,附带协议模板字段提取(命中模板时)。
- 鉴权:是
- 请求参数(Path):
| 参数 | 类型 | 说明 |
|---|---|---|
id |
string | 请求 UUID |
HTTP/1.1 200 OK
{
"request": {
"id": "7c8f9a2b-1234-5678-9abc-def012345678",
"method": "POST",
"url": "/webhook",
"path": "/webhook",
"headers": {
"Content-Type": "application/json",
"X-GitHub-Event": "push",
"X-GitHub-Delivery": "abc123",
"X-Hub-Signature-256": "sha256=..."
},
"query": {},
"body": "{\"ref\":\"refs/heads/main\",\"repository\":{\"full_name\":\"hookbridge/demo\"}}",
"content_type": "application/json",
"remote_ip": "192.30.252.1",
"user_agent": "GitHub-Hookshot/abc123",
"created_at": "2026-07-26T10:23:45.123Z",
"protocol_template": "GitHub",
"status_code": 200,
"response_body": "{\"ok\":true,\"msg\":\"HookBridge received\"}",
"signature": "a1b2c3d4e5f6...",
"duration_ms": 1
},
"fields": {
"event": "push",
"delivery_id": "abc123",
"ref": "refs/heads/main",
"repo": "hookbridge/demo"
}
}HTTP/1.1 200 OK
{
"id": "7c8f9a2b-...",
"method": "POST",
"url": "/custom-hook",
"path": "/custom-hook",
"headers": { "...": "..." },
"protocol_template": "",
"..."
: "..."
}HTTP/1.1 404 Not Found
{
"error": "not found"
}删除单条请求。
- 鉴权:是
- 请求参数(Path):
| 参数 | 类型 | 说明 |
|---|---|---|
id |
string | 请求 UUID |
HTTP/1.1 200 OK
{
"deleted": "7c8f9a2b-1234-5678-9abc-def012345678"
}HTTP/1.1 500 Internal Server Error
{
"error": "delete: database is closed"
}清空所有请求(不可恢复)。
- 鉴权:是
- 请求参数:无
HTTP/1.1 200 OK
{
"deleted": true
}HTTP/1.1 500 Internal Server Error
{
"error": "delete all: database is closed"
}一键重放指定请求。
- 鉴权:是
- 请求参数(Path):
| 参数 | 类型 | 说明 |
|---|---|---|
id |
string | 请求 UUID |
-
请求 Body:无
-
说明:优先使用企业版重放引擎(带记录)。若配置了转发目标,则重放到第一个 forward target;否则回退到本地标记
replayed: false。
HTTP/1.1 200 OK
{
"id": "replay-a1b2c3d4-...",
"original_id": "7c8f9a2b-1234-5678-9abc-def012345678",
"target_url": "https://upstream1.example.com/hook",
"method": "POST",
"status_code": 200,
"response_body": "{\"ok\":true}",
"duration_ms": 42,
"created_at": 1785049600123,
"success": true
}HTTP/1.1 404 Not Found
{
"error": "not found"
}HTTP/1.1 502 Bad Gateway
{
"error": "dial tcp: connection refused",
"id": "7c8f9a2b-..."
}修改后重放:支持覆盖 method / headers / body / content_type / target_url。
- 鉴权:是
- 请求参数(Path):
| 参数 | 类型 | 说明 |
|---|---|---|
id |
string | 请求 UUID |
- 请求 Body(JSON):
| 字段 | 类型 | 说明 |
|---|---|---|
target_url |
string | 重放目标 URL,空则用原请求 URL |
method |
string | 覆盖 HTTP 方法 |
headers |
map[string]string | 合并到原 headers(同名覆盖) |
body |
string (base64) 或 []byte | 覆盖请求体 |
content_type |
string | 覆盖 Content-Type |
POST /api/requests/7c8f9a2b-.../replay-modified
Authorization: Bearer xxx
Content-Type: application/json
{
"target_url": "https://staging.example.com/hook",
"method": "PUT",
"headers": {
"X-Replay-Reason": "debug"
},
"body": "eyJyZWYiOiJyZWZzL2hlYWRzL2RldiJ9",
"content_type": "application/json"
}HTTP/1.1 200 OK
{
"id": "replay-b2c3d4e5-...",
"original_id": "7c8f9a2b-...",
"target_url": "https://staging.example.com/hook",
"method": "PUT",
"status_code": 200,
"response_body": "{\"ok\":true}",
"duration_ms": 38,
"created_at": 1785049700123,
"success": true
}HTTP/1.1 501 Not Implemented
{
"error": "replay engine not enabled (enterprise feature)"
}批量重放,最多 500 条。
- 鉴权:是
- 请求 Body(JSON):
| 字段 | 类型 | 说明 |
|---|---|---|
ids |
[]string | 请求 ID 列表,最大 500 |
concurrent |
bool | 是否并发执行(默认 false 串行) |
opts |
ReplayOptions | 重放选项(同 10),应用到所有请求 |
POST /api/replay/batch
Authorization: Bearer xxx
Content-Type: application/json
{
"ids": [
"7c8f9a2b-...",
"8d9e0b1c-...",
"9e0f1c2d-..."
],
"concurrent": true,
"opts": {
"target_url": "https://staging.example.com/hook"
}
}HTTP/1.1 200 OK
{
"total": 3,
"success": 2,
"failed": 1,
"results": [
{
"original_id": "7c8f9a2b-...",
"success": true,
"status_code": 200,
"duration_ms": 42
},
{
"original_id": "8d9e0b1c-...",
"success": true,
"status_code": 200,
"duration_ms": 38
},
{
"original_id": "9e0f1c2d-...",
"success": false,
"error": "dial tcp: connection refused",
"duration_ms": 5
}
]
}HTTP/1.1 400 Bad Request
{
"error": "too many ids (max 500)"
}对比两条请求,返回字段级差异。
- 鉴权:是
- 请求 Body(JSON):
| 字段 | 类型 | 说明 |
|---|---|---|
left_id |
string | 左侧请求 ID |
right_id |
string | 右侧请求 ID |
POST /api/requests/compare
Authorization: Bearer xxx
Content-Type: application/json
{
"left_id": "7c8f9a2b-...",
"right_id": "8d9e0b1c-..."
}HTTP/1.1 200 OK
{
"left": {
"id": "7c8f9a2b-...",
"method": "POST",
"path": "/webhook",
"headers": { "...": "..." }
},
"right": {
"id": "8d9e0b1c-...",
"method": "POST",
"path": "/webhook",
"headers": { "...": "..." }
},
"diffs": [
{
"field": "headers.X-GitHub-Delivery",
"left": "abc123",
"right": "def456"
},
{
"field": "body.ref",
"left": "refs/heads/main",
"right": "refs/heads/dev"
}
]
}HTTP/1.1 404 Not Found
{
"error": "left not found"
}查询重放记录列表。
- 鉴权:是
- 请求参数(Query):
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
original_id |
string | 空 | 按原请求 ID 过滤 |
limit |
int | 50 | 返回条数上限 |
HTTP/1.1 200 OK
{
"items": [
{
"id": "replay-a1b2c3d4-...",
"original_id": "7c8f9a2b-...",
"target_url": "https://upstream1.example.com/hook",
"method": "POST",
"status_code": 200,
"response_body": "{\"ok\":true}",
"duration_ms": 42,
"created_at": 1785049600123,
"success": true
}
],
"total": 1
}HTTP/1.1 500 Internal Server Error
{
"error": "list replay records: database is closed"
}获取 Mock 规则列表。
- 鉴权:是
- 请求参数:无
HTTP/1.1 200 OK
{
"items": [
{
"id": 1,
"name": "Stripe 成功回调",
"enabled": true,
"priority": 100,
"match_path": "/stripe",
"match_method": "POST",
"match_header": null,
"match_body": null,
"response_status": 200,
"response_body": "{\"id\":\"evt_mock_001\",\"object\":\"event\",\"type\":\"payment_intent.succeeded\"}",
"response_headers": {
"Content-Type": "application/json"
},
"delay_ms": 0,
"created_at": 1785049600123,
"updated_at": 1785049600123
}
],
"total": 1
}HTTP/1.1 500 Internal Server Error
{
"error": "list mock rules: database is closed"
}创建 Mock 规则。
- 鉴权:是
- 请求 Body(JSON):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | 是 | 规则名称 |
enabled |
bool | 否 | 是否启用,默认 true |
priority |
int | 否 | 优先级(数字越大越先匹配),默认 0 |
match_path |
string | 否 | 匹配路径(精确或正则) |
match_method |
string | 否 | 匹配 HTTP 方法 |
match_header |
map[string]string | 否 | 匹配 header(值为正则) |
match_body |
string | 否 | 匹配 body(正则或关键字) |
response_status |
int | 否 | 响应状态码,默认 200 |
response_body |
string | 否 | 响应体 |
response_headers |
map[string]string | 否 | 响应头 |
delay_ms |
int | 否 | 响应延迟毫秒,默认 0 |
POST /api/mock/rules
Authorization: Bearer xxx
Content-Type: application/json
{
"name": "GitHub push 事件",
"enabled": true,
"priority": 100,
"match_path": "/github",
"match_method": "POST",
"match_header": {
"X-GitHub-Event": "push"
},
"response_status": 200,
"response_body": "{\"ok\":true}",
"response_headers": {
"Content-Type": "application/json"
},
"delay_ms": 100
}HTTP/1.1 201 Created
{
"id": 2,
"name": "GitHub push 事件",
"enabled": true,
"priority": 100,
"match_path": "/github",
"match_method": "POST",
"match_header": {
"X-GitHub-Event": "push"
},
"match_body": null,
"response_status": 200,
"response_body": "{\"ok\":true}",
"response_headers": {
"Content-Type": "application/json"
},
"delay_ms": 100,
"created_at": 1785049700123,
"updated_at": 1785049700123
}HTTP/1.1 400 Bad Request
{
"error": "name required"
}获取单条 Mock 规则详情。
- 鉴权:是
- 请求参数(Path):
| 参数 | 类型 | 说明 |
|---|---|---|
id |
int | 规则 ID |
HTTP/1.1 200 OK
{
"id": 2,
"name": "GitHub push 事件",
"enabled": true,
"priority": 100,
"..."
}HTTP/1.1 400 Bad Request
{
"error": "invalid id"
}HTTP/1.1 404 Not Found
{
"error": "not found"
}更新 Mock 规则(全量覆盖)。
- 鉴权:是
- 请求参数(Path):同 16
- 请求 Body(JSON):同 15 的字段(
id由路径指定,会被覆盖)
PUT /api/mock/rules/2
Authorization: Bearer xxx
Content-Type: application/json
{
"name": "GitHub push 事件(已禁用)",
"enabled": false,
"priority": 100,
"match_path": "/github",
"response_status": 200,
"response_body": "{\"ok\":true}"
}HTTP/1.1 200 OK
{
"id": 2,
"name": "GitHub push 事件(已禁用)",
"enabled": false,
"..."
}HTTP/1.1 400 Bad Request
{
"error": "invalid body: invalid character 'x' looking for beginning of value"
}删除 Mock 规则。
- 鉴权:是
- 请求参数(Path):同 16
HTTP/1.1 200 OK
{
"deleted": 2
}HTTP/1.1 400 Bad Request
{
"error": "invalid id"
}查询签名验证日志。
- 鉴权:是
- 请求参数(Query):
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
limit |
int | 50 | 返回条数上限 |
HTTP/1.1 200 OK
{
"items": [
{
"id": "siglog-a1b2c3d4-...",
"request_id": "7c8f9a2b-...",
"template": "Stripe",
"algorithm": "hmac-sha256",
"passed": true,
"reason": "",
"created_at": 1785049600123
},
{
"id": "siglog-b2c3d4e5-...",
"request_id": "8d9e0b1c-...",
"template": "GitHub",
"algorithm": "hmac-sha256",
"passed": false,
"reason": "signature mismatch: expected abc..., got def...",
"created_at": 1785049600456
}
],
"total": 2
}HTTP/1.1 500 Internal Server Error
{
"error": "list signature logs: database is closed"
}列出已注册的签名验证器名称。
- 鉴权:是
- 请求参数:无
HTTP/1.1 200 OK
{
"items": ["Stripe", "GitHub", "Alipay", "WeChatPay"],
"total": 4
}查询转发记录。
- 鉴权:是
- 请求参数(Query):
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
request_id |
string | 空 | 按原请求 ID 过滤 |
limit |
int | 50 | 返回条数上限 |
HTTP/1.1 200 OK
{
"items": [
{
"id": "fwd-a1b2c3d4-...",
"request_id": "7c8f9a2b-...",
"target": "https://upstream1.example.com/hook",
"attempt": 1,
"status_code": 200,
"response_body": "{\"ok\":true}",
"error": "",
"duration_ms": 42,
"success": 1,
"created_at": 1785049600123
},
{
"id": "fwd-b2c3d4e5-...",
"request_id": "8d9e0b1c-...",
"target": "https://upstream2.example.com/hook",
"attempt": 3,
"status_code": 0,
"response_body": "",
"error": "dial tcp: connection refused (after 3 retries)",
"duration_ms": 15000,
"success": 0,
"created_at": 1785049600456
}
],
"total": 2
}HTTP/1.1 500 Internal Server Error
{
"error": "list forward records: database is closed"
}获取统计数据,按方法 / 状态码 / Content-Type / 来源 IP / 模板 / 24 小时分布聚合。
- 鉴权:是
- 请求参数:无
- 说明:聚合统计基于最近 24 小时数据;
total字段为全量总数。
HTTP/1.1 200 OK
{
"total": 10234,
"last_24h": 5421,
"by_method": {
"POST": 9821,
"GET": 413
},
"by_status": {
"200": 10200,
"404": 34
},
"by_content_type": {
"application/json": 8900,
"application/x-www-form-urlencoded": 1000,
"application/xml": 334
},
"by_remote_ip": {
"192.30.252.1": 3120,
"203.0.113.42": 2200,
"127.0.0.1": 100
},
"by_template": {
"GitHub": 3120,
"Stripe": 892,
"WeChatPay": 134,
"unknown": 6088
},
"by_hour": {
"09:00": 320,
"10:00": 540,
"11:00": 612,
"12:00": 410,
"13:00": 380,
"14:00": 720
}
}HTTP/1.1 500 Internal Server Error
{
"error": "stats: database is closed"
}WebSocket 实时推送通道,新请求到达时秒级推送给所有已连接客户端。
- 鉴权:否(默认公开,建议反代时收紧)
- 协议:WebSocket(RFC 6455)
- 子协议:无
- Origin:允许任意(社区版自托管调试场景)
浏览器 JavaScript:
const ws = new WebSocket('ws://localhost:9876/ws');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(msg.type, msg);
};{
"type": "request",
"request": {
"id": "7c8f9a2b-...",
"method": "POST",
"path": "/webhook",
"headers": { "...": "..." },
"body": "{\"ref\":\"refs/heads/main\"}",
"protocol_template": "GitHub",
"created_at": "2026-07-26T10:23:45.123Z",
"duration_ms": 1
},
"parsed": {
"format": "json",
"fields": {
"ref": "refs/heads/main"
}
},
"is_dup": false,
"timestamp": 1785049600123
}服务端每 30 秒广播一次心跳:
{
"type": "ping",
"timestamp": 1785049630123
}type |
触发条件 | 携带字段 |
|---|---|---|
request |
收到新 WebHook 请求 | request / parsed / is_dup |
ping |
心跳(每 30 秒) | timestamp |
- 客户端读取超时 60 秒,超时未收到消息则断开
- 服务端每 30 秒推送一次
ping,可作为保活信号 - 客户端可发送任意消息保活(如空 JSON
{}),服务端仅用于刷新读取死线 - 服务端读取上限 8192 字节,超过则断开
- 单连接发送队列 64 条消息,满了则丢弃(避免慢客户端拖垮服务端)
- 广播通道总缓冲 256 条,满了则丢弃
- 注册 / 注销通道缓冲 8 个,避免注册洪峰阻塞主循环
所有端点在鉴权失败时返回:
HTTP/1.1 401 Unauthorized
{
"error": "unauthorized"
}路径不存在时返回:
HTTP/1.1 404 Not Found
{
"error": "not found",
"path": "unknown/path"
}方法不允许时返回:
HTTP/1.1 405 Method Not Allowed
{
"error": "method not allowed"
}服务端内部错误:
HTTP/1.1 500 Internal Server Error
{
"error": "<错误详情>"
}企业版功能未启用时(如重放引擎未注入):
HTTP/1.1 501 Not Implemented
{
"error": "replay engine not enabled (enterprise feature)"
}