Merge branch 'develop' into hazelnoot/following-timeline

This commit is contained in:
Hazel K 2024-10-07 10:45:16 -04:00
commit 0da7f9b3f6
26 changed files with 542 additions and 41 deletions

View file

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class ExternalWebsiteWarn1711008460816 {
name = 'ExternalWebsiteWarn1711008460816'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "trustedLinkUrlPatterns" character varying(3072) array NOT NULL DEFAULT '{}'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "trustedLinkUrlPatterns"`);
}
}

View file

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

View file

@ -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);

View file

@ -120,6 +120,7 @@ export class MetaEntityService {
imageUrl: ad.imageUrl,
dayOfWeek: ad.dayOfWeek,
})),
trustedLinkUrlPatterns: instance.trustedLinkUrlPatterns,
notesPerOneAd: instance.notesPerOneAd,
enableEmail: instance.enableEmail,
enableServiceWorker: instance.enableServiceWorker,

View file

@ -158,7 +158,12 @@ export class MiInstance {
default: false,
})
public isNSFW: boolean;
@Column('boolean', {
default: false,
})
public rejectReports: boolean;
@Column('varchar', {
length: 16384, default: '',
})

View file

@ -674,4 +674,12 @@ export class MiMeta {
nullable: true,
})
public urlPreviewUserAgent: string | null;
@Column('varchar', {
length: 3072,
array: true,
default: '{}',
comment: 'An array of URL strings or regex that can be used to omit warnings about redirects to external sites. Separate them with spaces to specify AND, and enclose them with slashes to specify regular expressions. Each item is regarded as an OR.',
})
public trustedLinkUrlPatterns: string[];
}

View file

@ -273,6 +273,14 @@ export const packedMetaLiteSchema = {
optional: false, nullable: false,
default: 'local',
},
trustedLinkUrlPatterns: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
},
} as const;

View file

@ -31,15 +31,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.notesRepository)
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
private queueService: QueueService,
) {
super(meta, paramDef, async (ps, me) => {
const followings = await this.followingsRepository.findBy({
followerHost: ps.host,
});
const followings = await this.followingsRepository.findBy([
{
followeeHost: ps.host,
},
{
followerHost: ps.host,
},
]);
const pairs = await Promise.all(followings.map(f => Promise.all([
this.usersRepository.findOneByOrFail({ id: f.followerId }),

View file

@ -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,

View file

@ -128,7 +128,7 @@ export const meta = {
},
silencedHosts: {
type: 'array',
optional: true,
optional: false,
nullable: false,
items: {
type: 'string',
@ -526,6 +526,14 @@ export const meta = {
type: 'string',
optional: false, nullable: true,
},
trustedLinkUrlPatterns: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
},
},
} as const;
@ -669,6 +677,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
urlPreviewRequireContentLength: instance.urlPreviewRequireContentLength,
urlPreviewUserAgent: instance.urlPreviewUserAgent,
urlPreviewSummaryProxyUrl: instance.urlPreviewSummaryProxyUrl,
trustedLinkUrlPatterns: instance.trustedLinkUrlPatterns,
};
});
}

View file

@ -176,6 +176,11 @@ export const paramDef = {
urlPreviewRequireContentLength: { type: 'boolean' },
urlPreviewUserAgent: { type: 'string', nullable: true },
urlPreviewSummaryProxyUrl: { type: 'string', nullable: true },
trustedLinkUrlPatterns: {
type: 'array', nullable: true, items: {
type: 'string',
},
},
},
required: [],
} as const;
@ -665,6 +670,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.urlPreviewSummaryProxyUrl = value === '' ? null : value;
}
if (Array.isArray(ps.trustedLinkUrlPatterns)) {
set.trustedLinkUrlPatterns = ps.trustedLinkUrlPatterns.filter(Boolean);
}
const before = await this.metaService.fetch(true);
await this.metaService.update(set);

View file

@ -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;