mirror of
https://github.com/docmost/docmost.git
synced 2026-05-16 14:14:06 +08:00
feat(webhooks): dispatch domain events to webhook subscribers
This commit is contained in:
@@ -33,6 +33,8 @@ import {
|
||||
HISTORY_INTERVAL,
|
||||
} from '../constants';
|
||||
import { TransclusionService } from '../../core/page/transclusion/transclusion.service';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@Injectable()
|
||||
export class PersistenceExtension implements Extension {
|
||||
@@ -47,6 +49,7 @@ export class PersistenceExtension implements Extension {
|
||||
@InjectQueue(QueueName.NOTIFICATION_QUEUE) private notificationQueue: Queue,
|
||||
private readonly collabHistory: CollabHistoryService,
|
||||
private readonly transclusionService: TransclusionService,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
async onLoadDocument(data: onLoadDocumentPayload) {
|
||||
@@ -199,6 +202,19 @@ export class PersistenceExtension implements Extension {
|
||||
});
|
||||
|
||||
await this.enqueuePageHistory(page);
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
page.workspaceId,
|
||||
WebhookEvent.PageUpdated,
|
||||
{
|
||||
id: page.id,
|
||||
slugId: page.slugId,
|
||||
title: page.title,
|
||||
spaceId: page.spaceId,
|
||||
workspaceId: page.workspaceId,
|
||||
updatedAt: page.updatedAt,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ import { InjectQueue } from '@nestjs/bullmq';
|
||||
import { QueueJob, QueueName } from '../../../integrations/queue/constants';
|
||||
import { Queue } from 'bullmq';
|
||||
import { createByteCountingStream } from '../../../common/helpers/utils';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@Injectable()
|
||||
export class AttachmentService {
|
||||
@@ -39,6 +41,7 @@ export class AttachmentService {
|
||||
private readonly spaceRepo: SpaceRepo,
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
@InjectQueue(QueueName.ATTACHMENT_QUEUE) private attachmentQueue: Queue,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
async uploadFile(opts: {
|
||||
@@ -271,7 +274,7 @@ export class AttachmentService {
|
||||
spaceId,
|
||||
trx,
|
||||
} = opts;
|
||||
return this.attachmentRepo.insertAttachment(
|
||||
const attachment = await this.attachmentRepo.insertAttachment(
|
||||
{
|
||||
id: attachmentId,
|
||||
type: type,
|
||||
@@ -287,6 +290,23 @@ export class AttachmentService {
|
||||
},
|
||||
trx,
|
||||
);
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspaceId,
|
||||
WebhookEvent.AttachmentUploaded,
|
||||
{
|
||||
id: attachment.id,
|
||||
fileName: attachment.fileName,
|
||||
mimeType: attachment.mimeType,
|
||||
fileSize: attachment.fileSize,
|
||||
pageId: attachment.pageId,
|
||||
spaceId: attachment.spaceId,
|
||||
workspaceId: attachment.workspaceId,
|
||||
creatorId: attachment.creatorId,
|
||||
},
|
||||
);
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
async handleDeleteAiChatAttachments(aiChatId: string) {
|
||||
|
||||
@@ -32,6 +32,8 @@ import {
|
||||
IAuditService,
|
||||
} from '../../integrations/audit/audit.service';
|
||||
import { WsService } from '../../ws/ws.service';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('comments')
|
||||
@@ -44,6 +46,7 @@ export class CommentController {
|
||||
private readonly pageAccessService: PageAccessService,
|
||||
private readonly wsService: WsService,
|
||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@@ -192,5 +195,16 @@ export class CommentController {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
comment.workspaceId,
|
||||
WebhookEvent.CommentDeleted,
|
||||
{
|
||||
id: comment.id,
|
||||
pageId: comment.pageId,
|
||||
spaceId: comment.spaceId,
|
||||
workspaceId: comment.workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import { QueueJob, QueueName } from '../../integrations/queue/constants';
|
||||
import { extractUserMentionIdsFromJson } from '../../common/helpers/prosemirror/utils';
|
||||
import { ICommentNotificationJob } from '../../integrations/queue/constants/queue.interface';
|
||||
import { WsService } from '../../ws/ws.service';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@Injectable()
|
||||
export class CommentService {
|
||||
@@ -33,6 +35,7 @@ export class CommentService {
|
||||
private generalQueue: Queue,
|
||||
@InjectQueue(QueueName.NOTIFICATION_QUEUE)
|
||||
private notificationQueue: Queue,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
async findById(commentId: string) {
|
||||
@@ -142,6 +145,21 @@ export class CommentService {
|
||||
comment,
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspaceId,
|
||||
WebhookEvent.CommentCreated,
|
||||
{
|
||||
id: comment.id,
|
||||
pageId: comment.pageId,
|
||||
spaceId: comment.spaceId,
|
||||
workspaceId: comment.workspaceId,
|
||||
type: comment.type,
|
||||
content: comment.content,
|
||||
creatorId: comment.creatorId,
|
||||
createdAt: comment.createdAt,
|
||||
},
|
||||
);
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
@@ -203,6 +221,22 @@ export class CommentService {
|
||||
comment,
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
comment.workspaceId,
|
||||
WebhookEvent.CommentUpdated,
|
||||
{
|
||||
id: comment.id,
|
||||
pageId: comment.pageId,
|
||||
spaceId: comment.spaceId,
|
||||
workspaceId: comment.workspaceId,
|
||||
type: comment.type,
|
||||
content: comment.content,
|
||||
creatorId: comment.creatorId,
|
||||
createdAt: comment.createdAt,
|
||||
updatedAt: comment.updatedAt,
|
||||
},
|
||||
);
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ import {
|
||||
IAuditService,
|
||||
} from '../../integrations/audit/audit.service';
|
||||
import { getPageTitle } from '../../common/helpers';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('pages')
|
||||
@@ -65,6 +67,7 @@ export class PageController {
|
||||
private readonly backlinkService: BacklinkService,
|
||||
private readonly labelService: LabelService,
|
||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@@ -366,6 +369,18 @@ export class PageController {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspace.id,
|
||||
WebhookEvent.PageDeleted,
|
||||
{
|
||||
id: page.id,
|
||||
slugId: page.slugId,
|
||||
title: page.title,
|
||||
spaceId: page.spaceId,
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,6 +421,18 @@ export class PageController {
|
||||
},
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspace.id,
|
||||
WebhookEvent.PageRestored,
|
||||
{
|
||||
id: page.id,
|
||||
slugId: page.slugId,
|
||||
title: page.title,
|
||||
spaceId: page.spaceId,
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
);
|
||||
|
||||
return this.pageRepo.findById(pageIdDto.pageId, {
|
||||
includeHasChildren: true,
|
||||
});
|
||||
|
||||
@@ -55,6 +55,8 @@ import { markdownToHtml } from '@docmost/editor-ext';
|
||||
import { WatcherService } from '../../watcher/watcher.service';
|
||||
import { sql } from 'kysely';
|
||||
import { TransclusionService } from '../transclusion/transclusion.service';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@Injectable()
|
||||
export class PageService {
|
||||
@@ -73,6 +75,7 @@ export class PageService {
|
||||
private collaborationGateway: CollaborationGateway,
|
||||
private readonly watcherService: WatcherService,
|
||||
private readonly transclusionService: TransclusionService,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
async findById(
|
||||
@@ -156,9 +159,30 @@ export class PageService {
|
||||
this.logger.warn(`Failed to queue add-page-watchers: ${err.message}`),
|
||||
);
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
page.workspaceId,
|
||||
WebhookEvent.PageCreated,
|
||||
this.toWebhookPagePayload(page),
|
||||
);
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
private toWebhookPagePayload(page: Page) {
|
||||
return {
|
||||
id: page.id,
|
||||
slugId: page.slugId,
|
||||
title: page.title,
|
||||
icon: page.icon,
|
||||
parentPageId: page.parentPageId,
|
||||
spaceId: page.spaceId,
|
||||
workspaceId: page.workspaceId,
|
||||
creatorId: page.creatorId,
|
||||
createdAt: page.createdAt,
|
||||
updatedAt: page.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
async nextPagePosition(spaceId: string, parentPageId?: string) {
|
||||
let pagePosition: string;
|
||||
|
||||
@@ -245,13 +269,21 @@ export class PageService {
|
||||
);
|
||||
}
|
||||
|
||||
return await this.pageRepo.findById(page.id, {
|
||||
const updatedPage = await this.pageRepo.findById(page.id, {
|
||||
includeSpace: true,
|
||||
includeContent: true,
|
||||
includeCreator: true,
|
||||
includeLastUpdatedBy: true,
|
||||
includeContributors: true,
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
updatedPage.workspaceId,
|
||||
WebhookEvent.PageUpdated,
|
||||
this.toWebhookPagePayload(updatedPage),
|
||||
);
|
||||
|
||||
return updatedPage;
|
||||
}
|
||||
|
||||
async updatePageContent(
|
||||
@@ -487,6 +519,18 @@ export class PageService {
|
||||
}
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
rootPage.workspaceId,
|
||||
WebhookEvent.PageMoved,
|
||||
{
|
||||
id: rootPage.id,
|
||||
slugId: rootPage.slugId,
|
||||
fromSpaceId: rootPage.spaceId,
|
||||
toSpaceId: spaceId,
|
||||
workspaceId: rootPage.workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
return { childPageIds };
|
||||
}
|
||||
|
||||
@@ -1015,6 +1059,14 @@ export class PageService {
|
||||
pageIds: pageIds,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
for (const id of pageIds) {
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspaceId,
|
||||
WebhookEvent.PageDeleted,
|
||||
{ id, workspaceId },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import {
|
||||
AUDIT_SERVICE,
|
||||
IAuditService,
|
||||
} from '../../../integrations/audit/audit.service';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceService {
|
||||
@@ -41,6 +43,7 @@ export class SpaceService {
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
@InjectQueue(QueueName.ATTACHMENT_QUEUE) private attachmentQueue: Queue,
|
||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
async createSpace(
|
||||
@@ -85,6 +88,17 @@ export class SpaceService {
|
||||
},
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspaceId,
|
||||
WebhookEvent.SpaceCreated,
|
||||
{
|
||||
id: space.id,
|
||||
name: space.name,
|
||||
slug: space.slug,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
return { ...space, memberCount: 1 };
|
||||
}
|
||||
|
||||
@@ -244,6 +258,17 @@ export class SpaceService {
|
||||
spaceId: updateSpaceDto.spaceId,
|
||||
changes: { before, after },
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspaceId,
|
||||
WebhookEvent.SpaceUpdated,
|
||||
{
|
||||
id: updatedSpace.id,
|
||||
name: updatedSpace.name,
|
||||
slug: updatedSpace.slug,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return updatedSpace;
|
||||
@@ -289,5 +314,15 @@ export class SpaceService {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspaceId,
|
||||
WebhookEvent.SpaceDeleted,
|
||||
{
|
||||
id: spaceId,
|
||||
name: space.name,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ import {
|
||||
AUDIT_SERVICE,
|
||||
IAuditService,
|
||||
} from '../../../integrations/audit/audit.service';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceInvitationService {
|
||||
@@ -55,6 +57,7 @@ export class WorkspaceInvitationService {
|
||||
@InjectQueue(QueueName.BILLING_QUEUE) private billingQueue: Queue,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
async getInvitations(workspaceId: string, pagination: PaginationOptions) {
|
||||
@@ -304,6 +307,18 @@ export class WorkspaceInvitationService {
|
||||
return;
|
||||
}
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspace.id,
|
||||
WebhookEvent.UserCreated,
|
||||
{
|
||||
id: newUser.id,
|
||||
name: newUser.name,
|
||||
email: newUser.email,
|
||||
role: newUser.role,
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
);
|
||||
|
||||
// notify the inviter
|
||||
const invitedByUser = await this.userRepo.findById(
|
||||
invitation.invitedById,
|
||||
|
||||
@@ -48,6 +48,8 @@ import {
|
||||
AUDIT_SERVICE,
|
||||
IAuditService,
|
||||
} from '../../../integrations/audit/audit.service';
|
||||
import { WebhookDispatcher } from '@docmost/ee/webhook/services/webhook-dispatcher.service';
|
||||
import { WebhookEvent } from '@docmost/ee/webhook/constants';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceService {
|
||||
@@ -72,6 +74,7 @@ export class WorkspaceService {
|
||||
@InjectQueue(QueueName.AI_QUEUE) private aiQueue: Queue,
|
||||
@Inject(AUDIT_SERVICE) private readonly auditService: IAuditService,
|
||||
private userSessionRepo: UserSessionRepo,
|
||||
private readonly webhookDispatcher: WebhookDispatcher,
|
||||
) {}
|
||||
|
||||
async findById(workspaceId: string) {
|
||||
@@ -736,6 +739,17 @@ export class WorkspaceService {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.webhookDispatcher.dispatch(
|
||||
workspaceId,
|
||||
WebhookEvent.UserDeactivated,
|
||||
{
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async activateUser(
|
||||
|
||||
+1
-1
Submodule apps/server/src/ee updated: 5c4c9e9bf3...0defcae385
Reference in New Issue
Block a user