From 87f61e714ad3b17856a6a5ac66051707badb3bd0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 19 Oct 2020 19:29:04 +0900 Subject: Resolve #6087 --- src/client/components/abuse-report-window.vue | 85 +++++++++++ src/client/components/note.vue | 17 ++- src/client/components/page-window.vue | 8 +- src/client/components/sidebar.vue | 7 +- src/client/components/ui/window.vue | 9 +- src/client/os.ts | 6 +- src/client/pages/instance/abuses.vue | 163 +++++++++++++++++++++ src/client/router.ts | 1 + src/client/scripts/get-user-menu.ts | 14 +- src/models/entities/abuse-user-report.ts | 41 +++++- src/models/repositories/abuse-user-report.ts | 9 +- src/remote/activitypub/kernel/flag/index.ts | 4 +- .../api/endpoints/admin/abuse-user-reports.ts | 38 +++++ .../endpoints/admin/remove-abuse-user-report.ts | 27 ---- .../endpoints/admin/resolve-abuse-user-report.ts | 30 ++++ src/server/api/endpoints/users/report-abuse.ts | 10 +- 16 files changed, 421 insertions(+), 48 deletions(-) create mode 100644 src/client/components/abuse-report-window.vue create mode 100644 src/client/pages/instance/abuses.vue delete mode 100644 src/server/api/endpoints/admin/remove-abuse-user-report.ts create mode 100644 src/server/api/endpoints/admin/resolve-abuse-user-report.ts (limited to 'src') diff --git a/src/client/components/abuse-report-window.vue b/src/client/components/abuse-report-window.vue new file mode 100644 index 0000000000..1d87cb1802 --- /dev/null +++ b/src/client/components/abuse-report-window.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/src/client/components/note.vue b/src/client/components/note.vue index cb364a04c9..85bdb9c6fb 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -101,7 +101,7 @@ + + diff --git a/src/client/components/sidebar.vue b/src/client/components/sidebar.vue index 7548b136ea..383378241b 100644 --- a/src/client/components/sidebar.vue +++ b/src/client/components/sidebar.vue @@ -46,7 +46,7 @@ + + diff --git a/src/client/router.ts b/src/client/router.ts index fc67f6ecfd..c9c7a32835 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -86,6 +86,7 @@ export const router = createRouter({ { path: '/instance/federation', component: page('instance/federation') }, { path: '/instance/relays', component: page('instance/relays') }, { path: '/instance/announcements', component: page('instance/announcements') }, + { path: '/instance/abuses', component: page('instance/abuses') }, { path: '/notes/:note', name: 'note', component: page('note') }, { path: '/tags/:tag', component: page('tag') }, { path: '/auth/:token', component: page('auth') }, diff --git a/src/client/scripts/get-user-menu.ts b/src/client/scripts/get-user-menu.ts index 63c3ae43b6..cace2e1425 100644 --- a/src/client/scripts/get-user-menu.ts +++ b/src/client/scripts/get-user-menu.ts @@ -1,4 +1,4 @@ -import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers, faMicrophoneSlash, faPlug } from '@fortawesome/free-solid-svg-icons'; +import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers, faMicrophoneSlash, faPlug, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; import { faSnowflake, faEnvelope } from '@fortawesome/free-regular-svg-icons'; import { i18n } from '@/i18n'; import copyToClipboard from '@/scripts/copy-to-clipboard'; @@ -102,6 +102,12 @@ export function getUserMenu(user) { }); } + async function reportAbuse() { + os.popup(await import('@/components/abuse-report-window.vue'), { + user: user, + }, {}, 'closed'); + } + async function getConfirmed(text: string): Promise { const confirm = await os.dialog({ type: 'warning', @@ -157,6 +163,12 @@ export function getUserMenu(user) { action: toggleBlock }]); + menu = menu.concat([null, { + icon: faExclamationCircle, + text: i18n.global.t('reportAbuse'), + action: reportAbuse + }]); + if (store.getters.isSignedIn && (store.state.i.isAdmin || store.state.i.isModerator)) { menu = menu.concat([null, { icon: faMicrophoneSlash, diff --git a/src/models/entities/abuse-user-report.ts b/src/models/entities/abuse-user-report.ts index 43ab56023a..c0cff139f6 100644 --- a/src/models/entities/abuse-user-report.ts +++ b/src/models/entities/abuse-user-report.ts @@ -3,7 +3,6 @@ import { User } from './user'; import { id } from '../id'; @Entity() -@Index(['userId', 'reporterId'], { unique: true }) export class AbuseUserReport { @PrimaryColumn(id()) public id: string; @@ -16,13 +15,13 @@ export class AbuseUserReport { @Index() @Column(id()) - public userId: User['id']; + public targetUserId: User['id']; @ManyToOne(type => User, { onDelete: 'CASCADE' }) @JoinColumn() - public user: User | null; + public targetUser: User | null; @Index() @Column(id()) @@ -34,8 +33,42 @@ export class AbuseUserReport { @JoinColumn() public reporter: User | null; + @Column({ + ...id(), + nullable: true + }) + public assigneeId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public assignee: User | null; + + @Index() + @Column('boolean', { + default: false + }) + public resolved: boolean; + @Column('varchar', { - length: 512, + length: 2048, }) public comment: string; + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public targetUserHost: string | null; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public reporterHost: string | null; + //#endregion } diff --git a/src/models/repositories/abuse-user-report.ts b/src/models/repositories/abuse-user-report.ts index bff64c770c..dbdaa5ee15 100644 --- a/src/models/repositories/abuse-user-report.ts +++ b/src/models/repositories/abuse-user-report.ts @@ -15,14 +15,19 @@ export class AbuseUserReportRepository extends Repository { id: report.id, createdAt: report.createdAt, comment: report.comment, + resolved: report.resolved, reporterId: report.reporterId, - userId: report.userId, + targetUserId: report.targetUserId, + assigneeId: report.assigneeId, reporter: Users.pack(report.reporter || report.reporterId, null, { detail: true }), - user: Users.pack(report.user || report.userId, null, { + targetUser: Users.pack(report.targetUser || report.targetUserId, null, { detail: true }), + assignee: report.assigneeId ? Users.pack(report.assignee || report.assigneeId, null, { + detail: true + }) : null, }); } diff --git a/src/remote/activitypub/kernel/flag/index.ts b/src/remote/activitypub/kernel/flag/index.ts index 9b3065b112..46ea789b4b 100644 --- a/src/remote/activitypub/kernel/flag/index.ts +++ b/src/remote/activitypub/kernel/flag/index.ts @@ -19,8 +19,10 @@ export default async (actor: IRemoteUser, activity: IFlag): Promise => { await AbuseUserReports.insert({ id: genId(), createdAt: new Date(), - userId: users[0].id, + targetUserId: users[0].id, + targetUserHost: users[0].host, reporterId: actor.id, + reporterHost: actor.host, comment: `${activity.content}\n${JSON.stringify(uris, null, 2)}` }); diff --git a/src/server/api/endpoints/admin/abuse-user-reports.ts b/src/server/api/endpoints/admin/abuse-user-reports.ts index d5a52184d1..6a7f380e16 100644 --- a/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -23,12 +23,50 @@ export const meta = { untilId: { validator: $.optional.type(ID), }, + + state: { + validator: $.optional.nullable.str, + default: null, + }, + + reporterOrigin: { + validator: $.optional.str.or([ + 'combined', + 'local', + 'remote', + ]), + default: 'combined' + }, + + targetUserOrigin: { + validator: $.optional.str.or([ + 'combined', + 'local', + 'remote', + ]), + default: 'combined' + }, } }; export default define(meta, async (ps) => { const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); + switch (ps.state) { + case 'resolved': query.andWhere('report.resolved = TRUE'); break; + case 'unresolved': query.andWhere('report.resolved = FALSE'); break; + } + + switch (ps.reporterOrigin) { + case 'local': query.andWhere('report.reporterHost IS NULL'); break; + case 'remote': query.andWhere('report.reporterHost IS NOT NULL'); break; + } + + switch (ps.targetUserOrigin) { + case 'local': query.andWhere('report.targetUserHost IS NULL'); break; + case 'remote': query.andWhere('report.targetUserHost IS NOT NULL'); break; + } + const reports = await query.take(ps.limit!).getMany(); return await AbuseUserReports.packMany(reports); diff --git a/src/server/api/endpoints/admin/remove-abuse-user-report.ts b/src/server/api/endpoints/admin/remove-abuse-user-report.ts deleted file mode 100644 index 150de5f5d4..0000000000 --- a/src/server/api/endpoints/admin/remove-abuse-user-report.ts +++ /dev/null @@ -1,27 +0,0 @@ -import $ from 'cafy'; -import { ID } from '../../../../misc/cafy-id'; -import define from '../../define'; -import { AbuseUserReports } from '../../../../models'; - -export const meta = { - tags: ['admin'], - - requireCredential: true as const, - requireModerator: true, - - params: { - reportId: { - validator: $.type(ID), - }, - } -}; - -export default define(meta, async (ps) => { - const report = await AbuseUserReports.findOne(ps.reportId); - - if (report == null) { - throw new Error('report not found'); - } - - await AbuseUserReports.delete(report.id); -}); diff --git a/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/src/server/api/endpoints/admin/resolve-abuse-user-report.ts new file mode 100644 index 0000000000..0a62b5f365 --- /dev/null +++ b/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -0,0 +1,30 @@ +import $ from 'cafy'; +import { ID } from '../../../../misc/cafy-id'; +import define from '../../define'; +import { AbuseUserReports } from '../../../../models'; + +export const meta = { + tags: ['admin'], + + requireCredential: true as const, + requireModerator: true, + + params: { + reportId: { + validator: $.type(ID), + }, + } +}; + +export default define(meta, async (ps, me) => { + const report = await AbuseUserReports.findOne(ps.reportId); + + if (report == null) { + throw new Error('report not found'); + } + + await AbuseUserReports.update(report.id, { + resolved: true, + assigneeId: me.id, + }); +}); diff --git a/src/server/api/endpoints/users/report-abuse.ts b/src/server/api/endpoints/users/report-abuse.ts index a9b5543f3c..eaa4cd6258 100644 --- a/src/server/api/endpoints/users/report-abuse.ts +++ b/src/server/api/endpoints/users/report-abuse.ts @@ -26,7 +26,7 @@ export const meta = { }, comment: { - validator: $.str.range(1, 3000), + validator: $.str.range(1, 2048), desc: { 'ja-JP': '迷惑行為の詳細' } @@ -72,9 +72,11 @@ export default define(meta, async (ps, me) => { const report = await AbuseUserReports.save({ id: genId(), createdAt: new Date(), - userId: user.id, + targetUserId: user.id, + targetUserHost: user.host, reporterId: me.id, - comment: ps.comment + reporterHost: null, + comment: ps.comment, }); // Publish event to moderators @@ -90,7 +92,7 @@ export default define(meta, async (ps, me) => { for (const moderator of moderators) { publishAdminStream(moderator.id, 'newAbuseUserReport', { id: report.id, - userId: report.userId, + targetUserId: report.targetUserId, reporterId: report.reporterId, comment: report.comment }); -- cgit v1.2.3-freya