Feat: GDPRモードを追加した
Signed-off-by: mattyatea <mattyacocacora0@gmail.com>
This commit is contained in:
parent
cb1586658e
commit
74e45b13eb
1
locales/index.d.ts
vendored
1
locales/index.d.ts
vendored
|
@ -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;
|
||||||
|
|
|
@ -873,6 +873,7 @@ itsOff: "オフになっています"
|
||||||
on: "オン"
|
on: "オン"
|
||||||
off: "オフ"
|
off: "オフ"
|
||||||
emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする"
|
emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする"
|
||||||
|
enableGDPRMode: "GDPRモードを有効にする"
|
||||||
unread: "未読"
|
unread: "未読"
|
||||||
filter: "フィルタ"
|
filter: "フィルタ"
|
||||||
controlPanel: "コントロールパネル"
|
controlPanel: "コントロールパネル"
|
||||||
|
|
11
packages/backend/migration/1703704097603-GDPRMode.js
Normal file
11
packages/backend/migration/1703704097603-GDPRMode.js
Normal 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"`);
|
||||||
|
}
|
||||||
|
}
|
11
packages/backend/migration/1704005554275-abusenoteIds.js
Normal file
11
packages/backend/migration/1704005554275-abusenoteIds.js
Normal 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"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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', {
|
||||||
|
|
|
@ -541,4 +541,9 @@ export class MiMeta {
|
||||||
default: 0,
|
default: 0,
|
||||||
})
|
})
|
||||||
public notesPerOneAd: number;
|
public notesPerOneAd: number;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public enableGDPRMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue