feat: outgoing webhooks

Owners and admins can point a project at other systems: a signed JSON
POST (HMAC-SHA256 of the body) when tickets, comments, releases or sprints
change, filtered by event or family. Deliveries are retried a couple of
times, the last one is shown, and a ping can be sent from the settings.
Private addresses are refused unless WEBHOOKS_ALLOW_PRIVATE is set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 15:36:19 +02:00
parent 046d77c813
commit d43fc620e9
37 changed files with 3660 additions and 28 deletions

View File

@@ -1,3 +1,4 @@
import { OutgoingWebhook } from '@task-stream/api/tracker/application';
import {
ActivityReadModel,
CommentReadModel,
@@ -30,6 +31,7 @@ export type ActivityDoc = Doc<ActivityReadModel>;
export type TimelineDoc = Doc<IssueTimelineEntry>;
export type NotificationDoc = Doc<NotificationReadModel>;
export type SavedFilterDoc = Doc<SavedFilterReadModel>;
export type OutgoingWebhookDoc = Omit<OutgoingWebhook, 'id'> & { _id: string };
export interface GitIntegrationDoc {
_id: string;
secret: string;
@@ -52,6 +54,7 @@ export const ReadModels = {
SavedFilter: 'SavedFilter',
Sprint: 'SprintReadModel',
GitIntegration: 'GitIntegration',
OutgoingWebhook: 'OutgoingWebhook',
} as const;
const userRef = { _id: false, id: String, name: String };
@@ -203,6 +206,18 @@ export const GitIntegrationSchema = new Schema<GitIntegrationDoc>(
{ ...loose, collection: 'git_integrations' },
);
export const OutgoingWebhookSchema = new Schema<OutgoingWebhookDoc>(
{
_id: String,
projectId: { type: String, index: true },
active: Boolean,
events: [String],
createdBy: userRef,
createdAt: String,
},
{ ...loose, collection: 'outgoing_webhooks' },
);
export const READ_MODEL_SCHEMAS = [
{ name: ReadModels.Project, schema: ProjectSchema },
{ name: ReadModels.Issue, schema: IssueSchema },
@@ -210,6 +225,7 @@ export const READ_MODEL_SCHEMAS = [
{ name: ReadModels.Release, schema: ReleaseSchema },
{ name: ReadModels.Sprint, schema: SprintSchema },
{ name: ReadModels.GitIntegration, schema: GitIntegrationSchema },
{ name: ReadModels.OutgoingWebhook, schema: OutgoingWebhookSchema },
{ name: ReadModels.Activity, schema: ActivitySchema },
{ name: ReadModels.Timeline, schema: TimelineSchema },
{ name: ReadModels.Notification, schema: NotificationSchema },