// Webhook management page for a project: list existing webhooks, add new ones, // toggle active, delete, send test ping, view recent delivery history per hook. import { type WebhookRow, type WebhookDeliveryRow, } from "../webhooks"; import { type ProjectRow } from "../store"; import { THEME_CSS, escapeHtml, escapeAttr, tabNavHTML } from "../render/layout"; import { projectSettingsTabsHTML } from "./projectUpstreams"; function statusClass(status: number | null): string { if (status === null) return "err"; if (status >= 200 && status < 300) return "ok"; if (status >= 400 && status < 500) return "warn"; return "err"; } function deliveryRowHtml(d: WebhookDeliveryRow): string { const status = d.response_status === null ? "—" : String(d.response_status); const cls = statusClass(d.response_status); const err = d.error ? `
${escapeHtml(d.error)}
` : ""; const body = d.response_body ? `
响应
${escapeHtml(d.response_body)}
` : ""; const payload = `
payload
${escapeHtml(d.payload)}
`; return ` ${escapeHtml(d.created_at)} ${escapeHtml(d.event)} ${escapeHtml(d.delivery_uuid.slice(0, 8))} ${status} ${d.duration_ms === null ? "—" : d.duration_ms + "ms"} ${payload}${body}${err} `; } function webhookCardHtml(wh: WebhookRow, deliveries: WebhookDeliveryRow[]): string { const recent = deliveries.slice(0, 10); const rows = recent.map(deliveryRowHtml).join("") || `暂无投递记录`; const events = (() => { try { return (JSON.parse(wh.events) as string[]).join(", ") || "(none)"; } catch { return "(invalid)"; } })(); return `

#${wh.id} · ${escapeHtml(wh.url)}

${wh.active ? "启用" : "停用"} 订阅: ${escapeHtml(events)} secret: ${wh.secret ? "•••• (" + wh.secret.length + " chars)" : ""}
近期投递(最多 10 条) ${rows}
时间事件delivery状态耗时详情
`; } export interface WebhooksPageInput { project: ProjectRow; webhooks: WebhookRow[]; deliveriesByWebhook: Map; } export function buildWebhooksPage(input: WebhooksPageInput): string { const { project, webhooks, deliveriesByWebhook } = input; const cards = webhooks .map((wh) => webhookCardHtml(wh, deliveriesByWebhook.get(wh.id) ?? [])) .join("") || `

这个项目还没有 webhook。
用下面的表单添加一个。

`; const formAction = `/${encodeURIComponent(project.owner)}/${encodeURIComponent(project.name)}/settings/webhooks`; const settingsTabs = projectSettingsTabsHTML(project.owner, project.name, "webhooks"); const html = ` ework-web · ${escapeHtml(project.owner + "/" + project.name)} · Webhooks ${tabNavHTML("projects")}

Webhooks

${settingsTabs}

Payload 协议兼容 Gitea(X-Gitea-Signature HMAC-SHA256 hex),下游 Action 不用改。
事件: issues (opened/closed/reopened), issue_comment (created)。

${cards}

添加 webhook

订阅:
`; return html; }