mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
chore: auto-fix lint & formatting [skip ci]
This commit is contained in:
parent
3f6f7f17b5
commit
c58b800987
6 changed files with 112 additions and 77 deletions
|
|
@ -46,47 +46,51 @@
|
|||
/>
|
||||
<div class="hint">{{ t("groupEditor.langHint") }}</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.emoji") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.emojiNote") }}</span></label
|
||||
>
|
||||
<label class="inline">
|
||||
<input v-model="form.emoji" type="checkbox" />
|
||||
<span>{{ t("groupEditor.emojiLabel") }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.forgeSources") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.forgeSourcesNote") }}</span></label
|
||||
>
|
||||
<div v-for="(s, i) in form.forgeSources" :key="i" class="forge-source-row">
|
||||
<input
|
||||
v-model="s.host"
|
||||
type="text"
|
||||
class="input f-host"
|
||||
:placeholder="t('groupEditor.forgeSourcesHost')"
|
||||
/>
|
||||
<select v-model="s.type" class="select forge-type">
|
||||
<option value="github">GitHub</option>
|
||||
<option value="gitea">Gitea</option>
|
||||
</select>
|
||||
<button type="button" class="icon-btn danger" @click="form.forgeSources.splice(i, 1)">
|
||||
✕
|
||||
</button>
|
||||
<input
|
||||
v-model="s.name"
|
||||
type="text"
|
||||
class="input f-name"
|
||||
:placeholder="t('groupEditor.forgeSourcesName')"
|
||||
/>
|
||||
<div class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.emoji") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.emojiNote") }}</span></label
|
||||
>
|
||||
<label class="inline">
|
||||
<input v-model="form.emoji" type="checkbox" />
|
||||
<span>{{ t("groupEditor.emojiLabel") }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
>{{ t("groupEditor.forgeSources") }}
|
||||
<span class="lbl-note">{{ t("groupEditor.forgeSourcesNote") }}</span></label
|
||||
>
|
||||
<div v-for="(s, i) in form.forgeSources" :key="i" class="forge-source-row">
|
||||
<input
|
||||
v-model="s.host"
|
||||
type="text"
|
||||
class="input f-host"
|
||||
:placeholder="t('groupEditor.forgeSourcesHost')"
|
||||
/>
|
||||
<select v-model="s.type" class="select forge-type">
|
||||
<option value="github">GitHub</option>
|
||||
<option value="gitea">Gitea</option>
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn danger"
|
||||
@click="form.forgeSources.splice(i, 1)"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
<input
|
||||
v-model="s.name"
|
||||
type="text"
|
||||
class="input f-name"
|
||||
:placeholder="t('groupEditor.forgeSourcesName')"
|
||||
/>
|
||||
</div>
|
||||
<button type="button" class="btn btn-ghost add-filter" @click="addForgeSource">
|
||||
{{ t("groupEditor.forgeSourcesAdd") }}
|
||||
</button>
|
||||
<div class="hint">{{ t("groupEditor.forgeSourcesHint") }}</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-ghost add-filter" @click="addForgeSource">
|
||||
{{ t("groupEditor.forgeSourcesAdd") }}
|
||||
</button>
|
||||
<div class="hint">{{ t("groupEditor.forgeSourcesHint") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Routes belong to groups. Groups scope admin access and can restrict which events
|
|||
| `providers` | string[] | No | Source platforms allowed into this group (`github`, `gitea`); empty = all |
|
||||
| `installationId` | number | No | GitHub App installation id bound to this group; only that installation's events are accepted (empty = all) |
|
||||
| `emoji` | boolean | No | Whether to include emoji in this group's messages (default `true`) |
|
||||
| `forgeSources` | object[] | No | Forge hosts: `{ host, type, name? }` entries (`type` is `github` or `gitea`, `name` is an optional display label) that label this group's message footers; empty = no label |
|
||||
| `forgeSources` | object[] | No | Forge hosts: `{ host, type, name? }` entries (`type` is `github` or `gitea`, `name` is an optional display label) that label this group's message footers; empty = no label |
|
||||
| `lang` | string | No | Message language for every route in this group (e.g. `en`, `zh`; custom via KV `i18n:<lang>`) — see [Message Language](./i18n) — defaults to `en` |
|
||||
| `logTarget` | object | No | Webhook log channel: a Discord `{ platform, channelId, threadId? }` or Telegram `{ platform, chatId, topicId? }` target that receives a summary of every webhook the group's routes dispatch |
|
||||
|
||||
|
|
@ -62,11 +62,13 @@ A group may set `logTarget` to a Discord channel/thread or Telegram chat/topic.
|
|||
A group defines the forges it receives events from via `forgeSources`, a list of `{ host, type, name? }` entries (`type` is `github` or `gitea`). Every message this group's routes send carries the footer label of the **first entry whose type matches the event's provider and whose host matches the repository URL's hostname** (GitHub events match `github.com`). The label is the entry's optional `name` — falling back to the host — so two self-hosted Gitea instances can be shown as "内网 Gitea" / "Git2 仓库" while matched by their distinct hosts:
|
||||
|
||||
```json
|
||||
{ "forgeSources": [
|
||||
{ "host": "github.com", "type": "github", "name": "GitHub 主站" },
|
||||
{ "host": "git1.example.com", "type": "gitea", "name": "内网 Gitea" },
|
||||
{ "host": "git2.example.com", "type": "gitea" }
|
||||
] }
|
||||
{
|
||||
"forgeSources": [
|
||||
{ "host": "github.com", "type": "github", "name": "GitHub 主站" },
|
||||
{ "host": "git1.example.com", "type": "gitea", "name": "内网 Gitea" },
|
||||
{ "host": "git2.example.com", "type": "gitea" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **Discord** — the embed footer shows the label next to the repo (`内网 Gitea · acme/widget`) with the site's favicon as the footer icon (derived from the repository URL).
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
| `providers` | string[] | 否 | 允许进入该分组的来源平台(`github`、`gitea`);空 = 全部 |
|
||||
| `installationId` | number | 否 | 绑定到该分组的 GitHub App 安装 id;仅接受该安装的事件(空 = 全部) |
|
||||
| `emoji` | boolean | 否 | 该分组消息是否包含表情(默认 `true`) |
|
||||
| `forgeSources` | object[] | 否 | 来源主机:`{ host, type, name? }` 条目(`type` 为 `github` 或 `gitea`,`name` 为可选显示名称),用于标注该分组消息底部;空 = 不标注 |
|
||||
| `forgeSources` | object[] | 否 | 来源主机:`{ host, type, name? }` 条目(`type` 为 `github` 或 `gitea`,`name` 为可选显示名称),用于标注该分组消息底部;空 = 不标注 |
|
||||
| `lang` | string | 否 | 该分组所有路由的消息语言(如 `en`、`zh`;可通过 KV `i18n:<lang>` 自定义)——见[消息语言](./i18n)——默认 `en` |
|
||||
| `logTarget` | object | 否 | Webhook 日志频道:Discord 目标 `{ platform, channelId, threadId? }` 或 Telegram 目标 `{ platform, chatId, topicId? }`,接收该分组路由每次分发 webhook 的摘要 |
|
||||
|
||||
|
|
@ -62,11 +62,13 @@
|
|||
分组通过 `forgeSources` 定义自己接收事件的 forge,即一组 `{ host, type, name? }` 条目(`type` 为 `github` 或 `gitea`)。该分组路由发出的每条消息都会使用**与事件来源类型匹配、且 host 与仓库 URL 主机名一致的第一条**配置作为底部标识(GitHub 事件匹配 `github.com`)。标识文本为条目的可选 `name`——未填则回退为 host——因此两个自建 Gitea 实例可以显示为「内网 Gitea」/「Git2 仓库」,同时通过各自主机名完成匹配:
|
||||
|
||||
```json
|
||||
{ "forgeSources": [
|
||||
{ "host": "github.com", "type": "github", "name": "GitHub 主站" },
|
||||
{ "host": "git1.example.com", "type": "gitea", "name": "内网 Gitea" },
|
||||
{ "host": "git2.example.com", "type": "gitea" }
|
||||
] }
|
||||
{
|
||||
"forgeSources": [
|
||||
{ "host": "github.com", "type": "github", "name": "GitHub 主站" },
|
||||
{ "host": "git1.example.com", "type": "gitea", "name": "内网 Gitea" },
|
||||
{ "host": "git2.example.com", "type": "gitea" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **Discord** — embed 底部在仓库名旁显示标识(`内网 Gitea · acme/widget`),并以按仓库 URL 推导的站点 favicon 作为底部图标。
|
||||
|
|
|
|||
|
|
@ -169,10 +169,7 @@ export function senderProfileUrl(repoUrl: string | undefined, login: string): st
|
|||
* entry's display `name` (or its host when no name is set). Link/favicon are
|
||||
* derived from the repository URL.
|
||||
*/
|
||||
export function forgeInfo(
|
||||
event: WebhookEvent,
|
||||
sources?: ForgeSource[],
|
||||
): NeutralForge | undefined {
|
||||
export function forgeInfo(event: WebhookEvent, sources?: ForgeSource[]): NeutralForge | undefined {
|
||||
const repoUrl = (event.payload.repository as { html_url?: string } | undefined)?.html_url;
|
||||
let host: string | undefined;
|
||||
if (repoUrl) {
|
||||
|
|
@ -186,9 +183,7 @@ export function forgeInfo(
|
|||
if (!host && event.provider === "github") host = "github.com";
|
||||
if (!host) return undefined;
|
||||
|
||||
const source = sources?.find(
|
||||
(s) => s.type === event.provider && s.host.toLowerCase() === host,
|
||||
);
|
||||
const source = sources?.find((s) => s.type === event.provider && s.host.toLowerCase() === host);
|
||||
if (!source) return undefined;
|
||||
|
||||
const label = source.name?.trim() || source.host;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ import { log } from "../lib/log";
|
|||
|
||||
const VALID_FILTER_TYPES = new Set(["event", "repo", "actor", "action", "branch", "keyword"]);
|
||||
const ID_RE = /^[a-z0-9][a-z0-9-]*$/;
|
||||
const HOST_RE = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
|
||||
const HOST_RE =
|
||||
/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
|
||||
|
||||
function isValidMatch(match: unknown): match is string | string[] {
|
||||
if (typeof match === "string") return match.trim().length > 0;
|
||||
|
|
@ -328,10 +329,7 @@ export function validateGroups(
|
|||
error: `group "${g.id}".forgeSources[${i}].type must be "github" | "gitea"`,
|
||||
};
|
||||
}
|
||||
if (
|
||||
s.name !== undefined &&
|
||||
(typeof s.name !== "string" || s.name.length > 50)
|
||||
) {
|
||||
if (s.name !== undefined && (typeof s.name !== "string" || s.name.length > 50)) {
|
||||
return {
|
||||
ok: false,
|
||||
error: `group "${g.id}".forgeSources[${i}].name must be a string`,
|
||||
|
|
|
|||
|
|
@ -525,7 +525,11 @@ describe("forge source branding", () => {
|
|||
it("labels github events with the configured host and the github favicon", () => {
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "github", payload: { repository: { html_url: "https://github.com/org/repo" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "github",
|
||||
payload: { repository: { html_url: "https://github.com/org/repo" } },
|
||||
},
|
||||
[ghHost],
|
||||
),
|
||||
).toEqual({
|
||||
|
|
@ -536,9 +540,7 @@ describe("forge source branding", () => {
|
|||
});
|
||||
|
||||
it("labels github events without a repository (ping) via the github.com fallback", () => {
|
||||
expect(
|
||||
forgeInfo({ event: "ping", provider: "github", payload: {} }, [ghHost]),
|
||||
).toEqual({
|
||||
expect(forgeInfo({ event: "ping", provider: "github", payload: {} }, [ghHost])).toEqual({
|
||||
name: "github.com",
|
||||
url: "https://github.com",
|
||||
iconUrl: "https://github.com/favicon.ico",
|
||||
|
|
@ -552,7 +554,11 @@ describe("forge source branding", () => {
|
|||
];
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "gitea", payload: { repository: { html_url: "https://git1.example.com/org/a" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "gitea",
|
||||
payload: { repository: { html_url: "https://git1.example.com/org/a" } },
|
||||
},
|
||||
sources,
|
||||
),
|
||||
).toEqual({
|
||||
|
|
@ -562,7 +568,11 @@ describe("forge source branding", () => {
|
|||
});
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "gitea", payload: { repository: { html_url: "https://git2.example.com/org/b" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "gitea",
|
||||
payload: { repository: { html_url: "https://git2.example.com/org/b" } },
|
||||
},
|
||||
sources,
|
||||
),
|
||||
).toEqual({
|
||||
|
|
@ -579,7 +589,11 @@ describe("forge source branding", () => {
|
|||
];
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "gitea", payload: { repository: { html_url: "https://git1.example.com/org/a" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "gitea",
|
||||
payload: { repository: { html_url: "https://git1.example.com/org/a" } },
|
||||
},
|
||||
sources,
|
||||
),
|
||||
).toEqual({
|
||||
|
|
@ -589,7 +603,11 @@ describe("forge source branding", () => {
|
|||
});
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "gitea", payload: { repository: { html_url: "https://git2.example.com/org/b" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "gitea",
|
||||
payload: { repository: { html_url: "https://git2.example.com/org/b" } },
|
||||
},
|
||||
sources,
|
||||
),
|
||||
).toEqual({
|
||||
|
|
@ -599,7 +617,11 @@ describe("forge source branding", () => {
|
|||
});
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "gitea", payload: { repository: { html_url: "https://git1.example.com/org/a" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "gitea",
|
||||
payload: { repository: { html_url: "https://git1.example.com/org/a" } },
|
||||
},
|
||||
[{ host: "git1.example.com", type: "gitea", name: " " }],
|
||||
),
|
||||
).toEqual({
|
||||
|
|
@ -612,7 +634,11 @@ describe("forge source branding", () => {
|
|||
it("matches hosts case-insensitively", () => {
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "gitea", payload: { repository: { html_url: "https://GIT1.Example.COM/org/a" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "gitea",
|
||||
payload: { repository: { html_url: "https://GIT1.Example.COM/org/a" } },
|
||||
},
|
||||
[{ host: "Git1.Example.com", type: "gitea" }],
|
||||
),
|
||||
).toEqual({
|
||||
|
|
@ -624,7 +650,9 @@ describe("forge source branding", () => {
|
|||
|
||||
it("returns no label when the gitea repo url is missing or unparseable", () => {
|
||||
expect(
|
||||
forgeInfo({ event: "push", provider: "gitea", payload: {} }, [{ host: "git1.example.com", type: "gitea" }]),
|
||||
forgeInfo({ event: "push", provider: "gitea", payload: {} }, [
|
||||
{ host: "git1.example.com", type: "gitea" },
|
||||
]),
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
forgeInfo(
|
||||
|
|
@ -635,10 +663,16 @@ describe("forge source branding", () => {
|
|||
});
|
||||
|
||||
it("returns undefined when no source matches the provider or host", () => {
|
||||
expect(forgeInfo({ event: "custom", provider: "custom", payload: {} }, [ghHost])).toBeUndefined();
|
||||
expect(
|
||||
forgeInfo({ event: "custom", provider: "custom", payload: {} }, [ghHost]),
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
forgeInfo(
|
||||
{ event: "push", provider: "gitea", payload: { repository: { html_url: "https://other.example.com/org/a" } } },
|
||||
{
|
||||
event: "push",
|
||||
provider: "gitea",
|
||||
payload: { repository: { html_url: "https://other.example.com/org/a" } },
|
||||
},
|
||||
[ghHost],
|
||||
),
|
||||
).toBeUndefined();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue