From 74e45b13eba75b3150f9aeb8d5a1f04e0afa6ac2 Mon Sep 17 00:00:00 2001 From: mattyatea Date: Sun, 31 Dec 2023 18:19:59 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20GDPR=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../migration/1703704097603-GDPRMode.js | 11 +++++++++ .../migration/1704005554275-abusenoteIds.js | 11 +++++++++ .../backend/src/core/GlobalEventService.ts | 1 + .../entities/AbuseUserReportEntityService.ts | 23 +++++++++++++++++-- .../backend/src/models/AbuseUserReport.ts | 5 ++++ packages/backend/src/models/Meta.ts | 5 ++++ .../src/server/api/endpoints/admin/meta.ts | 5 ++++ .../server/api/endpoints/admin/update-meta.ts | 5 ++++ .../api/endpoints/users/report-abuse.ts | 10 ++++---- .../frontend/src/components/MkAbuseReport.vue | 6 +++-- .../frontend/src/pages/admin/moderation.vue | 7 ++++++ 13 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 packages/backend/migration/1703704097603-GDPRMode.js create mode 100644 packages/backend/migration/1704005554275-abusenoteIds.js diff --git a/locales/index.d.ts b/locales/index.d.ts index 56792f856a..669f8b48e9 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -876,6 +876,7 @@ export interface Locale { "on": string; "off": string; "emailRequiredForSignup": string; + "enableGDPRMode": string; "unread": string; "filter": string; "controlPanel": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4169b42be0..1d7607b280 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -873,6 +873,7 @@ itsOff: "オフになっています" on: "オン" off: "オフ" emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" +enableGDPRMode: "GDPRモードを有効にする" unread: "未読" filter: "フィルタ" controlPanel: "コントロールパネル" diff --git a/packages/backend/migration/1703704097603-GDPRMode.js b/packages/backend/migration/1703704097603-GDPRMode.js new file mode 100644 index 0000000000..86a7e5d85f --- /dev/null +++ b/packages/backend/migration/1703704097603-GDPRMode.js @@ -0,0 +1,11 @@ +export class GDPRMode1703704097603 { + name = 'GDPRMode1703704097603' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "enableGDPRMode" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) {; + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGDPRMode"`); + } +} diff --git a/packages/backend/migration/1704005554275-abusenoteIds.js b/packages/backend/migration/1704005554275-abusenoteIds.js new file mode 100644 index 0000000000..e52f8850b7 --- /dev/null +++ b/packages/backend/migration/1704005554275-abusenoteIds.js @@ -0,0 +1,11 @@ +export class AbusenoteId1704005554275 { + name = 'AbusenoteId1704005554275' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "noteIds" jsonb NOT NULL DEFAULT '[]'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "noteIds"`); + } +} diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index f16c18c2d6..800ce47b1f 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -158,6 +158,7 @@ export interface AdminEventTypes { reporterId: MiUser['id'], comment: string; notes: any[]; + noteIds: string[]; }; } //#endregion diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 627dddd3bc..23a513dd76 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -4,12 +4,14 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AbuseUserReportsRepository } from '@/models/_.js'; +import type { AbuseUserReportsRepository, NotesRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -18,7 +20,11 @@ export class AbuseUserReportEntityService { @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: AbuseUserReportsRepository, + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, private idService: IdService, ) { } @@ -28,12 +34,25 @@ export class AbuseUserReportEntityService { src: MiAbuseUserReport['id'] | MiAbuseUserReport, ) { const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); + const notes = (report.notes.length === 0) ? report.notes : []; + if (report.noteIds && report.noteIds.length > 0) { + for (const x of report.noteIds) { + const exists = await this.notesRepository.countBy({ id: x }); + if (exists === 0) { + notes.push('deleted'); + continue; + } + notes.push(await this.noteEntityService.pack(x)); + } + } + + console.log(report.notes.length, null, notes); return await awaitAll({ id: report.id, createdAt: this.idService.parse(report.id).date.toISOString(), comment: report.comment, - notes: report.notes, + notes, resolved: report.resolved, reporterId: report.reporterId, targetUserId: report.targetUserId, diff --git a/packages/backend/src/models/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts index 995fbc4018..a30cdbada5 100644 --- a/packages/backend/src/models/AbuseUserReport.ts +++ b/packages/backend/src/models/AbuseUserReport.ts @@ -65,6 +65,11 @@ export class MiAbuseUserReport { }) public notes: any[]; + @Column('jsonb', { + default: [], + }) + public noteIds: string[] | null; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 84ca762492..818557dab3 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -541,4 +541,9 @@ export class MiMeta { default: 0, }) public notesPerOneAd: number; + + @Column('boolean', { + default: false, + }) + public enableGDPRMode: boolean; } diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index febc4ab1b1..4ee549bf1f 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -416,6 +416,10 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + enableGDPRMode: { + type: 'boolean', + optional: false, nullable: false, + }, }, }, } as const; @@ -534,6 +538,7 @@ export default class extends Endpoint { // eslint- perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax, perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax, notesPerOneAd: instance.notesPerOneAd, + enableGDPRMode: instance.enableGDPRMode, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 5a215696fb..dc0001a9d9 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -138,6 +138,7 @@ export const paramDef = { type: 'string', }, }, + enableGDPRMode: { type: 'boolean' }, }, required: [], } as const; @@ -209,6 +210,10 @@ export default class extends Endpoint { // eslint- if (ps.infoImageUrl !== undefined) { set.infoImageUrl = ps.infoImageUrl; } + console.log(ps.enableGDPRMode); + if (ps.enableGDPRMode !== undefined) { + set.enableGDPRMode = ps.enableGDPRMode; + } if (ps.notFoundImageUrl !== undefined) { set.notFoundImageUrl = ps.notFoundImageUrl; diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index a7edee9e03..5769f03b2f 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -25,7 +25,7 @@ export const meta = { requireCredential: true, kind: 'write:report-abuse', - description: 'User a report.', + description: 'File a report.', errors: { noSuchUser: { @@ -91,9 +91,9 @@ export default class extends Endpoint { // eslint- } const notes = ps.noteIds ? await this.notesRepository.find({ - where: { id: In(ps.noteIds) }, + where: { id: In(ps.noteIds), userId: user.id }, }) : []; - const filteredNotes = notes.filter(note => note.userId === user.id); + const report = await this.abuseUserReportsRepository.insert({ id: this.idService.gen(), targetUserId: user.id, @@ -101,7 +101,8 @@ export default class extends Endpoint { // eslint- reporterId: me.id, reporterHost: null, comment: ps.comment, - notes: ps.noteIds ? await this.noteEntityService.packMany(filteredNotes) : [], + notes: (ps.noteIds && !((await this.metaService.fetch()).enableGDPRMode)) ? await this.noteEntityService.packMany(notes) : [], + noteIds: (ps.noteIds && (await this.metaService.fetch()).enableGDPRMode) ? ps.noteIds : [], }).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0])); // Publish event to moderators @@ -115,6 +116,7 @@ export default class extends Endpoint { // eslint- reporterId: report.reporterId, comment: report.comment, notes: report.notes, + noteIds: report.noteIds ?? [], }); } const meta = await this.metaService.fetch(); diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index 4c8a334b23..abf7a86256 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -24,7 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only
- + +
note is deleted
@@ -62,7 +63,7 @@ const props = defineProps<{ id: string; createdAt:string; targetUserId:Misskey.entities.User['id']; - targetUser:Misskey.entities.User & {createdAt:string;}; + targetUser:Misskey.entities.User & { createdAt:string; }; reporter:Misskey.entities.User; assignee:Misskey.entities.User['id']; comment:string; @@ -109,6 +110,7 @@ function resolve() { padding: 24px; border-right: solid 1px var(--divider); } + .info { display: flex; box-sizing: border-box; diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index f6c0b29403..e61a476373 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -18,6 +18,10 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + {{ i18n.ts.serverRules }} @@ -79,6 +83,7 @@ const hiddenTags = ref(''); const preservedUsernames = ref(''); const tosUrl = ref(null); const privacyPolicyUrl = ref(null); +const enableGDPRMode = ref(false); async function init() { const meta = await os.api('admin/meta'); @@ -89,6 +94,7 @@ async function init() { preservedUsernames.value = meta.preservedUsernames.join('\n'); tosUrl.value = meta.tosUrl; privacyPolicyUrl.value = meta.privacyPolicyUrl; + enableGDPRMode.value = meta.enableGDPRMode; } function save() { @@ -100,6 +106,7 @@ function save() { sensitiveWords: sensitiveWords.value.split('\n'), hiddenTags: hiddenTags.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'), + enableGDPRMode: enableGDPRMode.value, }).then(() => { fetchInstance(); });