enhance: いくつかの設定ファイルの項目をコントロールパネルで設定するように (#16026)
* wip * Update CHANGELOG.md * feat: migrate to existing config value (#16030) * wip * Update CHANGELOG.md --------- Co-authored-by: anatawa12 <anatawa12@icloud.com>
This commit is contained in:
parent
51b5d740f6
commit
26506677c2
|
@ -215,20 +215,9 @@ proxyBypassHosts:
|
||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: true)
|
|
||||||
proxyRemoteFiles: true
|
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
|
||||||
signToActivityPubGet: true
|
|
||||||
|
|
||||||
allowedPrivateNetworks: [
|
allowedPrivateNetworks: [
|
||||||
'127.0.0.1/32'
|
'127.0.0.1/32'
|
||||||
]
|
]
|
||||||
|
|
||||||
# Disable automatic redirect for ActivityPub object lookup. (default: false)
|
|
||||||
# This is a strong defense against potential impersonation attacks if the viewer instance has inadequate validation.
|
|
||||||
# However it will make it impossible for other instances to lookup third-party user and notes through your URL.
|
|
||||||
#disallowExternalApRedirect: true
|
|
||||||
|
|
||||||
# Upload or download file size limits (bytes)
|
# Upload or download file size limits (bytes)
|
||||||
#maxFileSize: 262144000
|
#maxFileSize: 262144000
|
||||||
|
|
|
@ -227,12 +227,6 @@ proxyBypassHosts:
|
||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: true)
|
|
||||||
proxyRemoteFiles: true
|
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
|
||||||
signToActivityPubGet: true
|
|
||||||
|
|
||||||
# For security reasons, uploading attachments from the intranet is prohibited,
|
# For security reasons, uploading attachments from the intranet is prohibited,
|
||||||
# but exceptions can be made from the following settings. Default value is "undefined".
|
# but exceptions can be made from the following settings. Default value is "undefined".
|
||||||
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
||||||
|
@ -240,11 +234,6 @@ signToActivityPubGet: true
|
||||||
# '127.0.0.1/32'
|
# '127.0.0.1/32'
|
||||||
#]
|
#]
|
||||||
|
|
||||||
# Disable automatic redirect for ActivityPub object lookup. (default: false)
|
|
||||||
# This is a strong defense against potential impersonation attacks if the viewer instance has inadequate validation.
|
|
||||||
# However it will make it impossible for other instances to lookup third-party user and notes through your URL.
|
|
||||||
#disallowExternalApRedirect: true
|
|
||||||
|
|
||||||
# Upload or download file size limits (bytes)
|
# Upload or download file size limits (bytes)
|
||||||
#maxFileSize: 262144000
|
#maxFileSize: 262144000
|
||||||
|
|
||||||
|
|
|
@ -319,19 +319,12 @@ proxyBypassHosts:
|
||||||
# * Perform image compression (on a different server resource than the main process)
|
# * Perform image compression (on a different server resource than the main process)
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: true)
|
|
||||||
# Proxy remote files by this instance or mediaProxy to prevent remote files from running in remote domains.
|
|
||||||
proxyRemoteFiles: true
|
|
||||||
|
|
||||||
# Movie Thumbnail Generation URL
|
# Movie Thumbnail Generation URL
|
||||||
# There is no reference implementation.
|
# There is no reference implementation.
|
||||||
# For example, Misskey will point to the following URL:
|
# For example, Misskey will point to the following URL:
|
||||||
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
|
||||||
#videoThumbnailGenerator: https://example.com
|
#videoThumbnailGenerator: https://example.com
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
|
||||||
signToActivityPubGet: true
|
|
||||||
|
|
||||||
# For security reasons, uploading attachments from the intranet is prohibited,
|
# For security reasons, uploading attachments from the intranet is prohibited,
|
||||||
# but exceptions can be made from the following settings. Default value is "undefined".
|
# but exceptions can be made from the following settings. Default value is "undefined".
|
||||||
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
||||||
|
@ -339,11 +332,6 @@ signToActivityPubGet: true
|
||||||
# '127.0.0.1/32'
|
# '127.0.0.1/32'
|
||||||
#]
|
#]
|
||||||
|
|
||||||
# Disable automatic redirect for ActivityPub object lookup. (default: false)
|
|
||||||
# This is a strong defense against potential impersonation attacks if the viewer instance has inadequate validation.
|
|
||||||
# However it will make it impossible for other instances to lookup third-party user and notes through your URL.
|
|
||||||
#disallowExternalApRedirect: true
|
|
||||||
|
|
||||||
# Upload or download file size limits (bytes)
|
# Upload or download file size limits (bytes)
|
||||||
#maxFileSize: 262144000
|
#maxFileSize: 262144000
|
||||||
|
|
||||||
|
|
|
@ -202,12 +202,6 @@ proxyBypassHosts:
|
||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Proxy remote files (default: true)
|
|
||||||
proxyRemoteFiles: true
|
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
|
||||||
signToActivityPubGet: true
|
|
||||||
|
|
||||||
allowedPrivateNetworks: [
|
allowedPrivateNetworks: [
|
||||||
'127.0.0.1/32'
|
'127.0.0.1/32'
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
## 2025.5.1
|
## 2025.5.1
|
||||||
|
|
||||||
|
### Note
|
||||||
|
- 設定ファイルの以下の項目がコントロールパネルから設定するようになりました
|
||||||
|
- signToActivityPubGet
|
||||||
|
- proxyRemoteFiles
|
||||||
|
- disallowExternalApRedirect
|
||||||
|
- 許可しないかどうかではなく、許可するかどうかの設定(allowExternalApRedirect)になりました
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- Feat: 非ログインでサーバーを閲覧された際に、サーバー内のコンテンツを非公開にすることができるようになりました
|
- Feat: 非ログインでサーバーを閲覧された際に、サーバー内のコンテンツを非公開にすることができるようになりました
|
||||||
- モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます
|
- モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます
|
||||||
|
|
|
@ -221,9 +221,6 @@ id: "aidx"
|
||||||
# Media Proxy
|
# Media Proxy
|
||||||
#mediaProxy: https://example.com/proxy
|
#mediaProxy: https://example.com/proxy
|
||||||
|
|
||||||
# Sign to ActivityPub GET request (default: true)
|
|
||||||
signToActivityPubGet: true
|
|
||||||
|
|
||||||
#allowedPrivateNetworks: [
|
#allowedPrivateNetworks: [
|
||||||
# '127.0.0.1/32'
|
# '127.0.0.1/32'
|
||||||
#]
|
#]
|
||||||
|
|
|
@ -707,7 +707,7 @@ export interface Locale extends ILocale {
|
||||||
*/
|
*/
|
||||||
"cacheRemoteFiles": string;
|
"cacheRemoteFiles": string;
|
||||||
/**
|
/**
|
||||||
* この設定を有効にすると、リモートファイルをこのサーバーのストレージにキャッシュするようになります。画像の表示が高速になりますが、サーバーのストレージを多く消費します。リモートユーザーがどれほどキャッシュを保持するかは、ロールによるドライブ容量制限によって決定されます。この制限を超えた場合、古いファイルからキャッシュが削除されリンクになります。この設定が無効の場合、リモートのファイルを最初からリンクとして保持しますが、画像のサムネイル生成やユーザーのプライバシー保護のために、default.ymlでproxyRemoteFilesをtrueにすることをお勧めします。
|
* この設定を有効にすると、リモートファイルをこのサーバーのストレージにキャッシュするようになります。画像の表示が高速になりますが、サーバーのストレージを多く消費します。リモートユーザーがどれほどキャッシュを保持するかは、ロールによるドライブ容量制限によって決定されます。この制限を超えた場合、古いファイルからキャッシュが削除されリンクになります。この設定が無効の場合、リモートのファイルを最初からリンクとして保持します。
|
||||||
*/
|
*/
|
||||||
"cacheRemoteFilesDescription": string;
|
"cacheRemoteFilesDescription": string;
|
||||||
/**
|
/**
|
||||||
|
@ -3186,6 +3186,10 @@ export interface Locale extends ILocale {
|
||||||
* 反映には再起動が必要です。
|
* 反映には再起動が必要です。
|
||||||
*/
|
*/
|
||||||
"needReloadToApply": string;
|
"needReloadToApply": string;
|
||||||
|
/**
|
||||||
|
* 反映にはサーバーの再起動が必要です。
|
||||||
|
*/
|
||||||
|
"needToRestartServerToApply": string;
|
||||||
/**
|
/**
|
||||||
* タイトルバーを表示する
|
* タイトルバーを表示する
|
||||||
*/
|
*/
|
||||||
|
@ -6452,6 +6456,30 @@ export interface Locale extends ILocale {
|
||||||
* このサーバーを利用するのが自分だけの場合、このモードを有効にすることで動作が最適化されます。
|
* このサーバーを利用するのが自分だけの場合、このモードを有効にすることで動作が最適化されます。
|
||||||
*/
|
*/
|
||||||
"singleUserMode_description": string;
|
"singleUserMode_description": string;
|
||||||
|
/**
|
||||||
|
* GETリクエストに署名する
|
||||||
|
*/
|
||||||
|
"signToActivityPubGet": string;
|
||||||
|
/**
|
||||||
|
* 通常は有効にしてください。連合の通信に関する問題がある場合に、無効にすると改善することがありますが、逆にサーバーによっては通信が不可になることがあります。
|
||||||
|
*/
|
||||||
|
"signToActivityPubGet_description": string;
|
||||||
|
/**
|
||||||
|
* リモートファイルをプロキシする
|
||||||
|
*/
|
||||||
|
"proxyRemoteFiles": string;
|
||||||
|
/**
|
||||||
|
* 有効にすると、リモートのファイルをプロキシして提供します。画像のサムネイル生成やユーザーのプライバシー保護に役立ちます。
|
||||||
|
*/
|
||||||
|
"proxyRemoteFiles_description": string;
|
||||||
|
/**
|
||||||
|
* ActivityPub経由の照会にリダイレクトを許可する
|
||||||
|
*/
|
||||||
|
"allowExternalApRedirect": string;
|
||||||
|
/**
|
||||||
|
* 有効にすると、他のサーバーがこのサーバーを通して第三者のコンテンツを照会することが可能になりますが、コンテンツのなりすましが発生する可能性があります。
|
||||||
|
*/
|
||||||
|
"allowExternalApRedirect_description": string;
|
||||||
/**
|
/**
|
||||||
* 非利用者に対するユーザー作成コンテンツの公開範囲
|
* 非利用者に対するユーザー作成コンテンツの公開範囲
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -172,7 +172,7 @@ emojiUrl: "絵文字画像URL"
|
||||||
addEmoji: "絵文字を追加"
|
addEmoji: "絵文字を追加"
|
||||||
settingGuide: "おすすめ設定"
|
settingGuide: "おすすめ設定"
|
||||||
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
||||||
cacheRemoteFilesDescription: "この設定を有効にすると、リモートファイルをこのサーバーのストレージにキャッシュするようになります。画像の表示が高速になりますが、サーバーのストレージを多く消費します。リモートユーザーがどれほどキャッシュを保持するかは、ロールによるドライブ容量制限によって決定されます。この制限を超えた場合、古いファイルからキャッシュが削除されリンクになります。この設定が無効の場合、リモートのファイルを最初からリンクとして保持しますが、画像のサムネイル生成やユーザーのプライバシー保護のために、default.ymlでproxyRemoteFilesをtrueにすることをお勧めします。"
|
cacheRemoteFilesDescription: "この設定を有効にすると、リモートファイルをこのサーバーのストレージにキャッシュするようになります。画像の表示が高速になりますが、サーバーのストレージを多く消費します。リモートユーザーがどれほどキャッシュを保持するかは、ロールによるドライブ容量制限によって決定されます。この制限を超えた場合、古いファイルからキャッシュが削除されリンクになります。この設定が無効の場合、リモートのファイルを最初からリンクとして保持します。"
|
||||||
youCanCleanRemoteFilesCache: "ファイル管理の🗑️ボタンで全てのキャッシュを削除できます。"
|
youCanCleanRemoteFilesCache: "ファイル管理の🗑️ボタンで全てのキャッシュを削除できます。"
|
||||||
cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする"
|
cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする"
|
||||||
cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになります。"
|
cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになります。"
|
||||||
|
@ -792,6 +792,7 @@ wide: "広い"
|
||||||
narrow: "狭い"
|
narrow: "狭い"
|
||||||
reloadToApplySetting: "設定はページリロード後に反映されます。"
|
reloadToApplySetting: "設定はページリロード後に反映されます。"
|
||||||
needReloadToApply: "反映には再起動が必要です。"
|
needReloadToApply: "反映には再起動が必要です。"
|
||||||
|
needToRestartServerToApply: "反映にはサーバーの再起動が必要です。"
|
||||||
showTitlebar: "タイトルバーを表示する"
|
showTitlebar: "タイトルバーを表示する"
|
||||||
clearCache: "キャッシュをクリア"
|
clearCache: "キャッシュをクリア"
|
||||||
onlineUsersCount: "{n}人がオンライン"
|
onlineUsersCount: "{n}人がオンライン"
|
||||||
|
@ -1639,6 +1640,12 @@ _serverSettings:
|
||||||
deliverSuspendedSoftwareDescription: "脆弱性などの理由で、サーバーのソフトウェアの名前及びバージョンの範囲を指定して配信を停止できます。このバージョン情報はサーバーが提供したものであり、信頼性は保証されません。バージョン指定には semver の範囲指定が使用できますが、>= 2024.3.1 と指定すると 2024.3.1-custom.0 のようなカスタムバージョンが含まれないため、>= 2024.3.1-0 のように prerelease の指定を行うことを推奨します。"
|
deliverSuspendedSoftwareDescription: "脆弱性などの理由で、サーバーのソフトウェアの名前及びバージョンの範囲を指定して配信を停止できます。このバージョン情報はサーバーが提供したものであり、信頼性は保証されません。バージョン指定には semver の範囲指定が使用できますが、>= 2024.3.1 と指定すると 2024.3.1-custom.0 のようなカスタムバージョンが含まれないため、>= 2024.3.1-0 のように prerelease の指定を行うことを推奨します。"
|
||||||
singleUserMode: "お一人様モード"
|
singleUserMode: "お一人様モード"
|
||||||
singleUserMode_description: "このサーバーを利用するのが自分だけの場合、このモードを有効にすることで動作が最適化されます。"
|
singleUserMode_description: "このサーバーを利用するのが自分だけの場合、このモードを有効にすることで動作が最適化されます。"
|
||||||
|
signToActivityPubGet: "GETリクエストに署名する"
|
||||||
|
signToActivityPubGet_description: "通常は有効にしてください。連合の通信に関する問題がある場合に、無効にすると改善することがありますが、逆にサーバーによっては通信が不可になることがあります。"
|
||||||
|
proxyRemoteFiles: "リモートファイルをプロキシする"
|
||||||
|
proxyRemoteFiles_description: "有効にすると、リモートのファイルをプロキシして提供します。画像のサムネイル生成やユーザーのプライバシー保護に役立ちます。"
|
||||||
|
allowExternalApRedirect: "ActivityPub経由の照会にリダイレクトを許可する"
|
||||||
|
allowExternalApRedirect_description: "有効にすると、他のサーバーがこのサーバーを通して第三者のコンテンツを照会することが可能になりますが、コンテンツのなりすましが発生する可能性があります。"
|
||||||
userGeneratedContentsVisibilityForVisitor: "非利用者に対するユーザー作成コンテンツの公開範囲"
|
userGeneratedContentsVisibilityForVisitor: "非利用者に対するユーザー作成コンテンツの公開範囲"
|
||||||
userGeneratedContentsVisibilityForVisitor_description: "モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます。"
|
userGeneratedContentsVisibilityForVisitor_description: "モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます。"
|
||||||
userGeneratedContentsVisibilityForVisitor_description2: "サーバーで受信したリモートのコンテンツを含め、サーバー内の全てのコンテンツを無条件でインターネットに公開することはリスクが伴います。特に、分散型の特性を知らない閲覧者にとっては、リモートのコンテンツであってもサーバー内で作成されたコンテンツであると誤って認識してしまう可能性があるため、注意が必要です。"
|
userGeneratedContentsVisibilityForVisitor_description2: "サーバーで受信したリモートのコンテンツを含め、サーバー内の全てのコンテンツを無条件でインターネットに公開することはリスクが伴います。特に、分散型の特性を知らない閲覧者にとっては、リモートのコンテンツであってもサーバー内で作成されたコンテンツであると誤って認識してしまう可能性があるため、注意が必要です。"
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {loadConfig} from "./js/migration-config.js";
|
||||||
|
|
||||||
|
export class MigrateSomeConfigFileSettingsToMeta1746949539915 {
|
||||||
|
name = 'MigrateSomeConfigFileSettingsToMeta1746949539915'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
const config = loadConfig();
|
||||||
|
// $1 cannot be used in ALTER TABLE queries
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "proxyRemoteFiles" boolean NOT NULL DEFAULT ${config.proxyRemoteFiles}`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "signToActivityPubGet" boolean NOT NULL DEFAULT ${config.signToActivityPubGet}`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "allowExternalApRedirect" boolean NOT NULL DEFAULT ${!config.disallowExternalApRedirect}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "allowExternalApRedirect"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "signToActivityPubGet"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyRemoteFiles"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,29 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { path as configYamlPath } from '../../built/config.js';
|
||||||
|
import * as yaml from 'js-yaml';
|
||||||
|
import fs from "node:fs";
|
||||||
|
|
||||||
export function isConcurrentIndexMigrationEnabled() {
|
export function isConcurrentIndexMigrationEnabled() {
|
||||||
return process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1';
|
return process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loadedConfigCache = undefined;
|
||||||
|
|
||||||
|
function loadConfigInternal() {
|
||||||
|
const config = yaml.load(fs.readFileSync(configYamlPath, 'utf-8'));
|
||||||
|
|
||||||
|
return {
|
||||||
|
disallowExternalApRedirect: Boolean(config.disallowExternalApRedirect ?? false),
|
||||||
|
proxyRemoteFiles: Boolean(config.proxyRemoteFiles ?? false),
|
||||||
|
signToActivityPubGet: Boolean(config.signToActivityPubGet ?? true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadConfig() {
|
||||||
|
if (loadedConfigCache === undefined) {
|
||||||
|
loadedConfigCache = loadConfigInternal();
|
||||||
|
}
|
||||||
|
return loadedConfigCache;
|
||||||
|
}
|
||||||
|
|
|
@ -79,7 +79,6 @@ type Source = {
|
||||||
proxyBypassHosts?: string[];
|
proxyBypassHosts?: string[];
|
||||||
|
|
||||||
allowedPrivateNetworks?: string[];
|
allowedPrivateNetworks?: string[];
|
||||||
disallowExternalApRedirect?: boolean;
|
|
||||||
|
|
||||||
maxFileSize?: number;
|
maxFileSize?: number;
|
||||||
|
|
||||||
|
@ -100,11 +99,8 @@ type Source = {
|
||||||
inboxJobMaxAttempts?: number;
|
inboxJobMaxAttempts?: number;
|
||||||
|
|
||||||
mediaProxy?: string;
|
mediaProxy?: string;
|
||||||
proxyRemoteFiles?: boolean;
|
|
||||||
videoThumbnailGenerator?: string;
|
videoThumbnailGenerator?: string;
|
||||||
|
|
||||||
signToActivityPubGet?: boolean;
|
|
||||||
|
|
||||||
perChannelMaxNoteCacheCount?: number;
|
perChannelMaxNoteCacheCount?: number;
|
||||||
perUserNotificationsMaxCount?: number;
|
perUserNotificationsMaxCount?: number;
|
||||||
deactivateAntennaThreshold?: number;
|
deactivateAntennaThreshold?: number;
|
||||||
|
@ -156,7 +152,6 @@ export type Config = {
|
||||||
proxySmtp: string | undefined;
|
proxySmtp: string | undefined;
|
||||||
proxyBypassHosts: string[] | undefined;
|
proxyBypassHosts: string[] | undefined;
|
||||||
allowedPrivateNetworks: string[] | undefined;
|
allowedPrivateNetworks: string[] | undefined;
|
||||||
disallowExternalApRedirect: boolean;
|
|
||||||
maxFileSize: number;
|
maxFileSize: number;
|
||||||
clusterLimit: number | undefined;
|
clusterLimit: number | undefined;
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -170,8 +165,6 @@ export type Config = {
|
||||||
relationshipJobPerSec: number | undefined;
|
relationshipJobPerSec: number | undefined;
|
||||||
deliverJobMaxAttempts: number | undefined;
|
deliverJobMaxAttempts: number | undefined;
|
||||||
inboxJobMaxAttempts: number | undefined;
|
inboxJobMaxAttempts: number | undefined;
|
||||||
proxyRemoteFiles: boolean | undefined;
|
|
||||||
signToActivityPubGet: boolean | undefined;
|
|
||||||
logging?: {
|
logging?: {
|
||||||
sql?: {
|
sql?: {
|
||||||
disableQueryTruncation?: boolean,
|
disableQueryTruncation?: boolean,
|
||||||
|
@ -229,7 +222,7 @@ const dir = `${_dirname}/../../../.config`;
|
||||||
/**
|
/**
|
||||||
* Path of configuration file
|
* Path of configuration file
|
||||||
*/
|
*/
|
||||||
const path = process.env.MISSKEY_CONFIG_YML
|
export const path = process.env.MISSKEY_CONFIG_YML
|
||||||
? resolve(dir, process.env.MISSKEY_CONFIG_YML)
|
? resolve(dir, process.env.MISSKEY_CONFIG_YML)
|
||||||
: process.env.NODE_ENV === 'test'
|
: process.env.NODE_ENV === 'test'
|
||||||
? resolve(dir, 'test.yml')
|
? resolve(dir, 'test.yml')
|
||||||
|
@ -300,7 +293,6 @@ export function loadConfig(): Config {
|
||||||
proxySmtp: config.proxySmtp,
|
proxySmtp: config.proxySmtp,
|
||||||
proxyBypassHosts: config.proxyBypassHosts,
|
proxyBypassHosts: config.proxyBypassHosts,
|
||||||
allowedPrivateNetworks: config.allowedPrivateNetworks,
|
allowedPrivateNetworks: config.allowedPrivateNetworks,
|
||||||
disallowExternalApRedirect: config.disallowExternalApRedirect ?? false,
|
|
||||||
maxFileSize: config.maxFileSize ?? 262144000,
|
maxFileSize: config.maxFileSize ?? 262144000,
|
||||||
clusterLimit: config.clusterLimit,
|
clusterLimit: config.clusterLimit,
|
||||||
outgoingAddress: config.outgoingAddress,
|
outgoingAddress: config.outgoingAddress,
|
||||||
|
@ -313,8 +305,6 @@ export function loadConfig(): Config {
|
||||||
relationshipJobPerSec: config.relationshipJobPerSec,
|
relationshipJobPerSec: config.relationshipJobPerSec,
|
||||||
deliverJobMaxAttempts: config.deliverJobMaxAttempts,
|
deliverJobMaxAttempts: config.deliverJobMaxAttempts,
|
||||||
inboxJobMaxAttempts: config.inboxJobMaxAttempts,
|
inboxJobMaxAttempts: config.inboxJobMaxAttempts,
|
||||||
proxyRemoteFiles: config.proxyRemoteFiles,
|
|
||||||
signToActivityPubGet: config.signToActivityPubGet ?? true,
|
|
||||||
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
mediaProxy: externalMediaProxy ?? internalMediaProxy,
|
||||||
externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
|
externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
|
||||||
videoThumbnailGenerator: config.videoThumbnailGenerator ?
|
videoThumbnailGenerator: config.videoThumbnailGenerator ?
|
||||||
|
|
|
@ -104,7 +104,7 @@ export class Resolver {
|
||||||
throw new IdentifiableError('09d79f9e-64f1-4316-9cfa-e75c4d091574', 'Instance is blocked');
|
throw new IdentifiableError('09d79f9e-64f1-4316-9cfa-e75c4d091574', 'Instance is blocked');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.signToActivityPubGet && !this.user) {
|
if (this.meta.signToActivityPubGet && !this.user) {
|
||||||
this.user = await this.systemAccountService.fetch('actor');
|
this.user = await this.systemAccountService.fetch('actor');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { DriveFilesRepository } from '@/models/_.js';
|
import type { DriveFilesRepository, MiMeta } from '@/models/_.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
|
@ -34,6 +34,9 @@ export class DriveFileEntityService {
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
private config: Config,
|
private config: Config,
|
||||||
|
|
||||||
|
@Inject(DI.meta)
|
||||||
|
private meta: MiMeta,
|
||||||
|
|
||||||
@Inject(DI.driveFilesRepository)
|
@Inject(DI.driveFilesRepository)
|
||||||
private driveFilesRepository: DriveFilesRepository,
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
|
|
||||||
|
@ -95,7 +98,7 @@ export class DriveFileEntityService {
|
||||||
return this.getProxiedUrl(file.uri, 'static');
|
return this.getProxiedUrl(file.uri, 'static');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.uri != null && file.isLink && this.config.proxyRemoteFiles) {
|
if (file.uri != null && file.isLink && this.meta.proxyRemoteFiles) {
|
||||||
// リモートかつ期限切れはローカルプロキシを試みる
|
// リモートかつ期限切れはローカルプロキシを試みる
|
||||||
// 従来は/files/${thumbnailAccessKey}にアクセスしていたが、
|
// 従来は/files/${thumbnailAccessKey}にアクセスしていたが、
|
||||||
// /filesはメディアプロキシにリダイレクトするようにしたため直接メディアプロキシを指定する
|
// /filesはメディアプロキシにリダイレクトするようにしたため直接メディアプロキシを指定する
|
||||||
|
@ -115,7 +118,7 @@ export class DriveFileEntityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// リモートかつ期限切れはローカルプロキシを試みる
|
// リモートかつ期限切れはローカルプロキシを試みる
|
||||||
if (file.uri != null && file.isLink && this.config.proxyRemoteFiles) {
|
if (file.uri != null && file.isLink && this.meta.proxyRemoteFiles) {
|
||||||
const key = file.webpublicAccessKey;
|
const key = file.webpublicAccessKey;
|
||||||
|
|
||||||
if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外
|
if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外
|
||||||
|
|
|
@ -680,6 +680,21 @@ export class MiMeta {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
public singleUserMode: boolean;
|
public singleUserMode: boolean;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
public proxyRemoteFiles: boolean;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
public signToActivityPubGet: boolean;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
public allowExternalApRedirect: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SoftwareSuspension = {
|
export type SoftwareSuspension = {
|
||||||
|
|
|
@ -108,7 +108,7 @@ export class ServerService implements OnApplicationShutdown {
|
||||||
// this will break lookup that involve copying a URL from a third-party server, like trying to lookup http://charlie.example.com/@alice@alice.com
|
// this will break lookup that involve copying a URL from a third-party server, like trying to lookup http://charlie.example.com/@alice@alice.com
|
||||||
//
|
//
|
||||||
// this is not required by standard but protect us from peers that did not validate final URL.
|
// this is not required by standard but protect us from peers that did not validate final URL.
|
||||||
if (this.config.disallowExternalApRedirect) {
|
if (!this.meta.allowExternalApRedirect) {
|
||||||
const maybeApLookupRegex = /application\/activity\+json|application\/ld\+json.+activitystreams/i;
|
const maybeApLookupRegex = /application\/activity\+json|application\/ld\+json.+activitystreams/i;
|
||||||
fastify.addHook('onSend', (request, reply, _, done) => {
|
fastify.addHook('onSend', (request, reply, _, done) => {
|
||||||
const location = reply.getHeader('location');
|
const location = reply.getHeader('location');
|
||||||
|
@ -133,8 +133,8 @@ export class ServerService implements OnApplicationShutdown {
|
||||||
reply.header('content-type', 'text/plain; charset=utf-8');
|
reply.header('content-type', 'text/plain; charset=utf-8');
|
||||||
reply.header('link', `<${encodeURI(location)}>; rel="canonical"`);
|
reply.header('link', `<${encodeURI(location)}>; rel="canonical"`);
|
||||||
done(null, [
|
done(null, [
|
||||||
"Refusing to relay remote ActivityPub object lookup.",
|
'Refusing to relay remote ActivityPub object lookup.',
|
||||||
"",
|
'',
|
||||||
`Please remove 'application/activity+json' and 'application/ld+json' from the Accept header or fetch using the authoritative URL at ${location}.`,
|
`Please remove 'application/activity+json' and 'application/ld+json' from the Accept header or fetch using the authoritative URL at ${location}.`,
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
});
|
});
|
||||||
|
|
|
@ -555,6 +555,18 @@ export const meta = {
|
||||||
enum: ['all', 'local', 'none'],
|
enum: ['all', 'local', 'none'],
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
proxyRemoteFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
signToActivityPubGet: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
allowExternalApRedirect: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -702,6 +714,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
deliverSuspendedSoftware: instance.deliverSuspendedSoftware,
|
deliverSuspendedSoftware: instance.deliverSuspendedSoftware,
|
||||||
singleUserMode: instance.singleUserMode,
|
singleUserMode: instance.singleUserMode,
|
||||||
ugcVisibilityForVisitor: instance.ugcVisibilityForVisitor,
|
ugcVisibilityForVisitor: instance.ugcVisibilityForVisitor,
|
||||||
|
proxyRemoteFiles: instance.proxyRemoteFiles,
|
||||||
|
signToActivityPubGet: instance.signToActivityPubGet,
|
||||||
|
allowExternalApRedirect: instance.allowExternalApRedirect,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,9 @@ export const paramDef = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: ['all', 'local', 'none'],
|
enum: ['all', 'local', 'none'],
|
||||||
},
|
},
|
||||||
|
proxyRemoteFiles: { type: 'boolean' },
|
||||||
|
signToActivityPubGet: { type: 'boolean' },
|
||||||
|
allowExternalApRedirect: { type: 'boolean' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -703,6 +706,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
set.ugcVisibilityForVisitor = ps.ugcVisibilityForVisitor;
|
set.ugcVisibilityForVisitor = ps.ugcVisibilityForVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.proxyRemoteFiles !== undefined) {
|
||||||
|
set.proxyRemoteFiles = ps.proxyRemoteFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.signToActivityPubGet !== undefined) {
|
||||||
|
set.signToActivityPubGet = ps.signToActivityPubGet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.allowExternalApRedirect !== undefined) {
|
||||||
|
set.allowExternalApRedirect = ps.allowExternalApRedirect;
|
||||||
|
}
|
||||||
|
|
||||||
const before = await this.metaService.fetch(true);
|
const before = await this.metaService.fetch(true);
|
||||||
|
|
||||||
await this.metaService.update(set);
|
await this.metaService.update(set);
|
||||||
|
|
|
@ -17,8 +17,6 @@ proxyBypassHosts:
|
||||||
- www.recaptcha.net
|
- www.recaptcha.net
|
||||||
- hcaptcha.com
|
- hcaptcha.com
|
||||||
- challenges.cloudflare.com
|
- challenges.cloudflare.com
|
||||||
proxyRemoteFiles: true
|
|
||||||
signToActivityPubGet: true
|
|
||||||
allowedPrivateNetworks:
|
allowedPrivateNetworks:
|
||||||
- 127.0.0.1/32
|
- 127.0.0.1/32
|
||||||
- 172.20.0.0/16
|
- 172.20.0.0/16
|
||||||
|
|
|
@ -86,28 +86,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder>
|
|
||||||
<template #icon><i class="ti ti-cloud"></i></template>
|
|
||||||
<template #label>{{ i18n.ts.files }}</template>
|
|
||||||
<template v-if="filesForm.modified.value" #footer>
|
|
||||||
<MkFormFooter :form="filesForm"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="_gaps">
|
|
||||||
<MkSwitch v-model="filesForm.state.cacheRemoteFiles">
|
|
||||||
<template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
|
||||||
<template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
|
|
||||||
<template v-if="filesForm.state.cacheRemoteFiles">
|
|
||||||
<MkSwitch v-model="filesForm.state.cacheRemoteSensitiveFiles">
|
|
||||||
<template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
|
||||||
<template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
<template #icon><i class="ti ti-world-cog"></i></template>
|
<template #icon><i class="ti ti-world-cog"></i></template>
|
||||||
<template #label>ServiceWorker</template>
|
<template #label>ServiceWorker</template>
|
||||||
|
@ -255,6 +233,36 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkSwitch v-model="federationForm.state.signToActivityPubGet">
|
||||||
|
<template #label>{{ i18n.ts._serverSettings.signToActivityPubGet }}<span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||||
|
<template #caption>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
|
||||||
|
<MkSwitch v-model="federationForm.state.proxyRemoteFiles">
|
||||||
|
<template #label>{{ i18n.ts._serverSettings.proxyRemoteFiles }}<span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||||
|
<template #caption>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
|
||||||
|
<MkSwitch v-model="federationForm.state.allowExternalApRedirect">
|
||||||
|
<template #label>{{ i18n.ts._serverSettings.allowExternalApRedirect }}<span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||||
|
<template #caption>
|
||||||
|
<div>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</div>
|
||||||
|
<div>{{ i18n.ts.needToRestartServerToApply }}</div>
|
||||||
|
</template>
|
||||||
|
</MkSwitch>
|
||||||
|
|
||||||
|
<MkSwitch v-model="federationForm.state.cacheRemoteFiles">
|
||||||
|
<template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||||
|
<template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
|
||||||
|
<template v-if="federationForm.state.cacheRemoteFiles">
|
||||||
|
<MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles">
|
||||||
|
<template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||||
|
<template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
@ -338,17 +346,6 @@ const pinnedUsersForm = useForm({
|
||||||
fetchInstance(true);
|
fetchInstance(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
const filesForm = useForm({
|
|
||||||
cacheRemoteFiles: meta.cacheRemoteFiles,
|
|
||||||
cacheRemoteSensitiveFiles: meta.cacheRemoteSensitiveFiles,
|
|
||||||
}, async (state) => {
|
|
||||||
await os.apiWithDialog('admin/update-meta', {
|
|
||||||
cacheRemoteFiles: state.cacheRemoteFiles,
|
|
||||||
cacheRemoteSensitiveFiles: state.cacheRemoteSensitiveFiles,
|
|
||||||
});
|
|
||||||
fetchInstance(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
const serviceWorkerForm = useForm({
|
const serviceWorkerForm = useForm({
|
||||||
enableServiceWorker: meta.enableServiceWorker,
|
enableServiceWorker: meta.enableServiceWorker,
|
||||||
swPublicKey: meta.swPublickey ?? '',
|
swPublicKey: meta.swPublickey ?? '',
|
||||||
|
@ -394,11 +391,21 @@ const federationForm = useForm({
|
||||||
federation: meta.federation,
|
federation: meta.federation,
|
||||||
federationHosts: meta.federationHosts.join('\n'),
|
federationHosts: meta.federationHosts.join('\n'),
|
||||||
deliverSuspendedSoftware: meta.deliverSuspendedSoftware,
|
deliverSuspendedSoftware: meta.deliverSuspendedSoftware,
|
||||||
|
signToActivityPubGet: meta.signToActivityPubGet,
|
||||||
|
proxyRemoteFiles: meta.proxyRemoteFiles,
|
||||||
|
allowExternalApRedirect: meta.allowExternalApRedirect,
|
||||||
|
cacheRemoteFiles: meta.cacheRemoteFiles,
|
||||||
|
cacheRemoteSensitiveFiles: meta.cacheRemoteSensitiveFiles,
|
||||||
}, async (state) => {
|
}, async (state) => {
|
||||||
await os.apiWithDialog('admin/update-meta', {
|
await os.apiWithDialog('admin/update-meta', {
|
||||||
federation: state.federation,
|
federation: state.federation,
|
||||||
federationHosts: state.federationHosts.split('\n'),
|
federationHosts: state.federationHosts.split('\n'),
|
||||||
deliverSuspendedSoftware: state.deliverSuspendedSoftware,
|
deliverSuspendedSoftware: state.deliverSuspendedSoftware,
|
||||||
|
signToActivityPubGet: state.signToActivityPubGet,
|
||||||
|
proxyRemoteFiles: state.proxyRemoteFiles,
|
||||||
|
allowExternalApRedirect: state.allowExternalApRedirect,
|
||||||
|
cacheRemoteFiles: state.cacheRemoteFiles,
|
||||||
|
cacheRemoteSensitiveFiles: state.cacheRemoteSensitiveFiles,
|
||||||
});
|
});
|
||||||
fetchInstance(true);
|
fetchInstance(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -8785,6 +8785,9 @@ export type operations = {
|
||||||
singleUserMode: boolean;
|
singleUserMode: boolean;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
ugcVisibilityForVisitor: 'all' | 'local' | 'none';
|
ugcVisibilityForVisitor: 'all' | 'local' | 'none';
|
||||||
|
proxyRemoteFiles: boolean;
|
||||||
|
signToActivityPubGet: boolean;
|
||||||
|
allowExternalApRedirect: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -11458,6 +11461,9 @@ export type operations = {
|
||||||
singleUserMode?: boolean;
|
singleUserMode?: boolean;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
ugcVisibilityForVisitor?: 'all' | 'local' | 'none';
|
ugcVisibilityForVisitor?: 'all' | 'local' | 'none';
|
||||||
|
proxyRemoteFiles?: boolean;
|
||||||
|
signToActivityPubGet?: boolean;
|
||||||
|
allowExternalApRedirect?: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue