This commit is contained in:
mattyatea 2023-10-14 17:10:41 +09:00
parent 3d79428b35
commit 9818d1ab9e
7 changed files with 130 additions and 131 deletions

View File

@ -3,9 +3,9 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common'; import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core'; import { ModuleRef } from '@nestjs/core';
import {DataSource, IsNull } from 'typeorm'; import { DataSource, IsNull } from 'typeorm';
import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
@ -28,9 +28,8 @@ import { MetaService } from '@/core/MetaService.js';
import { CacheService } from '@/core/CacheService.js'; import { CacheService } from '@/core/CacheService.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { AccountMoveService } from '@/core/AccountMoveService.js'; import { AccountMoveService } from '@/core/AccountMoveService.js';
import { shouldSilenceInstance } from '@/misc/should-block-instance.js';
import Logger from '../logger.js'; import Logger from '../logger.js';
import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
const logger = new Logger('following/create'); const logger = new Logger('following/create');
@ -133,7 +132,7 @@ export class UserFollowingService implements OnModuleInit {
followee.isLocked || followee.isLocked ||
(followeeProfile.carefulBot && follower.isBot) || (followeeProfile.carefulBot && follower.isBot) ||
(this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') || (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') ||
( this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower) && await shouldSilenceInstance(follower.host,this.db)) ( this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower) && await shouldSilenceInstance(follower.host, this.db))
) { ) {
let autoAccept = false; let autoAccept = false;

View File

@ -3,16 +3,15 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import {Inject, Injectable} from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import type { } from '@/models/Blocking.js';
import type { MiInstance } from '@/models/Instance.js'; import type { MiInstance } from '@/models/Instance.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { shouldSilenceInstance } from '@/misc/should-block-instance.js';
import { DI } from '@/di-symbols.js';
import { UtilityService } from '../UtilityService.js'; import { UtilityService } from '../UtilityService.js';
import {shouldSilenceInstance} from "@/misc/should-block-instance.js";
import { DataSource } from 'typeorm';
import {DI} from "@/di-symbols.js";
@Injectable() @Injectable()
export class InstanceEntityService { export class InstanceEntityService {
@ -49,7 +48,7 @@ export class InstanceEntityService {
description: instance.description, description: instance.description,
maintainerName: instance.maintainerName, maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail, maintainerEmail: instance.maintainerEmail,
isSilenced: await shouldSilenceInstance(instance.host,this.db), isSilenced: await shouldSilenceInstance(instance.host, this.db),
iconUrl: instance.iconUrl, iconUrl: instance.iconUrl,
faviconUrl: instance.faviconUrl, faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor, themeColor: instance.themeColor,

View File

@ -1,16 +1,16 @@
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { MiMeta } from "@/models/Meta.js"; import { MiMeta } from '@/models/Meta.js';
let cache: MiMeta; let cache: MiMeta;
export async function fetchMeta(noCache = false , db: DataSource): Promise<MiMeta> { export async function fetchMeta(noCache = false, db: DataSource): Promise<MiMeta> {
if (!noCache && cache) return cache; if (!noCache && cache) return cache;
return await db.transaction(async (transactionalEntityManager) => { return await db.transaction(async (transactionalEntityManager) => {
// New IDs are prioritized because multiple records may have been created due to past bugs. // New IDs are prioritized because multiple records may have been created due to past bugs.
const metas = await transactionalEntityManager.find(MiMeta, { const metas = await transactionalEntityManager.find(MiMeta, {
order: { order: {
id: "DESC", id: 'DESC',
}, },
}); });
@ -25,9 +25,9 @@ export async function fetchMeta(noCache = false , db: DataSource): Promise<MiMet
.upsert( .upsert(
MiMeta, MiMeta,
{ {
id: "x", id: 'x',
}, },
["id"], ['id'],
) )
.then((x) => .then((x) =>
transactionalEntityManager.findOneByOrFail(MiMeta, x.identifiers[0]), transactionalEntityManager.findOneByOrFail(MiMeta, x.identifiers[0]),

View File

@ -1,14 +1,14 @@
import { fetchMeta } from "@/misc/fetch-meta.js"; import { DataSource } from 'typeorm';
import type { MiInstance } from "@/models/Instance.js"; import { fetchMeta } from '@/misc/fetch-meta.js';
import type { MiMeta } from "@/models/Meta.js"; import type { MiInstance } from '@/models/Instance.js';
import { DataSource } from "typeorm"; import type { MiMeta } from '@/models/Meta.js';
export async function shouldSilenceInstance( export async function shouldSilenceInstance(
host: MiInstance["host"], host: MiInstance['host'],
db : DataSource, db : DataSource,
meta?: MiMeta, meta?: MiMeta,
): Promise<boolean> { ): Promise<boolean> {
const { silencedHosts } = meta ?? (await fetchMeta(true,db)); const { silencedHosts } = meta ?? (await fetchMeta(true, db));
return silencedHosts.some( return silencedHosts.some(
(limitedHost: string) => host === limitedHost || host.endsWith(`.${limitedHost}`), (limitedHost: string) => host === limitedHost || host.endsWith(`.${limitedHost}`),
); );

View File

@ -3,11 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import {Injectable} from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import type {MiMeta} from '@/models/Meta.js'; import type { MiMeta } from '@/models/Meta.js';
import {ModerationLogService} from '@/core/ModerationLogService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js';
import {Endpoint} from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import {MetaService} from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
export const meta = { export const meta = {
tags: ['admin'], tags: ['admin'],
@ -19,117 +19,117 @@ export const meta = {
export const paramDef = { export const paramDef = {
type: 'object', type: 'object',
properties: { properties: {
disableRegistration: {type: 'boolean', nullable: true}, disableRegistration: { type: 'boolean', nullable: true },
pinnedUsers: { pinnedUsers: {
type: 'array', nullable: true, items: { type: 'array', nullable: true, items: {
type: 'string', type: 'string',
} },
}, },
hiddenTags: { hiddenTags: {
type: 'array', nullable: true, items: { type: 'array', nullable: true, items: {
type: 'string', type: 'string',
} },
}, },
blockedHosts: { blockedHosts: {
type: 'array', nullable: true, items: { type: 'array', nullable: true, items: {
type: 'string', type: 'string',
} },
}, },
sensitiveWords: { sensitiveWords: {
type: 'array', nullable: true, items: { type: 'array', nullable: true, items: {
type: 'string', type: 'string',
} },
}, },
themeColor: {type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$'}, themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
mascotImageUrl: {type: 'string', nullable: true}, mascotImageUrl: { type: 'string', nullable: true },
bannerUrl: {type: 'string', nullable: true}, bannerUrl: { type: 'string', nullable: true },
serverErrorImageUrl: {type: 'string', nullable: true}, serverErrorImageUrl: { type: 'string', nullable: true },
infoImageUrl: {type: 'string', nullable: true}, infoImageUrl: { type: 'string', nullable: true },
notFoundImageUrl: {type: 'string', nullable: true}, notFoundImageUrl: { type: 'string', nullable: true },
iconUrl: {type: 'string', nullable: true}, iconUrl: { type: 'string', nullable: true },
app192IconUrl: {type: 'string', nullable: true}, app192IconUrl: { type: 'string', nullable: true },
app512IconUrl: {type: 'string', nullable: true}, app512IconUrl: { type: 'string', nullable: true },
backgroundImageUrl: {type: 'string', nullable: true}, backgroundImageUrl: { type: 'string', nullable: true },
logoImageUrl: {type: 'string', nullable: true}, logoImageUrl: { type: 'string', nullable: true },
name: {type: 'string', nullable: true}, name: { type: 'string', nullable: true },
shortName: {type: 'string', nullable: true}, shortName: { type: 'string', nullable: true },
description: {type: 'string', nullable: true}, description: { type: 'string', nullable: true },
defaultLightTheme: {type: 'string', nullable: true}, defaultLightTheme: { type: 'string', nullable: true },
defaultDarkTheme: {type: 'string', nullable: true}, defaultDarkTheme: { type: 'string', nullable: true },
cacheRemoteFiles: {type: 'boolean'}, cacheRemoteFiles: { type: 'boolean' },
cacheRemoteSensitiveFiles: {type: 'boolean'}, cacheRemoteSensitiveFiles: { type: 'boolean' },
emailRequiredForSignup: {type: 'boolean'}, emailRequiredForSignup: { type: 'boolean' },
enableHcaptcha: {type: 'boolean'}, enableHcaptcha: { type: 'boolean' },
hcaptchaSiteKey: {type: 'string', nullable: true}, hcaptchaSiteKey: { type: 'string', nullable: true },
hcaptchaSecretKey: {type: 'string', nullable: true}, hcaptchaSecretKey: { type: 'string', nullable: true },
enableRecaptcha: {type: 'boolean'}, enableRecaptcha: { type: 'boolean' },
recaptchaSiteKey: {type: 'string', nullable: true}, recaptchaSiteKey: { type: 'string', nullable: true },
recaptchaSecretKey: {type: 'string', nullable: true}, recaptchaSecretKey: { type: 'string', nullable: true },
enableTurnstile: {type: 'boolean'}, enableTurnstile: { type: 'boolean' },
turnstileSiteKey: {type: 'string', nullable: true}, turnstileSiteKey: { type: 'string', nullable: true },
turnstileSecretKey: {type: 'string', nullable: true}, turnstileSecretKey: { type: 'string', nullable: true },
sensitiveMediaDetection: {type: 'string', enum: ['none', 'all', 'local', 'remote']}, sensitiveMediaDetection: { type: 'string', enum: ['none', 'all', 'local', 'remote'] },
sensitiveMediaDetectionSensitivity: {type: 'string', enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh']}, sensitiveMediaDetectionSensitivity: { type: 'string', enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'] },
setSensitiveFlagAutomatically: {type: 'boolean'}, setSensitiveFlagAutomatically: { type: 'boolean' },
enableSensitiveMediaDetectionForVideos: {type: 'boolean'}, enableSensitiveMediaDetectionForVideos: { type: 'boolean' },
proxyAccountId: {type: 'string', format: 'misskey:id', nullable: true}, proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true },
maintainerName: {type: 'string', nullable: true}, maintainerName: { type: 'string', nullable: true },
maintainerEmail: {type: 'string', nullable: true}, maintainerEmail: { type: 'string', nullable: true },
langs: { langs: {
type: 'array', items: { type: 'array', items: {
type: 'string', type: 'string',
} },
}, },
summalyProxy: {type: 'string', nullable: true}, summalyProxy: { type: 'string', nullable: true },
deeplAuthKey: {type: 'string', nullable: true}, deeplAuthKey: { type: 'string', nullable: true },
deeplIsPro: {type: 'boolean'}, deeplIsPro: { type: 'boolean' },
enableEmail: {type: 'boolean'}, enableEmail: { type: 'boolean' },
email: {type: 'string', nullable: true}, email: { type: 'string', nullable: true },
smtpSecure: {type: 'boolean'}, smtpSecure: { type: 'boolean' },
smtpHost: {type: 'string', nullable: true}, smtpHost: { type: 'string', nullable: true },
smtpPort: {type: 'integer', nullable: true}, smtpPort: { type: 'integer', nullable: true },
smtpUser: {type: 'string', nullable: true}, smtpUser: { type: 'string', nullable: true },
smtpPass: {type: 'string', nullable: true}, smtpPass: { type: 'string', nullable: true },
enableServiceWorker: {type: 'boolean'}, enableServiceWorker: { type: 'boolean' },
swPublicKey: {type: 'string', nullable: true}, swPublicKey: { type: 'string', nullable: true },
swPrivateKey: {type: 'string', nullable: true}, swPrivateKey: { type: 'string', nullable: true },
tosUrl: {type: 'string', nullable: true}, tosUrl: { type: 'string', nullable: true },
repositoryUrl: {type: 'string'}, repositoryUrl: { type: 'string' },
feedbackUrl: {type: 'string'}, feedbackUrl: { type: 'string' },
impressumUrl: {type: 'string'}, impressumUrl: { type: 'string' },
privacyPolicyUrl: {type: 'string'}, privacyPolicyUrl: { type: 'string' },
useObjectStorage: {type: 'boolean'}, useObjectStorage: { type: 'boolean' },
objectStorageBaseUrl: {type: 'string', nullable: true}, objectStorageBaseUrl: { type: 'string', nullable: true },
objectStorageBucket: {type: 'string', nullable: true}, objectStorageBucket: { type: 'string', nullable: true },
objectStoragePrefix: {type: 'string', nullable: true}, objectStoragePrefix: { type: 'string', nullable: true },
objectStorageEndpoint: {type: 'string', nullable: true}, objectStorageEndpoint: { type: 'string', nullable: true },
objectStorageRegion: {type: 'string', nullable: true}, objectStorageRegion: { type: 'string', nullable: true },
objectStoragePort: {type: 'integer', nullable: true}, objectStoragePort: { type: 'integer', nullable: true },
objectStorageAccessKey: {type: 'string', nullable: true}, objectStorageAccessKey: { type: 'string', nullable: true },
objectStorageSecretKey: {type: 'string', nullable: true}, objectStorageSecretKey: { type: 'string', nullable: true },
objectStorageUseSSL: {type: 'boolean'}, objectStorageUseSSL: { type: 'boolean' },
objectStorageUseProxy: {type: 'boolean'}, objectStorageUseProxy: { type: 'boolean' },
objectStorageSetPublicRead: {type: 'boolean'}, objectStorageSetPublicRead: { type: 'boolean' },
objectStorageS3ForcePathStyle: {type: 'boolean'}, objectStorageS3ForcePathStyle: { type: 'boolean' },
enableIpLogging: {type: 'boolean'}, enableIpLogging: { type: 'boolean' },
enableActiveEmailValidation: {type: 'boolean'}, enableActiveEmailValidation: { type: 'boolean' },
enableChartsForRemoteUser: {type: 'boolean'}, enableChartsForRemoteUser: { type: 'boolean' },
enableChartsForFederatedInstances: {type: 'boolean'}, enableChartsForFederatedInstances: { type: 'boolean' },
enableServerMachineStats: {type: 'boolean'}, enableServerMachineStats: { type: 'boolean' },
enableIdenticonGeneration: {type: 'boolean'}, enableIdenticonGeneration: { type: 'boolean' },
serverRules: {type: 'array', items: {type: 'string'}}, serverRules: { type: 'array', items: { type: 'string' } },
preservedUsernames: {type: 'array', items: {type: 'string'}}, preservedUsernames: { type: 'array', items: { type: 'string' } },
manifestJsonOverride: {type: 'string'}, manifestJsonOverride: { type: 'string' },
perLocalUserUserTimelineCacheMax: {type: 'integer'}, perLocalUserUserTimelineCacheMax: { type: 'integer' },
perRemoteUserUserTimelineCacheMax: {type: 'integer'}, perRemoteUserUserTimelineCacheMax: { type: 'integer' },
perUserHomeTimelineCacheMax: {type: 'integer'}, perUserHomeTimelineCacheMax: { type: 'integer' },
perUserListTimelineCacheMax: {type: 'integer'}, perUserListTimelineCacheMax: { type: 'integer' },
notesPerOneAd: {type: 'integer'}, notesPerOneAd: { type: 'integer' },
silencedHosts: { silencedHosts: {
type: "array", type: 'array',
nullable: true, nullable: true,
items: { items: {
type: "string", type: 'string',
}, },
}, },
}, },
@ -165,11 +165,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.sensitiveWords = ps.sensitiveWords.filter(Boolean); set.sensitiveWords = ps.sensitiveWords.filter(Boolean);
} }
if (Array.isArray(ps.silencedHosts)) { if (Array.isArray(ps.silencedHosts)) {
let lastValue = ""; let lastValue = '';
set.silencedHosts = ps.silencedHosts.sort().filter((h) => { set.silencedHosts = ps.silencedHosts.sort().filter((h) => {
const lv = lastValue; const lv = lastValue;
lastValue = h; lastValue = h;
return h !== "" && h !== lv && !set.blockedHosts?.includes(h); return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
}); });
} }
if (ps.themeColor !== undefined) { if (ps.themeColor !== undefined) {

View File

@ -76,7 +76,7 @@ const pagination = {
state === 'publishing' ? { publishing: true } : state === 'publishing' ? { publishing: true } :
state === 'suspended' ? { suspended: true } : state === 'suspended' ? { suspended: true } :
state === 'blocked' ? { blocked: true } : state === 'blocked' ? { blocked: true } :
state === 'silenced' ? {silenced: true} : state === 'silenced' ? { silenced: true } :
state === 'notResponding' ? { notResponding: true } : state === 'notResponding' ? { notResponding: true } :
{}), {}),
})), })),
@ -85,7 +85,7 @@ const pagination = {
function getStatus(instance) { function getStatus(instance) {
if (instance.isSuspended) return 'Suspended'; if (instance.isSuspended) return 'Suspended';
if (instance.isBlocked) return 'Blocked'; if (instance.isBlocked) return 'Blocked';
if (instance.isSilenced) return 'Silenced' if (instance.isSilenced) return 'Silenced';
if (instance.isNotResponding) return 'Error'; if (instance.isNotResponding) return 'Error';
return 'Alive'; return 'Alive';
} }

View File

@ -5,8 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header><XHeader :actions="headerActions" v-model:tab="tab" :tabs="headerTabs"/></template> <template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32" > <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
<FormSuspense :p="init"> <FormSuspense :p="init">
<MkTextarea v-if="tab === 'block'" v-model="blockedHosts"> <MkTextarea v-if="tab === 'block'" v-model="blockedHosts">
<span>{{ i18n.ts.blockedInstances }}</span> <span>{{ i18n.ts.blockedInstances }}</span>
@ -14,9 +14,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkTextarea> </MkTextarea>
<MkTextarea v-else-if="tab === 'silence'" v-model="silencedHosts" class="_formBlock"> <MkTextarea v-else-if="tab === 'silence'" v-model="silencedHosts" class="_formBlock">
<span>{{ i18n.ts.silencedInstances }}</span> <span>{{ i18n.ts.silencedInstances }}</span>
<template #caption>{{ <template #caption>
{{
i18n.ts.silencedInstancesDescription i18n.ts.silencedInstancesDescription
}}</template> }}
</template>
</MkTextarea> </MkTextarea>
<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
</FormSuspense> </FormSuspense>
@ -25,7 +27,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { } from 'vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
@ -36,8 +37,8 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
let blockedHosts: string = $ref(''); let blockedHosts: string = $ref('');
let silencedHosts: string = $ref(""); let silencedHosts: string = $ref('');
let tab = $ref("block"); let tab = $ref('block');
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
@ -48,7 +49,7 @@ async function init() {
function save() { function save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
blockedHosts: blockedHosts.split('\n') || [], blockedHosts: blockedHosts.split('\n') || [],
silencedHosts: silencedHosts.split("\n") || [], silencedHosts: silencedHosts.split('\n') || [],
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
@ -59,14 +60,14 @@ const headerActions = $computed(() => []);
const headerTabs = $computed(() => [ const headerTabs = $computed(() => [
{ {
key: "block", key: 'block',
title: i18n.ts.block, title: i18n.ts.block,
icon: "ph-prohibit ph-bold ph-lg", icon: 'ph-prohibit ph-bold ph-lg',
}, },
{ {
key: "silence", key: 'silence',
title: i18n.ts.silence, title: i18n.ts.silence,
icon: "ph-eye-slash ph-bold ph-lg", icon: 'ph-eye-slash ph-bold ph-lg',
}, },
]); ]);