Feat: GDPRモードを追加した

Signed-off-by: mattyatea <mattyacocacora0@gmail.com>
This commit is contained in:
mattyatea 2023-12-31 18:19:59 +09:00
parent cb1586658e
commit 74e45b13eb
No known key found for this signature in database
GPG key ID: 068E54E2C33BEF9A
13 changed files with 83 additions and 8 deletions

1
locales/index.d.ts vendored
View file

@ -876,6 +876,7 @@ export interface Locale {
"on": string; "on": string;
"off": string; "off": string;
"emailRequiredForSignup": string; "emailRequiredForSignup": string;
"enableGDPRMode": string;
"unread": string; "unread": string;
"filter": string; "filter": string;
"controlPanel": string; "controlPanel": string;

View file

@ -873,6 +873,7 @@ itsOff: "オフになっています"
on: "オン" on: "オン"
off: "オフ" off: "オフ"
emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする"
enableGDPRMode: "GDPRモードを有効にする"
unread: "未読" unread: "未読"
filter: "フィルタ" filter: "フィルタ"
controlPanel: "コントロールパネル" controlPanel: "コントロールパネル"

View file

@ -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"`);
}
}

View file

@ -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"`);
}
}

View file

@ -158,6 +158,7 @@ export interface AdminEventTypes {
reporterId: MiUser['id'], reporterId: MiUser['id'],
comment: string; comment: string;
notes: any[]; notes: any[];
noteIds: string[];
}; };
} }
//#endregion //#endregion

View file

@ -4,12 +4,14 @@
*/ */
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js'; 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 { awaitAll } from '@/misc/prelude/await-all.js';
import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { UserEntityService } from './UserEntityService.js'; import { UserEntityService } from './UserEntityService.js';
@Injectable() @Injectable()
@ -18,7 +20,11 @@ export class AbuseUserReportEntityService {
@Inject(DI.abuseUserReportsRepository) @Inject(DI.abuseUserReportsRepository)
private abuseUserReportsRepository: AbuseUserReportsRepository, private abuseUserReportsRepository: AbuseUserReportsRepository,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private userEntityService: UserEntityService, private userEntityService: UserEntityService,
private noteEntityService: NoteEntityService,
private idService: IdService, private idService: IdService,
) { ) {
} }
@ -28,12 +34,25 @@ export class AbuseUserReportEntityService {
src: MiAbuseUserReport['id'] | MiAbuseUserReport, src: MiAbuseUserReport['id'] | MiAbuseUserReport,
) { ) {
const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); 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({ return await awaitAll({
id: report.id, id: report.id,
createdAt: this.idService.parse(report.id).date.toISOString(), createdAt: this.idService.parse(report.id).date.toISOString(),
comment: report.comment, comment: report.comment,
notes: report.notes, notes,
resolved: report.resolved, resolved: report.resolved,
reporterId: report.reporterId, reporterId: report.reporterId,
targetUserId: report.targetUserId, targetUserId: report.targetUserId,

View file

@ -65,6 +65,11 @@ export class MiAbuseUserReport {
}) })
public notes: any[]; public notes: any[];
@Column('jsonb', {
default: [],
})
public noteIds: string[] | null;
//#region Denormalized fields //#region Denormalized fields
@Index() @Index()
@Column('varchar', { @Column('varchar', {

View file

@ -541,4 +541,9 @@ export class MiMeta {
default: 0, default: 0,
}) })
public notesPerOneAd: number; public notesPerOneAd: number;
@Column('boolean', {
default: false,
})
public enableGDPRMode: boolean;
} }

View file

@ -416,6 +416,10 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: false, optional: false, nullable: false,
}, },
enableGDPRMode: {
type: 'boolean',
optional: false, nullable: false,
},
}, },
}, },
} as const; } as const;
@ -534,6 +538,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax, perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax,
perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax, perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax,
notesPerOneAd: instance.notesPerOneAd, notesPerOneAd: instance.notesPerOneAd,
enableGDPRMode: instance.enableGDPRMode,
}; };
}); });
} }

View file

