summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorMarie <github@yuugi.dev>2024-10-06 19:39:38 +0000
committerMarie <github@yuugi.dev>2024-10-06 19:39:38 +0000
commit28bfd875372bb7769e1e303b4153ca2fdd0f2b8d (patch)
treea9ddb132cd78c1f99a491bb458f51fae5bceba2f /packages
parentmerge: Change the recent external url warning popup to the one from Cherrypic... (diff)
parentMerge branch 'develop' into hazelnoot/579-reject-reports (diff)
downloadsharkey-28bfd875372bb7769e1e303b4153ca2fdd0f2b8d.tar.gz
sharkey-28bfd875372bb7769e1e303b4153ca2fdd0f2b8d.tar.bz2
sharkey-28bfd875372bb7769e1e303b4153ca2fdd0f2b8d.zip
merge: Add option to reject reports from an instance (Resolves #579, #715, #716) (!662)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/662 Closes #579, #715, and #716 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Marie <github@yuugi.dev>
Diffstat (limited to 'packages')
-rw-r--r--packages/backend/migration/1728177700920-add-reject-reports.js16
-rw-r--r--packages/backend/src/core/activitypub/ApInboxService.ts9
-rw-r--r--packages/backend/src/models/Instance.ts7
-rw-r--r--packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts18
-rw-r--r--packages/backend/src/types.ts20
-rw-r--r--packages/frontend/src/pages/admin/modlog.ModLog.vue8
-rw-r--r--packages/frontend/src/pages/instance-info.vue13
7 files changed, 89 insertions, 2 deletions
diff --git a/packages/backend/migration/1728177700920-add-reject-reports.js b/packages/backend/migration/1728177700920-add-reject-reports.js
new file mode 100644
index 0000000000..ed5f6bc559
--- /dev/null
+++ b/packages/backend/migration/1728177700920-add-reject-reports.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class AddRejectReports1728177700920 {
+ name = 'AddRejectReports1728177700920'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "instance" ADD "rejectReports" boolean NOT NULL DEFAULT false`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "rejectReports"`);
+ }
+}
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 6a28cbad15..bce67a458f 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -5,6 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
+import * as Bull from 'bullmq';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { UserFollowingService } from '@/core/UserFollowingService.js';
@@ -29,6 +30,7 @@ import { bindThis } from '@/decorators.js';
import type { MiRemoteUser } from '@/models/User.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { AbuseReportService } from '@/core/AbuseReportService.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
import { ApNoteService } from './models/ApNoteService.js';
import { ApLoggerService } from './ApLoggerService.js';
@@ -83,6 +85,7 @@ export class ApInboxService {
private apQuestionService: ApQuestionService,
private queueService: QueueService,
private globalEventService: GlobalEventService,
+ private federatedInstanceService: FederatedInstanceService,
) {
this.logger = this.apLoggerService.logger;
}
@@ -530,6 +533,12 @@ export class ApInboxService {
@bindThis
private async flag(actor: MiRemoteUser, activity: IFlag): Promise<string> {
+ // Make sure the source instance is allowed to send reports.
+ const instance = await this.federatedInstanceService.fetch(actor.host);
+ if (instance.rejectReports) {
+ throw new Bull.UnrecoverableError(`Rejecting report from instance: ${actor.host}`);
+ }
+
// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
const uris = getApIds(activity.object);
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index dd625f95d3..ba93190c57 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -158,7 +158,12 @@ export class MiInstance {
default: false,
})
public isNSFW: boolean;
-
+
+ @Column('boolean', {
+ default: false,
+ })
+ public rejectReports: boolean;
+
@Column('varchar', {
length: 16384, default: '',
})
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
index 8b142910a6..daf19c4435 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
@@ -25,6 +25,7 @@ export const paramDef = {
host: { type: 'string' },
isSuspended: { type: 'boolean' },
isNSFW: { type: 'boolean' },
+ rejectReports: { type: 'boolean' },
moderationNote: { type: 'string' },
},
required: ['host'],
@@ -57,6 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
await this.federatedInstanceService.update(instance.id, {
suspensionState,
isNSFW: ps.isNSFW,
+ rejectReports: ps.rejectReports,
moderationNote: ps.moderationNote,
});
@@ -74,6 +76,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
}
+ if (ps.isNSFW != null && instance.isNSFW !== ps.isNSFW) {
+ const message = ps.rejectReports ? 'setRemoteInstanceNSFW' : 'unsetRemoteInstanceNSFW';
+ this.moderationLogService.log(me, message, {
+ id: instance.id,
+ host: instance.host,
+ });
+ }
+
+ if (ps.rejectReports != null && instance.rejectReports !== ps.rejectReports) {
+ const message = ps.rejectReports ? 'rejectRemoteInstanceReports' : 'acceptRemoteInstanceReports';
+ this.moderationLogService.log(me, message, {
+ id: instance.id,
+ host: instance.host,
+ });
+ }
+
if (ps.moderationNote != null && instance.moderationNote !== ps.moderationNote) {
this.moderationLogService.log(me, 'updateRemoteInstanceNote', {
id: instance.id,
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index d83d414096..d64d72c07f 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -77,8 +77,12 @@ export const moderationLogTypes = [
'deleteGlobalAnnouncement',
'deleteUserAnnouncement',
'resetPassword',
+ 'setRemoteInstanceNSFW',
+ 'unsetRemoteInstanceNSFW',
'suspendRemoteInstance',
'unsuspendRemoteInstance',
+ 'rejectRemoteInstanceReports',
+ 'acceptRemoteInstanceReports',
'updateRemoteInstanceNote',
'markSensitiveDriveFile',
'unmarkSensitiveDriveFile',
@@ -227,6 +231,14 @@ export type ModerationLogPayloads = {
userUsername: string;
userHost: string | null;
};
+ setRemoteInstanceNSFW: {
+ id: string;
+ host: string;
+ };
+ unsetRemoteInstanceNSFW: {
+ id: string;
+ host: string;
+ };
suspendRemoteInstance: {
id: string;
host: string;
@@ -235,6 +247,14 @@ export type ModerationLogPayloads = {
id: string;
host: string;
};
+ rejectRemoteInstanceReports: {
+ id: string;
+ host: string;
+ };
+ acceptRemoteInstanceReports: {
+ id: string;
+ host: string;
+ };
updateRemoteInstanceNote: {
id: string;
host: string;
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index f6f276de53..9fe804b2bd 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -23,6 +23,10 @@ SPDX-License-Identifier: AGPL-3.0-only
'markSensitiveDriveFile',
'resetPassword',
'suspendRemoteInstance',
+ 'setRemoteInstanceNSFW',
+ 'unsetRemoteInstanceNSFW',
+ 'rejectRemoteInstanceReports',
+ 'acceptRemoteInstanceReports',
].includes(log.type),
[$style.logRed]: [
'suspend',
@@ -61,6 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-else-if="log.type === 'unmarkSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
<span v-else-if="log.type === 'suspendRemoteInstance'">: {{ log.info.host }}</span>
<span v-else-if="log.type === 'unsuspendRemoteInstance'">: {{ log.info.host }}</span>
+ <span v-else-if="log.type === 'setRemoteInstanceNSFW'">: {{ log.info.host }}</span>
+ <span v-else-if="log.type === 'unsetRemoteInstanceNSFW'">: {{ log.info.host }}</span>
+ <span v-else-if="log.type === 'rejectRemoteInstanceReports'">: {{ log.info.host }}</span>
+ <span v-else-if="log.type === 'acceptRemoteInstanceReports'">: {{ log.info.host }}</span>
<span v-else-if="log.type === 'createGlobalAnnouncement'">: {{ log.info.announcement.title }}</span>
<span v-else-if="log.type === 'updateGlobalAnnouncement'">: {{ log.info.before.title }}</span>
<span v-else-if="log.type === 'deleteGlobalAnnouncement'">: {{ log.info.announcement.title }}</span>
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 821cd599eb..5401ea51e0 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -49,7 +49,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance || isBaseBlocked" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
<MkInfo v-if="isBaseSilenced" warn>{{ i18n.ts.silencedByBase }}</MkInfo>
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance || isBaseSilenced" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
- <MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">Mark as NSFW</MkSwitch>
+ <MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">{{ i18n.ts.markInstanceAsNSFW }}</MkSwitch>
+ <MkSwitch v-model="rejectReports" :disabled="!instance" @update:modelValue="toggleRejectReports">{{ i18n.ts.rejectReports }}</MkSwitch>
<MkInfo v-if="isBaseMediaSilenced" warn>{{ i18n.ts.mediaSilencedByBase }}</MkInfo>
<MkSwitch v-model="isMediaSilenced" :disabled="!meta || !instance || isBaseMediaSilenced" @update:modelValue="toggleMediaSilenced">{{ i18n.ts.mediaSilenceThisInstance }}</MkSwitch>
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
@@ -174,6 +175,7 @@ const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'au
const isBlocked = ref(false);
const isSilenced = ref(false);
const isNSFW = ref(false);
+const rejectReports = ref(false);
const isMediaSilenced = ref(false);
const faviconUrl = ref<string | null>(null);
const moderationNote = ref('');
@@ -219,6 +221,7 @@ async function fetch(): Promise<void> {
isBlocked.value = instance.value?.isBlocked ?? false;
isSilenced.value = instance.value?.isSilenced ?? false;
isNSFW.value = instance.value?.isNSFW ?? false;
+ rejectReports.value = instance.value?.rejectReports ?? false;
isMediaSilenced.value = instance.value?.isMediaSilenced ?? false;
faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
moderationNote.value = instance.value?.moderationNote ?? '';
@@ -279,6 +282,14 @@ async function toggleNSFW(): Promise<void> {
});
}
+async function toggleRejectReports(): Promise<void> {
+ if (!instance.value) throw new Error('No instance?');
+ await misskeyApi('admin/federation/update-instance', {
+ host: instance.value.host,
+ rejectReports: rejectReports.value,
+ });
+}
+
function refreshMetadata(): void {
if (!instance.value) throw new Error('No instance?');
misskeyApi('admin/federation/refresh-remote-instance-metadata', {