feat: media silence (#13842)
* feat: media silence * fix: lint * feat: deny creating custom emoji reaction and using custom emoji from media silenced hosts * chore: メディアサイレンスの説明にカスタム絵文字の話を追加 * Update locales/ja-JP.yml Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> * chore: update index.d.ts * docs(changelog): update changelog --------- Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
This commit is contained in:
parent
8f40f932e4
commit
5c42a0e439
16 changed files with 124 additions and 11 deletions
|
@ -9,6 +9,8 @@
|
||||||
- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
|
- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
|
||||||
- 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
|
- 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
|
||||||
- Feat: ユーザ作成時にSystemWebhookを送信可能に #14281
|
- Feat: ユーザ作成時にSystemWebhookを送信可能に #14281
|
||||||
|
- Feat: メディアサイレンスを実装 #13842
|
||||||
|
- メディアサイレンスされたサーバーに所属するアカウントによるファイルはすべてセンシティブとして扱われ、カスタム絵文字が使用できないようになります。
|
||||||
- Enhance: 管理画面でアーカイブにしたお知らせを表示・編集できるように
|
- Enhance: 管理画面でアーカイブにしたお知らせを表示・編集できるように
|
||||||
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
|
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
|
||||||
- Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
|
- Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
|
||||||
|
|
12
locales/index.d.ts
vendored
12
locales/index.d.ts
vendored
|
@ -864,6 +864,10 @@ export interface Locale extends ILocale {
|
||||||
* サーバーをサイレンス
|
* サーバーをサイレンス
|
||||||
*/
|
*/
|
||||||
"silenceThisInstance": string;
|
"silenceThisInstance": string;
|
||||||
|
/**
|
||||||
|
* サーバーをメディアサイレンス
|
||||||
|
*/
|
||||||
|
"mediaSilenceThisInstance": string;
|
||||||
/**
|
/**
|
||||||
* 操作
|
* 操作
|
||||||
*/
|
*/
|
||||||
|
@ -948,6 +952,14 @@ export interface Locale extends ILocale {
|
||||||
* サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。
|
* サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。
|
||||||
*/
|
*/
|
||||||
"silencedInstancesDescription": string;
|
"silencedInstancesDescription": string;
|
||||||
|
/**
|
||||||
|
* メディアサイレンスしたサーバー
|
||||||
|
*/
|
||||||
|
"mediaSilencedInstances": string;
|
||||||
|
/**
|
||||||
|
* メディアサイレンスしたいサーバーのホストを改行で区切って設定します。メディアサイレンスされたサーバーに所属するアカウントによるファイルはすべてセンシティブとして扱われ、カスタム絵文字が使用できないようになります。ブロックしたインスタンスには影響しません。
|
||||||
|
*/
|
||||||
|
"mediaSilencedInstancesDescription": string;
|
||||||
/**
|
/**
|
||||||
* ミュートとブロック
|
* ミュートとブロック
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -212,6 +212,7 @@ perDay: "1日ごと"
|
||||||
stopActivityDelivery: "アクティビティの配送を停止"
|
stopActivityDelivery: "アクティビティの配送を停止"
|
||||||
blockThisInstance: "このサーバーをブロック"
|
blockThisInstance: "このサーバーをブロック"
|
||||||
silenceThisInstance: "サーバーをサイレンス"
|
silenceThisInstance: "サーバーをサイレンス"
|
||||||
|
mediaSilenceThisInstance: "サーバーをメディアサイレンス"
|
||||||
operations: "操作"
|
operations: "操作"
|
||||||
software: "ソフトウェア"
|
software: "ソフトウェア"
|
||||||
version: "バージョン"
|
version: "バージョン"
|
||||||
|
@ -233,6 +234,8 @@ blockedInstances: "ブロックしたサーバー"
|
||||||
blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。"
|
blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。"
|
||||||
silencedInstances: "サイレンスしたサーバー"
|
silencedInstances: "サイレンスしたサーバー"
|
||||||
silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。"
|
silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。"
|
||||||
|
mediaSilencedInstances: "メディアサイレンスしたサーバー"
|
||||||
|
mediaSilencedInstancesDescription: "メディアサイレンスしたいサーバーのホストを改行で区切って設定します。メディアサイレンスされたサーバーに所属するアカウントによるファイルはすべてセンシティブとして扱われ、カスタム絵文字が使用できないようになります。ブロックしたインスタンスには影響しません。"
|
||||||
muteAndBlock: "ミュートとブロック"
|
muteAndBlock: "ミュートとブロック"
|
||||||
mutedUsers: "ミュートしたユーザー"
|
mutedUsers: "ミュートしたユーザー"
|
||||||
blockedUsers: "ブロックしたユーザー"
|
blockedUsers: "ブロックしたユーザー"
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MediaSilenceForHosts1716197366117 {
|
||||||
|
name = 'MediaSilenceForHosts1716197366117'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "mediaSilencedHosts" character varying(1024) array NOT NULL DEFAULT '{}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mediaSilencedHosts"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ import { RoleService } from '@/core/RoleService.js';
|
||||||
import { correctFilename } from '@/misc/correct-filename.js';
|
import { correctFilename } from '@/misc/correct-filename.js';
|
||||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
|
|
||||||
type AddFileArgs = {
|
type AddFileArgs = {
|
||||||
/** User who wish to add file */
|
/** User who wish to add file */
|
||||||
|
@ -127,6 +128,7 @@ export class DriveService {
|
||||||
private driveChart: DriveChart,
|
private driveChart: DriveChart,
|
||||||
private perUserDriveChart: PerUserDriveChart,
|
private perUserDriveChart: PerUserDriveChart,
|
||||||
private instanceChart: InstanceChart,
|
private instanceChart: InstanceChart,
|
||||||
|
private utilityService: UtilityService,
|
||||||
) {
|
) {
|
||||||
const logger = new Logger('drive', 'blue');
|
const logger = new Logger('drive', 'blue');
|
||||||
this.registerLogger = logger.createSubLogger('register', 'yellow');
|
this.registerLogger = logger.createSubLogger('register', 'yellow');
|
||||||
|
@ -587,6 +589,7 @@ export class DriveService {
|
||||||
sensitive ?? false
|
sensitive ?? false
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
|
if (user && this.utilityService.isMediaSilencedHost(instance.mediaSilencedHosts, user.host)) file.isSensitive = true;
|
||||||
if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
|
if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
|
||||||
if (info.sensitive && instance.setSensitiveFlagAutomatically) file.isSensitive = true;
|
if (info.sensitive && instance.setSensitiveFlagAutomatically) file.isSensitive = true;
|
||||||
if (userRoleNSFW) file.isSensitive = true;
|
if (userRoleNSFW) file.isSensitive = true;
|
||||||
|
|
|
@ -364,6 +364,9 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
mentionedUsers = data.apMentions ?? await this.extractMentionedUsers(user, combinedTokens);
|
mentionedUsers = data.apMentions ?? await this.extractMentionedUsers(user, combinedTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the host is media-silenced, custom emojis are not allowed
|
||||||
|
if (this.utilityService.isMediaSilencedHost(meta.mediaSilencedHosts, user.host)) emojis = [];
|
||||||
|
|
||||||
tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32);
|
tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32);
|
||||||
|
|
||||||
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
|
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
|
||||||
|
|
|
@ -105,6 +105,8 @@ export class ReactionService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async create(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot'] }, note: MiNote, _reaction?: string | null) {
|
public async create(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot'] }, note: MiNote, _reaction?: string | null) {
|
||||||
|
const meta = await this.metaService.fetch();
|
||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (note.userId !== user.id) {
|
if (note.userId !== user.id) {
|
||||||
const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id);
|
const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id);
|
||||||
|
@ -148,6 +150,11 @@ export class ReactionService {
|
||||||
if ((note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && emoji.isSensitive) {
|
if ((note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && emoji.isSensitive) {
|
||||||
reaction = FALLBACK;
|
reaction = FALLBACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for media silenced host, custom emoji reactions are not allowed
|
||||||
|
if (reacterHost != null && this.utilityService.isMediaSilencedHost(meta.mediaSilencedHosts, reacterHost)) {
|
||||||
|
reaction = FALLBACK;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// リアクションとして使う権限がない
|
// リアクションとして使う権限がない
|
||||||
reaction = FALLBACK;
|
reaction = FALLBACK;
|
||||||
|
@ -220,8 +227,6 @@ export class ReactionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = await this.metaService.fetch();
|
|
||||||
|
|
||||||
if (meta.enableChartsForRemoteUser || (user.host == null)) {
|
if (meta.enableChartsForRemoteUser || (user.host == null)) {
|
||||||
this.perUserReactionsChart.update(user, note);
|
this.perUserReactionsChart.update(user, note);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,12 @@ export class UtilityService {
|
||||||
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
|
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public isMediaSilencedHost(silencedHosts: string[] | undefined, host: string | null): boolean {
|
||||||
|
if (!silencedHosts || host == null) return false;
|
||||||
|
return silencedHosts.some(x => host.toLowerCase() === x);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public concatNoteContentsForKeyWordCheck(content: {
|
public concatNoteContentsForKeyWordCheck(content: {
|
||||||
cw?: string | null;
|
cw?: string | null;
|
||||||
|
|
|
@ -50,6 +50,7 @@ export class InstanceEntityService {
|
||||||
maintainerName: instance.maintainerName,
|
maintainerName: instance.maintainerName,
|
||||||
maintainerEmail: instance.maintainerEmail,
|
maintainerEmail: instance.maintainerEmail,
|
||||||
isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host),
|
isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host),
|
||||||
|
isMediaSilenced: this.utilityService.isMediaSilencedHost(meta.mediaSilencedHosts, instance.host),
|
||||||
iconUrl: instance.iconUrl,
|
iconUrl: instance.iconUrl,
|
||||||
faviconUrl: instance.faviconUrl,
|
faviconUrl: instance.faviconUrl,
|
||||||
themeColor: instance.themeColor,
|
themeColor: instance.themeColor,
|
||||||
|
|
|
@ -86,6 +86,11 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public silencedHosts: string[];
|
public silencedHosts: string[];
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 1024, array: true, default: '{}',
|
||||||
|
})
|
||||||
|
public mediaSilencedHosts: string[];
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024,
|
length: 1024,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
|
@ -88,6 +88,10 @@ export const packedFederationInstanceSchema = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
isMediaSilenced: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
iconUrl: {
|
iconUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
|
|
@ -128,6 +128,16 @@ export const meta = {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mediaSilencedHosts: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
pinnedUsers: {
|
pinnedUsers: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -552,6 +562,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
hiddenTags: instance.hiddenTags,
|
hiddenTags: instance.hiddenTags,
|
||||||
blockedHosts: instance.blockedHosts,
|
blockedHosts: instance.blockedHosts,
|
||||||
silencedHosts: instance.silencedHosts,
|
silencedHosts: instance.silencedHosts,
|
||||||
|
mediaSilencedHosts: instance.mediaSilencedHosts,
|
||||||
sensitiveWords: instance.sensitiveWords,
|
sensitiveWords: instance.sensitiveWords,
|
||||||
prohibitedWords: instance.prohibitedWords,
|
prohibitedWords: instance.prohibitedWords,
|
||||||
preservedUsernames: instance.preservedUsernames,
|
preservedUsernames: instance.preservedUsernames,
|
||||||
|
|
|
@ -150,6 +150,13 @@ export const paramDef = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mediaSilencedHosts: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: true,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
summalyProxy: {
|
summalyProxy: {
|
||||||
type: 'string', nullable: true,
|
type: 'string', nullable: true,
|
||||||
description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
|
description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
|
||||||
|
@ -203,6 +210,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
|
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(ps.mediaSilencedHosts)) {
|
||||||
|
let lastValue = '';
|
||||||
|
set.mediaSilencedHosts = ps.mediaSilencedHosts.sort().filter((h) => {
|
||||||
|
const lv = lastValue;
|
||||||
|
lastValue = h;
|
||||||
|
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
|
||||||
|
});
|
||||||
|
}
|
||||||
if (ps.themeColor !== undefined) {
|
if (ps.themeColor !== undefined) {
|
||||||
set.themeColor = ps.themeColor;
|
set.themeColor = ps.themeColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #header><XHeader v-model:tab="tab" :actions="headerActions" :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">
|
<template v-if="tab === 'block'">
|
||||||
<span>{{ i18n.ts.blockedInstances }}</span>
|
<MkTextarea v-model="blockedHosts">
|
||||||
<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
|
<span>{{ i18n.ts.blockedInstances }}</span>
|
||||||
</MkTextarea>
|
<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
|
||||||
<MkTextarea v-else-if="tab === 'silence'" v-model="silencedHosts" class="_formBlock">
|
</MkTextarea>
|
||||||
<span>{{ i18n.ts.silencedInstances }}</span>
|
</template>
|
||||||
<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
|
<template v-else-if="tab === 'silence'">
|
||||||
</MkTextarea>
|
<MkTextarea v-model="silencedHosts" class="_formBlock">
|
||||||
|
<span>{{ i18n.ts.silencedInstances }}</span>
|
||||||
|
<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
|
||||||
|
</MkTextarea>
|
||||||
|
<MkTextarea v-model="mediaSilencedHosts" class="_formBlock">
|
||||||
|
<span>{{ i18n.ts.mediaSilencedInstances }}</span>
|
||||||
|
<template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
|
||||||
|
</MkTextarea>
|
||||||
|
</template>
|
||||||
<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>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
@ -36,18 +44,21 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
||||||
const blockedHosts = ref<string>('');
|
const blockedHosts = ref<string>('');
|
||||||
const silencedHosts = ref<string>('');
|
const silencedHosts = ref<string>('');
|
||||||
|
const mediaSilencedHosts = ref<string>('');
|
||||||
const tab = ref('block');
|
const tab = ref('block');
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await misskeyApi('admin/meta');
|
const meta = await misskeyApi('admin/meta');
|
||||||
blockedHosts.value = meta.blockedHosts.join('\n');
|
blockedHosts.value = meta.blockedHosts.join('\n');
|
||||||
silencedHosts.value = meta.silencedHosts.join('\n');
|
silencedHosts.value = meta.silencedHosts.join('\n');
|
||||||
|
mediaSilencedHosts.value = meta.mediaSilencedHosts.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog('admin/update-meta', {
|
os.apiWithDialog('admin/update-meta', {
|
||||||
blockedHosts: blockedHosts.value.split('\n') || [],
|
blockedHosts: blockedHosts.value.split('\n') || [],
|
||||||
silencedHosts: silencedHosts.value.split('\n') || [],
|
silencedHosts: silencedHosts.value.split('\n') || [],
|
||||||
|
mediaSilencedHosts: mediaSilencedHosts.value.split('\n') || [],
|
||||||
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance(true);
|
fetchInstance(true);
|
||||||
|
|
|
@ -47,6 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkButton v-if="suspensionState !== 'none'" :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton>
|
<MkButton v-if="suspensionState !== 'none'" :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton>
|
||||||
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
|
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
|
||||||
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
|
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
|
||||||
|
<MkSwitch v-model="isMediaSilenced" :disabled="!meta || !instance" @update:modelValue="toggleMediaSilenced">{{ i18n.ts.mediaSilenceThisInstance }}</MkSwitch>
|
||||||
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
|
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
|
||||||
<MkTextarea v-model="moderationNote" manualSave>
|
<MkTextarea v-model="moderationNote" manualSave>
|
||||||
<template #label>{{ i18n.ts.moderationNote }}</template>
|
<template #label>{{ i18n.ts.moderationNote }}</template>
|
||||||
|
@ -167,6 +168,7 @@ const instance = ref<Misskey.entities.FederationInstance | null>(null);
|
||||||
const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding'>('none');
|
const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding'>('none');
|
||||||
const isBlocked = ref(false);
|
const isBlocked = ref(false);
|
||||||
const isSilenced = ref(false);
|
const isSilenced = ref(false);
|
||||||
|
const isMediaSilenced = ref(false);
|
||||||
const faviconUrl = ref<string | null>(null);
|
const faviconUrl = ref<string | null>(null);
|
||||||
const moderationNote = ref('');
|
const moderationNote = ref('');
|
||||||
|
|
||||||
|
@ -195,8 +197,9 @@ async function fetch(): Promise<void> {
|
||||||
suspensionState.value = instance.value?.suspensionState ?? 'none';
|
suspensionState.value = instance.value?.suspensionState ?? 'none';
|
||||||
isBlocked.value = instance.value?.isBlocked ?? false;
|
isBlocked.value = instance.value?.isBlocked ?? false;
|
||||||
isSilenced.value = instance.value?.isSilenced ?? false;
|
isSilenced.value = instance.value?.isSilenced ?? false;
|
||||||
|
isMediaSilenced.value = instance.value?.isMediaSilenced ?? false;
|
||||||
faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
|
faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
|
||||||
moderationNote.value = instance.value?.moderationNote;
|
moderationNote.value = instance.value?.moderationNote ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleBlock(): Promise<void> {
|
async function toggleBlock(): Promise<void> {
|
||||||
|
@ -218,6 +221,16 @@ async function toggleSilenced(): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function toggleMediaSilenced(): Promise<void> {
|
||||||
|
if (!meta.value) throw new Error('No meta?');
|
||||||
|
if (!instance.value) throw new Error('No instance?');
|
||||||
|
const { host } = instance.value;
|
||||||
|
const mediaSilencedHosts = meta.value.mediaSilencedHosts ?? [];
|
||||||
|
await misskeyApi('admin/update-meta', {
|
||||||
|
mediaSilencedHosts: isMediaSilenced.value ? mediaSilencedHosts.concat([host]) : mediaSilencedHosts.filter(x => x !== host),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function stopDelivery(): Promise<void> {
|
async function stopDelivery(): Promise<void> {
|
||||||
if (!instance.value) throw new Error('No instance?');
|
if (!instance.value) throw new Error('No instance?');
|
||||||
suspensionState.value = 'manuallySuspended';
|
suspensionState.value = 'manuallySuspended';
|
||||||
|
|
|
@ -4599,6 +4599,7 @@ export type components = {
|
||||||
maintainerName: string | null;
|
maintainerName: string | null;
|
||||||
maintainerEmail: string | null;
|
maintainerEmail: string | null;
|
||||||
isSilenced: boolean;
|
isSilenced: boolean;
|
||||||
|
isMediaSilenced: boolean;
|
||||||
/** Format: url */
|
/** Format: url */
|
||||||
iconUrl: string | null;
|
iconUrl: string | null;
|
||||||
/** Format: url */
|
/** Format: url */
|
||||||
|
@ -5044,6 +5045,7 @@ export type operations = {
|
||||||
enableServiceWorker: boolean;
|
enableServiceWorker: boolean;
|
||||||
translatorAvailable: boolean;
|
translatorAvailable: boolean;
|
||||||
silencedHosts?: string[];
|
silencedHosts?: string[];
|
||||||
|
mediaSilencedHosts: string[];
|
||||||
pinnedUsers: string[];
|
pinnedUsers: string[];
|
||||||
hiddenTags: string[];
|
hiddenTags: string[];
|
||||||
blockedHosts: string[];
|
blockedHosts: string[];
|
||||||
|
@ -9371,6 +9373,7 @@ export type operations = {
|
||||||
perUserListTimelineCacheMax?: number;
|
perUserListTimelineCacheMax?: number;
|
||||||
notesPerOneAd?: number;
|
notesPerOneAd?: number;
|
||||||
silencedHosts?: string[] | null;
|
silencedHosts?: string[] | null;
|
||||||
|
mediaSilencedHosts?: string[] | null;
|
||||||
/** @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead. */
|
/** @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead. */
|
||||||
summalyProxy?: string | null;
|
summalyProxy?: string | null;
|
||||||
urlPreviewEnabled?: boolean;
|
urlPreviewEnabled?: boolean;
|
||||||
|
|
Loading…
Reference in a new issue