@ -138,6 +138,7 @@ export const paramDef = {
type: 'string', type: 'string',
}, },
}, },
enableGDPRMode: { type: 'boolean' },
}, },
required: [], required: [],
} as const; } as const;
@ -209,6 +210,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.infoImageUrl !== undefined) { if (ps.infoImageUrl !== undefined) {
set.infoImageUrl = ps.infoImageUrl; set.infoImageUrl = ps.infoImageUrl;
} }
console.log(ps.enableGDPRMode);
if (ps.enableGDPRMode !== undefined) {
set.enableGDPRMode = ps.enableGDPRMode;
}
if (ps.notFoundImageUrl !== undefined) { if (ps.notFoundImageUrl !== undefined) {
set.notFoundImageUrl = ps.notFoundImageUrl; set.notFoundImageUrl = ps.notFoundImageUrl;

View file

@ -25,7 +25,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'write:report-abuse', kind: 'write:report-abuse',
description: 'User a report.', description: 'File a report.',
errors: { errors: {
noSuchUser: { noSuchUser: {
@ -91,9 +91,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
const notes = ps.noteIds ? await this.notesRepository.find({ 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({ const report = await this.abuseUserReportsRepository.insert({
id: this.idService.gen(), id: this.idService.gen(),
targetUserId: user.id, targetUserId: user.id,
@ -101,7 +101,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
reporterId: me.id, reporterId: me.id,
reporterHost: null, reporterHost: null,
comment: ps.comment, 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])); }).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0]));
// Publish event to moderators // Publish event to moderators
@ -115,6 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
reporterId: report.reporterId, reporterId: report.reporterId,
comment: report.comment, comment: report.comment,
notes: report.notes, notes: report.notes,
noteIds: report.noteIds ?? [],
}); });
} }
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();

View file

@ -24,7 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder v-if="report.notes.length !== 0" :class="$style.notes"> <MkFolder v-if="report.notes.length !== 0" :class="$style.notes">
<template #label>{{ i18n.ts.reportedNote }}</template> <template #label>{{ i18n.ts.reportedNote }}</template>
<div v-for="note in report.notes" :class="$style.notes"> <div v-for="note in report.notes" :class="$style.notes">
<MkNoteSimple :note="note"/> <MkNoteSimple v-if="note !== 'deleted'" :note="note"/>
<div v-else> note is deleted </div>
</div> </div>
</MkFolder> </MkFolder>
</div> </div>
@ -62,7 +63,7 @@ const props = defineProps<{
id: string; id: string;
createdAt:string; createdAt:string;
targetUserId:Misskey.entities.User['id']; targetUserId:Misskey.entities.User['id'];
targetUser:Misskey.entities.User & {createdAt:string;}; targetUser:Misskey.entities.User & { createdAt:string; };
reporter:Misskey.entities.User; reporter:Misskey.entities.User;
assignee:Misskey.entities.User['id']; assignee:Misskey.entities.User['id'];
comment:string; comment:string;
@ -109,6 +110,7 @@ function resolve() {
padding: 24px; padding: 24px;
border-right: solid 1px var(--divider); border-right: solid 1px var(--divider);
} }
.info { .info {
display: flex; display: flex;
box-sizing: border-box; box-sizing: border-box;

View file

@ -18,6 +18,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.emailRequiredForSignup }}</template> <template #label>{{ i18n.ts.emailRequiredForSignup }}</template>
</MkSwitch> </MkSwitch>
<MkSwitch v-model="enableGDPRMode">
<template #label>{{ i18n.ts.enableGDPRMode }}</template>
</MkSwitch>
<FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink>
<MkInput v-model="tosUrl" type="url"> <MkInput v-model="tosUrl" type="url">
@ -79,6 +83,7 @@ const hiddenTags = ref<string>('');
const preservedUsernames = ref<string>(''); const preservedUsernames = ref<string>('');
const tosUrl = ref<string | null>(null); const tosUrl = ref<string | null>(null);
const privacyPolicyUrl = ref<string | null>(null); const privacyPolicyUrl = ref<string | null>(null);
const enableGDPRMode = ref<boolean>(false);
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
@ -89,6 +94,7 @@ async function init() {
preservedUsernames.value = meta.preservedUsernames.join('\n'); preservedUsernames.value = meta.preservedUsernames.join('\n');
tosUrl.value = meta.tosUrl; tosUrl.value = meta.tosUrl;
privacyPolicyUrl.value = meta.privacyPolicyUrl; privacyPolicyUrl.value = meta.privacyPolicyUrl;
enableGDPRMode.value = meta.enableGDPRMode;
} }
function save() { function save() {
@ -100,6 +106,7 @@ function save() {
sensitiveWords: sensitiveWords.value.split('\n'), sensitiveWords: sensitiveWords.value.split('\n'),
hiddenTags: hiddenTags.value.split('\n'), hiddenTags: hiddenTags.value.split('\n'),
preservedUsernames: preservedUsernames.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'),
enableGDPRMode: enableGDPRMode.value,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });