refactor(backend): SystemWebhookで送信されるペイロードの型を追加 (#14980)

This commit is contained in:
おさむのひと 2024-11-19 10:41:39 +09:00 committed by GitHub
parent c271534aba
commit 7b9c884a5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 63 additions and 29 deletions

View file

@ -154,9 +154,9 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
const convertedReports = abuseReports.map(it => { const convertedReports = abuseReports.map(it => {
return { return {
...it, ...it,
reporter: usersMap.get(it.reporterId), reporter: usersMap.get(it.reporterId) ?? null,
targetUser: usersMap.get(it.targetUserId), targetUser: usersMap.get(it.targetUserId) ?? null,
assignee: it.assigneeId ? usersMap.get(it.assigneeId) : null, assignee: it.assigneeId ? (usersMap.get(it.assigneeId) ?? null) : null,
}; };
}); });

View file

@ -7,13 +7,15 @@ import { randomUUID } from 'node:crypto';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { IActivity } from '@/core/activitypub/type.js'; import type { IActivity } from '@/core/activitypub/type.js';
import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiDriveFile } from '@/models/DriveFile.js';
import type { MiWebhook, WebhookEventTypes, webhookEventTypes } from '@/models/Webhook.js'; import type { MiWebhook, WebhookEventTypes } from '@/models/Webhook.js';
import type { MiSystemWebhook, SystemWebhookEventType } from '@/models/SystemWebhook.js'; import type { MiSystemWebhook, SystemWebhookEventType } from '@/models/SystemWebhook.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js'; import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
import { type SystemWebhookPayload } from '@/core/SystemWebhookService.js';
import { type UserWebhookPayload } from './UserWebhookService.js';
import type { import type {
DbJobData, DbJobData,
DeliverJobData, DeliverJobData,
@ -30,12 +32,11 @@ import type {
ObjectStorageQueue, ObjectStorageQueue,
RelationshipQueue, RelationshipQueue,
SystemQueue, SystemQueue,
UserWebhookDeliverQueue,
SystemWebhookDeliverQueue, SystemWebhookDeliverQueue,
UserWebhookDeliverQueue,
} from './QueueModule.js'; } from './QueueModule.js';
import type httpSignature from '@peertube/http-signature'; import type httpSignature from '@peertube/http-signature';
import type * as Bull from 'bullmq'; import type * as Bull from 'bullmq';
import { type UserWebhookPayload } from './UserWebhookService.js';
@Injectable() @Injectable()
export class QueueService { export class QueueService {
@ -501,10 +502,10 @@ export class QueueService {
* @see SystemWebhookDeliverProcessorService * @see SystemWebhookDeliverProcessorService
*/ */
@bindThis @bindThis
public systemWebhookDeliver( public systemWebhookDeliver<T extends SystemWebhookEventType>(
webhook: MiSystemWebhook, webhook: MiSystemWebhook,
type: SystemWebhookEventType, type: T,
content: unknown, content: SystemWebhookPayload<T>,
opts?: { attempts?: number }, opts?: { attempts?: number },
) { ) {
const data: SystemWebhookDeliverJobData = { const data: SystemWebhookDeliverJobData = {

View file

@ -15,8 +15,39 @@ import { QueueService } from '@/core/QueueService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
import { Packed } from '@/misc/json-schema.js';
import { AbuseReportResolveType } from '@/models/AbuseUserReport.js';
import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
import type { OnApplicationShutdown } from '@nestjs/common'; import type { OnApplicationShutdown } from '@nestjs/common';
export type AbuseReportPayload = {
id: string;
targetUserId: string;
targetUser: Packed<'UserLite'> | null;
targetUserHost: string | null;
reporterId: string;
reporter: Packed<'UserLite'> | null;
reporterHost: string | null;
assigneeId: string | null;
assignee: Packed<'UserLite'> | null;
resolved: boolean;
forwarded: boolean;
comment: string;
moderationNote: string;
resolvedAs: AbuseReportResolveType | null;
};
export type InactiveModeratorsWarningPayload = {
remainingTime: ModeratorInactivityRemainingTime;
};
export type SystemWebhookPayload<T extends SystemWebhookEventType> =
T extends 'abuseReport' | 'abuseReportResolved' ? AbuseReportPayload :
T extends 'userCreated' ? Packed<'UserLite'> :
T extends 'inactiveModeratorsWarning' ? InactiveModeratorsWarningPayload :
T extends 'inactiveModeratorsInvitationOnlyChanged' ? Record<string, never> :
never;
@Injectable() @Injectable()
export class SystemWebhookService implements OnApplicationShutdown { export class SystemWebhookService implements OnApplicationShutdown {
private logger: Logger; private logger: Logger;
@ -168,7 +199,7 @@ export class SystemWebhookService implements OnApplicationShutdown {
public async enqueueSystemWebhook<T extends SystemWebhookEventType>( public async enqueueSystemWebhook<T extends SystemWebhookEventType>(
webhook: MiSystemWebhook | MiSystemWebhook['id'], webhook: MiSystemWebhook | MiSystemWebhook['id'],
type: T, type: T,
content: unknown, content: SystemWebhookPayload<T>,
) { ) {
const webhookEntity = typeof webhook === 'string' const webhookEntity = typeof webhook === 'string'
? (await this.fetchActiveSystemWebhooks()).find(a => a.id === webhook) ? (await this.fetchActiveSystemWebhooks()).find(a => a.id === webhook)

View file

@ -7,7 +7,7 @@ import { Injectable } from '@nestjs/common';
import { MiAbuseUserReport, MiNote, MiUser, MiWebhook } from '@/models/_.js'; import { MiAbuseUserReport, MiNote, MiUser, MiWebhook } from '@/models/_.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWebhook.js'; import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWebhook.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js'; import { AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js';
import { Packed } from '@/misc/json-schema.js'; import { Packed } from '@/misc/json-schema.js';
import { type WebhookEventTypes } from '@/models/Webhook.js'; import { type WebhookEventTypes } from '@/models/Webhook.js';
import { type UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js'; import { type UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
@ -16,13 +16,7 @@ import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModera
const oneDayMillis = 24 * 60 * 60 * 1000; const oneDayMillis = 24 * 60 * 60 * 1000;
type AbuseUserReportDto = Omit<MiAbuseUserReport, 'targetUser' | 'reporter' | 'assignee'> & { function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseReportPayload {
targetUser: Packed<'UserLite'> | null,
reporter: Packed<'UserLite'> | null,
assignee: Packed<'UserLite'> | null,
};
function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseUserReportDto {
const result: MiAbuseUserReport = { const result: MiAbuseUserReport = {
id: 'dummy-abuse-report1', id: 'dummy-abuse-report1',
targetUserId: 'dummy-target-user', targetUserId: 'dummy-target-user',
@ -389,7 +383,8 @@ export class WebhookTestService {
break; break;
} }
// まだ実装されていない (#9485) // まだ実装されていない (#9485)
case 'reaction': return; case 'reaction':
return;
default: { default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustiveAssertion: never = params.type; const _exhaustiveAssertion: never = params.type;
@ -407,10 +402,10 @@ export class WebhookTestService {
* - on * - on
*/ */
@bindThis @bindThis
public async testSystemWebhook( public async testSystemWebhook<T extends SystemWebhookEventType>(
params: { params: {
webhookId: MiSystemWebhook['id'], webhookId: MiSystemWebhook['id'],
type: SystemWebhookEventType, type: T,
override?: Partial<Omit<MiSystemWebhook, 'id'>>, override?: Partial<Omit<MiSystemWebhook, 'id'>>,
}, },
) { ) {
@ -420,7 +415,7 @@ export class WebhookTestService {
} }
const webhook = webhooks[0]; const webhook = webhooks[0];
const send = (contents: unknown) => { const send = <U extends SystemWebhookEventType>(type: U, contents: SystemWebhookPayload<U>) => {
const merged = { const merged = {
...webhook, ...webhook,
...params.override, ...params.override,
@ -428,12 +423,12 @@ export class WebhookTestService {
// テスト目的なのでSystemWebhookServiceの機能を経由せず直接キューに追加するチェック処理などをスキップする意図. // テスト目的なのでSystemWebhookServiceの機能を経由せず直接キューに追加するチェック処理などをスキップする意図.
// また、Jobの試行回数も1回だけ. // また、Jobの試行回数も1回だけ.
this.queueService.systemWebhookDeliver(merged, params.type, contents, { attempts: 1 }); this.queueService.systemWebhookDeliver(merged, type, contents, { attempts: 1 });
}; };
switch (params.type) { switch (params.type) {
case 'abuseReport': { case 'abuseReport': {
send(generateAbuseReport({ send('abuseReport', generateAbuseReport({
targetUserId: dummyUser1.id, targetUserId: dummyUser1.id,
targetUser: dummyUser1, targetUser: dummyUser1,
reporterId: dummyUser2.id, reporterId: dummyUser2.id,
@ -442,7 +437,7 @@ export class WebhookTestService {
break; break;
} }
case 'abuseReportResolved': { case 'abuseReportResolved': {
send(generateAbuseReport({ send('abuseReportResolved', generateAbuseReport({
targetUserId: dummyUser1.id, targetUserId: dummyUser1.id,
targetUser: dummyUser1, targetUser: dummyUser1,
reporterId: dummyUser2.id, reporterId: dummyUser2.id,
@ -454,7 +449,7 @@ export class WebhookTestService {
break; break;
} }
case 'userCreated': { case 'userCreated': {
send(toPackedUserLite(dummyUser1)); send('userCreated', toPackedUserLite(dummyUser1));
break; break;
} }
case 'inactiveModeratorsWarning': { case 'inactiveModeratorsWarning': {
@ -464,15 +459,20 @@ export class WebhookTestService {
asHours: 24, asHours: 24,
}; };
send({ send('inactiveModeratorsWarning', {
remainingTime: dummyTime, remainingTime: dummyTime,
}); });
break; break;
} }
case 'inactiveModeratorsInvitationOnlyChanged': { case 'inactiveModeratorsInvitationOnlyChanged': {
send({}); send('inactiveModeratorsInvitationOnlyChanged', {});
break; break;
} }
default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exhaustiveAssertion: never = params.type;
return;
}
} }
} }
} }

View file

@ -7,6 +7,8 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ
import { id } from './util/id.js'; import { id } from './util/id.js';
import { MiUser } from './User.js'; import { MiUser } from './User.js';
export type AbuseReportResolveType = 'accept' | 'reject';
@Entity('abuse_user_report') @Entity('abuse_user_report')
export class MiAbuseUserReport { export class MiAbuseUserReport {
@PrimaryColumn(id()) @PrimaryColumn(id())
@ -76,7 +78,7 @@ export class MiAbuseUserReport {
@Column('varchar', { @Column('varchar', {
length: 128, nullable: true, length: 128, nullable: true,
}) })
public resolvedAs: 'accept' | 'reject' | null; public resolvedAs: AbuseReportResolveType | null;
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()