feat: 通報を受けた際にメールまたはWebhookで通知を送出出来るようにする (#13758)
* feat: 通報を受けた際にメールまたはWebhookで通知を送出出来るようにする * モデログに対応&エンドポイントを単一オブジェクトでのサポートに変更(API経由で大量に作るシチュエーションもないと思うので) * fix spdx * fix migration * fix migration * fix models * add e2e webhook * tweak * fix modlog * fix bugs * add tests and fix bugs * add tests and fix bugs * add tests * fix path * regenerate locale * 混入除去 * 混入除去 * add abuseReportResolved * fix pnpm-lock.yaml * add abuseReportResolved test * fix bugs * fix ui * add tests * fix CHANGELOG.md * add tests * add RoleService.getModeratorIds tests * WebhookServiceをUserとSystemに分割 * fix CHANGELOG.md * fix test * insertOneを使う用に * fix * regenerate locales * revert version * separate webhook job queue * fix * 🎨 * Update QueueProcessorService.ts --------- Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
		| @@ -6,8 +6,13 @@ | ||||
| import { Module } from '@nestjs/common'; | ||||
|  | ||||
| import { CoreModule } from '@/core/CoreModule.js'; | ||||
| import * as ep___admin_meta from './endpoints/admin/meta.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_list from '@/server/api/endpoints/admin/abuse-report/notification-recipient/list.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_show from '@/server/api/endpoints/admin/abuse-report/notification-recipient/show.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_create from '@/server/api/endpoints/admin/abuse-report/notification-recipient/create.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_update from '@/server/api/endpoints/admin/abuse-report/notification-recipient/update.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_delete from '@/server/api/endpoints/admin/abuse-report/notification-recipient/delete.js'; | ||||
| import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; | ||||
| import * as ep___admin_meta from './endpoints/admin/meta.js'; | ||||
| import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; | ||||
| import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; | ||||
| import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js'; | ||||
| @@ -82,6 +87,11 @@ import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; | ||||
| import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; | ||||
| import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; | ||||
| import * as ep___admin_roles_users from './endpoints/admin/roles/users.js'; | ||||
| import * as ep___admin_systemWebhook_create from './endpoints/admin/system-webhook/create.js'; | ||||
| import * as ep___admin_systemWebhook_delete from './endpoints/admin/system-webhook/delete.js'; | ||||
| import * as ep___admin_systemWebhook_list from './endpoints/admin/system-webhook/list.js'; | ||||
| import * as ep___admin_systemWebhook_show from './endpoints/admin/system-webhook/show.js'; | ||||
| import * as ep___admin_systemWebhook_update from './endpoints/admin/system-webhook/update.js'; | ||||
| import * as ep___announcements from './endpoints/announcements.js'; | ||||
| import * as ep___announcements_show from './endpoints/announcements/show.js'; | ||||
| import * as ep___antennas_create from './endpoints/antennas/create.js'; | ||||
| @@ -381,6 +391,11 @@ import type { Provider } from '@nestjs/common'; | ||||
|  | ||||
| const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }; | ||||
| const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }; | ||||
| const $admin_abuseReport_notificationRecipient_list: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/list', useClass: ep___admin_abuseReport_notificationRecipient_list.default }; | ||||
| const $admin_abuseReport_notificationRecipient_show: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/show', useClass: ep___admin_abuseReport_notificationRecipient_show.default }; | ||||
| const $admin_abuseReport_notificationRecipient_create: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/create', useClass: ep___admin_abuseReport_notificationRecipient_create.default }; | ||||
| const $admin_abuseReport_notificationRecipient_update: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/update', useClass: ep___admin_abuseReport_notificationRecipient_update.default }; | ||||
| const $admin_abuseReport_notificationRecipient_delete: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/delete', useClass: ep___admin_abuseReport_notificationRecipient_delete.default }; | ||||
| const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }; | ||||
| const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }; | ||||
| const $admin_accounts_findByEmail: Provider = { provide: 'ep:admin/accounts/find-by-email', useClass: ep___admin_accounts_findByEmail.default }; | ||||
| @@ -455,6 +470,11 @@ const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useCla | ||||
| const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default }; | ||||
| const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default }; | ||||
| const $admin_roles_users: Provider = { provide: 'ep:admin/roles/users', useClass: ep___admin_roles_users.default }; | ||||
| const $admin_systemWebhook_create: Provider = { provide: 'ep:admin/system-webhook/create', useClass: ep___admin_systemWebhook_create.default }; | ||||
| const $admin_systemWebhook_delete: Provider = { provide: 'ep:admin/system-webhook/delete', useClass: ep___admin_systemWebhook_delete.default }; | ||||
| const $admin_systemWebhook_list: Provider = { provide: 'ep:admin/system-webhook/list', useClass: ep___admin_systemWebhook_list.default }; | ||||
| const $admin_systemWebhook_show: Provider = { provide: 'ep:admin/system-webhook/show', useClass: ep___admin_systemWebhook_show.default }; | ||||
| const $admin_systemWebhook_update: Provider = { provide: 'ep:admin/system-webhook/update', useClass: ep___admin_systemWebhook_update.default }; | ||||
| const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default }; | ||||
| const $announcements_show: Provider = { provide: 'ep:announcements/show', useClass: ep___announcements_show.default }; | ||||
| const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }; | ||||
| @@ -758,6 +778,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ | ||||
| 		ApiLoggerService, | ||||
| 		$admin_meta, | ||||
| 		$admin_abuseUserReports, | ||||
| 		$admin_abuseReport_notificationRecipient_list, | ||||
| 		$admin_abuseReport_notificationRecipient_show, | ||||
| 		$admin_abuseReport_notificationRecipient_create, | ||||
| 		$admin_abuseReport_notificationRecipient_update, | ||||
| 		$admin_abuseReport_notificationRecipient_delete, | ||||
| 		$admin_accounts_create, | ||||
| 		$admin_accounts_delete, | ||||
| 		$admin_accounts_findByEmail, | ||||
| @@ -832,6 +857,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ | ||||
| 		$admin_roles_unassign, | ||||
| 		$admin_roles_updateDefaultPolicies, | ||||
| 		$admin_roles_users, | ||||
| 		$admin_systemWebhook_create, | ||||
| 		$admin_systemWebhook_delete, | ||||
| 		$admin_systemWebhook_list, | ||||
| 		$admin_systemWebhook_show, | ||||
| 		$admin_systemWebhook_update, | ||||
| 		$announcements, | ||||
| 		$announcements_show, | ||||
| 		$antennas_create, | ||||
| @@ -1129,6 +1159,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ | ||||
| 	exports: [ | ||||
| 		$admin_meta, | ||||
| 		$admin_abuseUserReports, | ||||
| 		$admin_abuseReport_notificationRecipient_list, | ||||
| 		$admin_abuseReport_notificationRecipient_show, | ||||
| 		$admin_abuseReport_notificationRecipient_create, | ||||
| 		$admin_abuseReport_notificationRecipient_update, | ||||
| 		$admin_abuseReport_notificationRecipient_delete, | ||||
| 		$admin_accounts_create, | ||||
| 		$admin_accounts_delete, | ||||
| 		$admin_accounts_findByEmail, | ||||
| @@ -1203,6 +1238,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ | ||||
| 		$admin_roles_unassign, | ||||
| 		$admin_roles_updateDefaultPolicies, | ||||
| 		$admin_roles_users, | ||||
| 		$admin_systemWebhook_create, | ||||
| 		$admin_systemWebhook_delete, | ||||
| 		$admin_systemWebhook_list, | ||||
| 		$admin_systemWebhook_show, | ||||
| 		$admin_systemWebhook_update, | ||||
| 		$announcements, | ||||
| 		$announcements_show, | ||||
| 		$antennas_create, | ||||
|   | ||||
| @@ -6,8 +6,18 @@ | ||||
| import { permissions } from 'misskey-js'; | ||||
| import type { KeyOf, Schema } from '@/misc/json-schema.js'; | ||||
|  | ||||
| import * as ep___admin_meta from './endpoints/admin/meta.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_list | ||||
| 	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/list.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_show | ||||
| 	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/show.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_create | ||||
| 	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/create.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_update | ||||
| 	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/update.js'; | ||||
| import * as ep___admin_abuseReport_notificationRecipient_delete | ||||
| 	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/delete.js'; | ||||
| import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; | ||||
| import * as ep___admin_meta from './endpoints/admin/meta.js'; | ||||
| import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; | ||||
| import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; | ||||
| import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js'; | ||||
| @@ -44,7 +54,8 @@ import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-c | ||||
| import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; | ||||
| import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; | ||||
| import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; | ||||
| import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; | ||||
| import * as ep___admin_federation_refreshRemoteInstanceMetadata | ||||
| 	from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; | ||||
| import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; | ||||
| import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js'; | ||||
| import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; | ||||
| @@ -82,6 +93,11 @@ import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; | ||||
| import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; | ||||
| import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; | ||||
| import * as ep___admin_roles_users from './endpoints/admin/roles/users.js'; | ||||
| import * as ep___admin_systemWebhook_create from './endpoints/admin/system-webhook/create.js'; | ||||
| import * as ep___admin_systemWebhook_delete from './endpoints/admin/system-webhook/delete.js'; | ||||
| import * as ep___admin_systemWebhook_list from './endpoints/admin/system-webhook/list.js'; | ||||
| import * as ep___admin_systemWebhook_show from './endpoints/admin/system-webhook/show.js'; | ||||
| import * as ep___admin_systemWebhook_update from './endpoints/admin/system-webhook/update.js'; | ||||
| import * as ep___announcements from './endpoints/announcements.js'; | ||||
| import * as ep___announcements_show from './endpoints/announcements/show.js'; | ||||
| import * as ep___antennas_create from './endpoints/antennas/create.js'; | ||||
| @@ -379,6 +395,11 @@ import * as ep___reversi_verify from './endpoints/reversi/verify.js'; | ||||
| const eps = [ | ||||
| 	['admin/meta', ep___admin_meta], | ||||
| 	['admin/abuse-user-reports', ep___admin_abuseUserReports], | ||||
| 	['admin/abuse-report/notification-recipient/list', ep___admin_abuseReport_notificationRecipient_list], | ||||
| 	['admin/abuse-report/notification-recipient/show', ep___admin_abuseReport_notificationRecipient_show], | ||||
| 	['admin/abuse-report/notification-recipient/create', ep___admin_abuseReport_notificationRecipient_create], | ||||
| 	['admin/abuse-report/notification-recipient/update', ep___admin_abuseReport_notificationRecipient_update], | ||||
| 	['admin/abuse-report/notification-recipient/delete', ep___admin_abuseReport_notificationRecipient_delete], | ||||
| 	['admin/accounts/create', ep___admin_accounts_create], | ||||
| 	['admin/accounts/delete', ep___admin_accounts_delete], | ||||
| 	['admin/accounts/find-by-email', ep___admin_accounts_findByEmail], | ||||
| @@ -453,6 +474,11 @@ const eps = [ | ||||
| 	['admin/roles/unassign', ep___admin_roles_unassign], | ||||
| 	['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies], | ||||
| 	['admin/roles/users', ep___admin_roles_users], | ||||
| 	['admin/system-webhook/create', ep___admin_systemWebhook_create], | ||||
| 	['admin/system-webhook/delete', ep___admin_systemWebhook_delete], | ||||
| 	['admin/system-webhook/list', ep___admin_systemWebhook_list], | ||||
| 	['admin/system-webhook/show', ep___admin_systemWebhook_show], | ||||
| 	['admin/system-webhook/update', ep___admin_systemWebhook_update], | ||||
| 	['announcements', ep___announcements], | ||||
| 	['announcements/show', ep___announcements_show], | ||||
| 	['antennas/create', ep___antennas_create], | ||||
| @@ -873,8 +899,12 @@ export interface IEndpoint { | ||||
| const endpoints: IEndpoint[] = (eps as [string, any]).map(([name, ep]) => { | ||||
| 	return { | ||||
| 		name: name, | ||||
| 		get meta() { return ep.meta ?? {}; }, | ||||
| 		get params() { return ep.paramDef; }, | ||||
| 		get meta() { | ||||
| 			return ep.meta ?? {}; | ||||
| 		}, | ||||
| 		get params() { | ||||
| 			return ep.paramDef; | ||||
| 		}, | ||||
| 	}; | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,122 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { ApiError } from '@/server/api/error.js'; | ||||
| import { | ||||
| 	AbuseReportNotificationRecipientEntityService, | ||||
| } from '@/core/entities/AbuseReportNotificationRecipientEntityService.js'; | ||||
| import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import type { UserProfilesRepository } from '@/models/_.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'abuse-report', 'notification-recipient'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:abuse-report:notification-recipient', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 		ref: 'AbuseReportNotificationRecipient', | ||||
| 	}, | ||||
|  | ||||
| 	errors: { | ||||
| 		correlationCheckEmail: { | ||||
| 			message: 'If "method" is email, "userId" must be set.', | ||||
| 			code: 'CORRELATION_CHECK_EMAIL', | ||||
| 			id: '348bb8ae-575a-6fe9-4327-5811999def8f', | ||||
| 			httpStatusCode: 400, | ||||
| 		}, | ||||
| 		correlationCheckWebhook: { | ||||
| 			message: 'If "method" is webhook, "systemWebhookId" must be set.', | ||||
| 			code: 'CORRELATION_CHECK_WEBHOOK', | ||||
| 			id: 'b0c15051-de2d-29ef-260c-9585cddd701a', | ||||
| 			httpStatusCode: 400, | ||||
| 		}, | ||||
| 		emailAddressNotSet: { | ||||
| 			message: 'Email address is not set.', | ||||
| 			code: 'EMAIL_ADDRESS_NOT_SET', | ||||
| 			id: '7cc1d85e-2f58-fc31-b644-3de8d0d3421f', | ||||
| 			httpStatusCode: 400, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		isActive: { | ||||
| 			type: 'boolean', | ||||
| 		}, | ||||
| 		name: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 255, | ||||
| 		}, | ||||
| 		method: { | ||||
| 			type: 'string', | ||||
| 			enum: ['email', 'webhook'], | ||||
| 		}, | ||||
| 		userId: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 		systemWebhookId: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [ | ||||
| 		'isActive', | ||||
| 		'name', | ||||
| 		'method', | ||||
| 	], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		@Inject(DI.userProfilesRepository) | ||||
| 		private userProfilesRepository: UserProfilesRepository, | ||||
| 		private abuseReportNotificationService: AbuseReportNotificationService, | ||||
| 		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			if (ps.method === 'email') { | ||||
| 				const userProfile = await this.userProfilesRepository.findOneBy({ userId: ps.userId }); | ||||
| 				if (!ps.userId || !userProfile) { | ||||
| 					throw new ApiError(meta.errors.correlationCheckEmail); | ||||
| 				} | ||||
|  | ||||
| 				if (!userProfile.email || !userProfile.emailVerified) { | ||||
| 					throw new ApiError(meta.errors.emailAddressNotSet); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (ps.method === 'webhook' && !ps.systemWebhookId) { | ||||
| 				throw new ApiError(meta.errors.correlationCheckWebhook); | ||||
| 			} | ||||
|  | ||||
| 			const userId = ps.method === 'email' ? ps.userId : null; | ||||
| 			const systemWebhookId = ps.method === 'webhook' ? ps.systemWebhookId : null; | ||||
| 			const result = await this.abuseReportNotificationService.createRecipient( | ||||
| 				{ | ||||
| 					isActive: ps.isActive, | ||||
| 					name: ps.name, | ||||
| 					method: ps.method, | ||||
| 					userId: userId ?? null, | ||||
| 					systemWebhookId: systemWebhookId ?? null, | ||||
| 				}, | ||||
| 				me, | ||||
| 			); | ||||
|  | ||||
| 			return this.abuseReportNotificationRecipientEntityService.pack(result); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'abuse-report', 'notification-recipient'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:abuse-report:notification-recipient', | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		id: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [ | ||||
| 		'id', | ||||
| 	], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private abuseReportNotificationService: AbuseReportNotificationService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			await this.abuseReportNotificationService.deleteRecipient( | ||||
| 				ps.id, | ||||
| 				me, | ||||
| 			); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { | ||||
| 	AbuseReportNotificationRecipientEntityService, | ||||
| } from '@/core/entities/AbuseReportNotificationRecipientEntityService.js'; | ||||
| import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'abuse-report', 'notification-recipient'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'read:admin:abuse-report:notification-recipient', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'array', | ||||
| 		items: { | ||||
| 			type: 'object', | ||||
| 			ref: 'AbuseReportNotificationRecipient', | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		method: { | ||||
| 			type: 'array', | ||||
| 			items: { | ||||
| 				type: 'string', | ||||
| 				enum: ['email', 'webhook'], | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private abuseReportNotificationService: AbuseReportNotificationService, | ||||
| 		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps) => { | ||||
| 			const recipients = await this.abuseReportNotificationService.fetchRecipients({ method: ps.method }); | ||||
| 			return this.abuseReportNotificationRecipientEntityService.packMany(recipients); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { | ||||
| 	AbuseReportNotificationRecipientEntityService, | ||||
| } from '@/core/entities/AbuseReportNotificationRecipientEntityService.js'; | ||||
| import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js'; | ||||
| import { ApiError } from '@/server/api/error.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'abuse-report', 'notification-recipient'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'read:admin:abuse-report:notification-recipient', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 		ref: 'AbuseReportNotificationRecipient', | ||||
| 	}, | ||||
|  | ||||
| 	errors: { | ||||
| 		noSuchRecipient: { | ||||
| 			message: 'No such recipient.', | ||||
| 			code: 'NO_SUCH_RECIPIENT', | ||||
| 			id: '013de6a8-f757-04cb-4d73-cc2a7e3368e4', | ||||
| 			kind: 'server', | ||||
| 			httpStatusCode: 404, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		id: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: ['id'], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private abuseReportNotificationService: AbuseReportNotificationService, | ||||
| 		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps) => { | ||||
| 			const recipients = await this.abuseReportNotificationService.fetchRecipients({ ids: [ps.id] }); | ||||
| 			if (recipients.length === 0) { | ||||
| 				throw new ApiError(meta.errors.noSuchRecipient); | ||||
| 			} | ||||
|  | ||||
| 			return this.abuseReportNotificationRecipientEntityService.pack(recipients[0]); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,128 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { ApiError } from '@/server/api/error.js'; | ||||
| import { | ||||
| 	AbuseReportNotificationRecipientEntityService, | ||||
| } from '@/core/entities/AbuseReportNotificationRecipientEntityService.js'; | ||||
| import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import type { UserProfilesRepository } from '@/models/_.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'abuse-report', 'notification-recipient'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:abuse-report:notification-recipient', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 		ref: 'AbuseReportNotificationRecipient', | ||||
| 	}, | ||||
|  | ||||
| 	errors: { | ||||
| 		correlationCheckEmail: { | ||||
| 			message: 'If "method" is email, "userId" must be set.', | ||||
| 			code: 'CORRELATION_CHECK_EMAIL', | ||||
| 			id: '348bb8ae-575a-6fe9-4327-5811999def8f', | ||||
| 			httpStatusCode: 400, | ||||
| 		}, | ||||
| 		correlationCheckWebhook: { | ||||
| 			message: 'If "method" is webhook, "systemWebhookId" must be set.', | ||||
| 			code: 'CORRELATION_CHECK_WEBHOOK', | ||||
| 			id: 'b0c15051-de2d-29ef-260c-9585cddd701a', | ||||
| 			httpStatusCode: 400, | ||||
| 		}, | ||||
| 		emailAddressNotSet: { | ||||
| 			message: 'Email address is not set.', | ||||
| 			code: 'EMAIL_ADDRESS_NOT_SET', | ||||
| 			id: '7cc1d85e-2f58-fc31-b644-3de8d0d3421f', | ||||
| 			httpStatusCode: 400, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		id: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 		isActive: { | ||||
| 			type: 'boolean', | ||||
| 		}, | ||||
| 		name: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 255, | ||||
| 		}, | ||||
| 		method: { | ||||
| 			type: 'string', | ||||
| 			enum: ['email', 'webhook'], | ||||
| 		}, | ||||
| 		userId: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 		systemWebhookId: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [ | ||||
| 		'id', | ||||
| 		'isActive', | ||||
| 		'name', | ||||
| 		'method', | ||||
| 	], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		@Inject(DI.userProfilesRepository) | ||||
| 		private userProfilesRepository: UserProfilesRepository, | ||||
| 		private abuseReportNotificationService: AbuseReportNotificationService, | ||||
| 		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			if (ps.method === 'email') { | ||||
| 				const userProfile = await this.userProfilesRepository.findOneBy({ userId: ps.userId }); | ||||
| 				if (!ps.userId || !userProfile) { | ||||
| 					throw new ApiError(meta.errors.correlationCheckEmail); | ||||
| 				} | ||||
|  | ||||
| 				if (!userProfile.email || !userProfile.emailVerified) { | ||||
| 					throw new ApiError(meta.errors.emailAddressNotSet); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (ps.method === 'webhook' && !ps.systemWebhookId) { | ||||
| 				throw new ApiError(meta.errors.correlationCheckWebhook); | ||||
| 			} | ||||
|  | ||||
| 			const userId = ps.method === 'email' ? ps.userId : null; | ||||
| 			const systemWebhookId = ps.method === 'webhook' ? ps.systemWebhookId : null; | ||||
| 			const result = await this.abuseReportNotificationService.updateRecipient( | ||||
| 				{ | ||||
| 					id: ps.id, | ||||
| 					isActive: ps.isActive, | ||||
| 					name: ps.name, | ||||
| 					method: ps.method, | ||||
| 					userId: userId ?? null, | ||||
| 					systemWebhookId: systemWebhookId ?? null, | ||||
| 				}, | ||||
| 				me, | ||||
| 			); | ||||
|  | ||||
| 			return this.abuseReportNotificationRecipientEntityService.pack(result); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -5,7 +5,7 @@ | ||||
|  | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js'; | ||||
| import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, UserWebhookDeliverQueue, SystemWebhookDeliverQueue } from '@/core/QueueModule.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin'], | ||||
| @@ -53,7 +53,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 		@Inject('queue:inbox') public inboxQueue: InboxQueue, | ||||
| 		@Inject('queue:db') public dbQueue: DbQueue, | ||||
| 		@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue, | ||||
| 		@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, | ||||
| 		@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue, | ||||
| 		@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const deliverJobCounts = await this.deliverQueue.getJobCounts(); | ||||
|   | ||||
| @@ -5,12 +5,10 @@ | ||||
|  | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import type { UsersRepository, AbuseUserReportsRepository } from '@/models/_.js'; | ||||
| import { InstanceActorService } from '@/core/InstanceActorService.js'; | ||||
| import { QueueService } from '@/core/QueueService.js'; | ||||
| import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||
| import type { AbuseUserReportsRepository } from '@/models/_.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { ModerationLogService } from '@/core/ModerationLogService.js'; | ||||
| import { ApiError } from '@/server/api/error.js'; | ||||
| import { AbuseReportService } from '@/core/AbuseReportService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin'], | ||||
| @@ -18,6 +16,16 @@ export const meta = { | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	kind: 'write:admin:resolve-abuse-user-report', | ||||
|  | ||||
| 	errors: { | ||||
| 		noSuchAbuseReport: { | ||||
| 			message: 'No such abuse report.', | ||||
| 			code: 'NO_SUCH_ABUSE_REPORT', | ||||
| 			id: 'ac3794dd-2ce4-d878-e546-73c60c06b398', | ||||
| 			kind: 'server', | ||||
| 			httpStatusCode: 404, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| @@ -29,47 +37,20 @@ export const paramDef = { | ||||
| 	required: ['reportId'], | ||||
| } as const; | ||||
|  | ||||
| // TODO: ロジックをサービスに切り出す | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		@Inject(DI.usersRepository) | ||||
| 		private usersRepository: UsersRepository, | ||||
|  | ||||
| 		@Inject(DI.abuseUserReportsRepository) | ||||
| 		private abuseUserReportsRepository: AbuseUserReportsRepository, | ||||
|  | ||||
| 		private queueService: QueueService, | ||||
| 		private instanceActorService: InstanceActorService, | ||||
| 		private apRendererService: ApRendererService, | ||||
| 		private moderationLogService: ModerationLogService, | ||||
| 		private abuseReportService: AbuseReportService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId }); | ||||
|  | ||||
| 			if (report == null) { | ||||
| 				throw new Error('report not found'); | ||||
| 			if (!report) { | ||||
| 				throw new ApiError(meta.errors.noSuchAbuseReport); | ||||
| 			} | ||||
|  | ||||
| 			if (ps.forward && report.targetUserHost != null) { | ||||
| 				const actor = await this.instanceActorService.getInstanceActor(); | ||||
| 				const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId }); | ||||
|  | ||||
| 				this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox, false); | ||||
| 			} | ||||
|  | ||||
| 			await this.abuseUserReportsRepository.update(report.id, { | ||||
| 				resolved: true, | ||||
| 				assigneeId: me.id, | ||||
| 				forwarded: ps.forward && report.targetUserHost != null, | ||||
| 			}); | ||||
|  | ||||
| 			this.moderationLogService.log(me, 'resolveAbuseReport', { | ||||
| 				reportId: report.id, | ||||
| 				report: report, | ||||
| 				forwarded: ps.forward && report.targetUserHost != null, | ||||
| 			}); | ||||
| 			await this.abuseReportService.resolve([{ reportId: report.id, forward: ps.forward }], me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,85 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js'; | ||||
| import { systemWebhookEventTypes } from '@/models/SystemWebhook.js'; | ||||
| import { SystemWebhookService } from '@/core/SystemWebhookService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'system-webhook'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:system-webhook', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 		ref: 'SystemWebhook', | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		isActive: { | ||||
| 			type: 'boolean', | ||||
| 		}, | ||||
| 		name: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 255, | ||||
| 		}, | ||||
| 		on: { | ||||
| 			type: 'array', | ||||
| 			items: { | ||||
| 				type: 'string', | ||||
| 				enum: systemWebhookEventTypes, | ||||
| 			}, | ||||
| 		}, | ||||
| 		url: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 1024, | ||||
| 		}, | ||||
| 		secret: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 1024, | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [ | ||||
| 		'isActive', | ||||
| 		'name', | ||||
| 		'on', | ||||
| 		'url', | ||||
| 		'secret', | ||||
| 	], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private systemWebhookService: SystemWebhookService, | ||||
| 		private systemWebhookEntityService: SystemWebhookEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const result = await this.systemWebhookService.createSystemWebhook( | ||||
| 				{ | ||||
| 					isActive: ps.isActive, | ||||
| 					name: ps.name, | ||||
| 					on: ps.on, | ||||
| 					url: ps.url, | ||||
| 					secret: ps.secret, | ||||
| 				}, | ||||
| 				me, | ||||
| 			); | ||||
|  | ||||
| 			return this.systemWebhookEntityService.pack(result); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { SystemWebhookService } from '@/core/SystemWebhookService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'system-webhook'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:system-webhook', | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		id: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [ | ||||
| 		'id', | ||||
| 	], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private systemWebhookService: SystemWebhookService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			await this.systemWebhookService.deleteSystemWebhook( | ||||
| 				ps.id, | ||||
| 				me, | ||||
| 			); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js'; | ||||
| import { systemWebhookEventTypes } from '@/models/SystemWebhook.js'; | ||||
| import { SystemWebhookService } from '@/core/SystemWebhookService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'system-webhook'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:system-webhook', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'array', | ||||
| 		items: { | ||||
| 			type: 'object', | ||||
| 			ref: 'SystemWebhook', | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		isActive: { | ||||
| 			type: 'boolean', | ||||
| 		}, | ||||
| 		on: { | ||||
| 			type: 'array', | ||||
| 			items: { | ||||
| 				type: 'string', | ||||
| 				enum: systemWebhookEventTypes, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private systemWebhookService: SystemWebhookService, | ||||
| 		private systemWebhookEntityService: SystemWebhookEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps) => { | ||||
| 			const webhooks = await this.systemWebhookService.fetchSystemWebhooks({ | ||||
| 				isActive: ps.isActive, | ||||
| 				on: ps.on, | ||||
| 			}); | ||||
| 			return this.systemWebhookEntityService.packMany(webhooks); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js'; | ||||
| import { ApiError } from '@/server/api/error.js'; | ||||
| import { SystemWebhookService } from '@/core/SystemWebhookService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'system-webhook'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:system-webhook', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 		ref: 'SystemWebhook', | ||||
| 	}, | ||||
|  | ||||
| 	errors: { | ||||
| 		noSuchSystemWebhook: { | ||||
| 			message: 'No such SystemWebhook.', | ||||
| 			code: 'NO_SUCH_SYSTEM_WEBHOOK', | ||||
| 			id: '38dd1ffe-04b4-6ff5-d8ba-4e6a6ae22c9d', | ||||
| 			kind: 'server', | ||||
| 			httpStatusCode: 404, | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		id: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: ['id'], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private systemWebhookService: SystemWebhookService, | ||||
| 		private systemWebhookEntityService: SystemWebhookEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps) => { | ||||
| 			const webhooks = await this.systemWebhookService.fetchSystemWebhooks({ ids: [ps.id] }); | ||||
| 			if (webhooks.length === 0) { | ||||
| 				throw new ApiError(meta.errors.noSuchSystemWebhook); | ||||
| 			} | ||||
|  | ||||
| 			return this.systemWebhookEntityService.pack(webhooks[0]); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,91 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: syuilo and misskey-project | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js'; | ||||
| import { systemWebhookEventTypes } from '@/models/SystemWebhook.js'; | ||||
| import { SystemWebhookService } from '@/core/SystemWebhookService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['admin', 'system-webhook'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
| 	requireModerator: true, | ||||
| 	secure: true, | ||||
| 	kind: 'write:admin:system-webhook', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 		ref: 'SystemWebhook', | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		id: { | ||||
| 			type: 'string', | ||||
| 			format: 'misskey:id', | ||||
| 		}, | ||||
| 		isActive: { | ||||
| 			type: 'boolean', | ||||
| 		}, | ||||
| 		name: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 255, | ||||
| 		}, | ||||
| 		on: { | ||||
| 			type: 'array', | ||||
| 			items: { | ||||
| 				type: 'string', | ||||
| 				enum: systemWebhookEventTypes, | ||||
| 			}, | ||||
| 		}, | ||||
| 		url: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 1024, | ||||
| 		}, | ||||
| 		secret: { | ||||
| 			type: 'string', | ||||
| 			minLength: 1, | ||||
| 			maxLength: 1024, | ||||
| 		}, | ||||
| 	}, | ||||
| 	required: [ | ||||
| 		'id', | ||||
| 		'isActive', | ||||
| 		'name', | ||||
| 		'on', | ||||
| 		'url', | ||||
| 		'secret', | ||||
| 	], | ||||
| } as const; | ||||
|  | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		private systemWebhookService: SystemWebhookService, | ||||
| 		private systemWebhookEntityService: SystemWebhookEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const result = await this.systemWebhookService.updateSystemWebhook( | ||||
| 				{ | ||||
| 					id: ps.id, | ||||
| 					isActive: ps.isActive, | ||||
| 					name: ps.name, | ||||
| 					on: ps.on, | ||||
| 					url: ps.url, | ||||
| 					secret: ps.secret, | ||||
| 				}, | ||||
| 				me, | ||||
| 			); | ||||
|  | ||||
| 			return this.systemWebhookEntityService.pack(result); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -3,17 +3,11 @@ | ||||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
|  | ||||
| import sanitizeHtml from 'sanitize-html'; | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import type { AbuseUserReportsRepository } from '@/models/_.js'; | ||||
| import { IdService } from '@/core/IdService.js'; | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { GlobalEventService } from '@/core/GlobalEventService.js'; | ||||
| import { MetaService } from '@/core/MetaService.js'; | ||||
| import { EmailService } from '@/core/EmailService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { GetterService } from '@/server/api/GetterService.js'; | ||||
| import { RoleService } from '@/core/RoleService.js'; | ||||
| import { AbuseReportService } from '@/core/AbuseReportService.js'; | ||||
| import { ApiError } from '../../error.js'; | ||||
|  | ||||
| export const meta = { | ||||
| @@ -57,60 +51,32 @@ export const paramDef = { | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export | ||||
| 	constructor( | ||||
| 		@Inject(DI.abuseUserReportsRepository) | ||||
| 		private abuseUserReportsRepository: AbuseUserReportsRepository, | ||||
|  | ||||
| 		private idService: IdService, | ||||
| 		private metaService: MetaService, | ||||
| 		private emailService: EmailService, | ||||
| 		private getterService: GetterService, | ||||
| 		private roleService: RoleService, | ||||
| 		private globalEventService: GlobalEventService, | ||||
| 		private abuseReportService: AbuseReportService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			// Lookup user | ||||
| 			const user = await this.getterService.getUser(ps.userId).catch(err => { | ||||
| 			const targetUser = await this.getterService.getUser(ps.userId).catch(err => { | ||||
| 				if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); | ||||
| 				throw err; | ||||
| 			}); | ||||
|  | ||||
| 			if (user.id === me.id) { | ||||
| 			if (targetUser.id === me.id) { | ||||
| 				throw new ApiError(meta.errors.cannotReportYourself); | ||||
| 			} | ||||
|  | ||||
| 			if (await this.roleService.isAdministrator(user)) { | ||||
| 			if (await this.roleService.isAdministrator(targetUser)) { | ||||
| 				throw new ApiError(meta.errors.cannotReportAdmin); | ||||
| 			} | ||||
|  | ||||
| 			const report = await this.abuseUserReportsRepository.insertOne({ | ||||
| 				id: this.idService.gen(), | ||||
| 				targetUserId: user.id, | ||||
| 				targetUserHost: user.host, | ||||
| 			await this.abuseReportService.report([{ | ||||
| 				targetUserId: targetUser.id, | ||||
| 				targetUserHost: targetUser.host, | ||||
| 				reporterId: me.id, | ||||
| 				reporterHost: null, | ||||
| 				comment: ps.comment, | ||||
| 			}); | ||||
|  | ||||
| 			// Publish event to moderators | ||||
| 			setImmediate(async () => { | ||||
| 				const moderators = await this.roleService.getModerators(); | ||||
|  | ||||
| 				for (const moderator of moderators) { | ||||
| 					this.globalEventService.publishAdminStream(moderator.id, 'newAbuseUserReport', { | ||||
| 						id: report.id, | ||||
| 						targetUserId: report.targetUserId, | ||||
| 						reporterId: report.reporterId, | ||||
| 						comment: report.comment, | ||||
| 					}); | ||||
| 				} | ||||
|  | ||||
| 				const meta = await this.metaService.fetch(); | ||||
| 				if (meta.email) { | ||||
| 					this.emailService.sendEmail(meta.email, 'New abuse report', | ||||
| 						sanitizeHtml(ps.comment), | ||||
| 						sanitizeHtml(ps.comment)); | ||||
| 				} | ||||
| 			}); | ||||
| 			}]); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 おさむのひと
					おさむのひと