diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-05-12 16:55:01 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-12 16:55:01 +0900 |
| commit | 26506677c2debbe7d58b26f1e5c98b834bc72e0f (patch) | |
| tree | f6b8da6b0e7ddf837b41895ef4de1e3f36275d0b /packages | |
| parent | 🎨 (diff) | |
| download | misskey-26506677c2debbe7d58b26f1e5c98b834bc72e0f.tar.gz misskey-26506677c2debbe7d58b26f1e5c98b834bc72e0f.tar.bz2 misskey-26506677c2debbe7d58b26f1e5c98b834bc72e0f.zip | |
enhance: いくつかの設定ファイルの項目をコントロールパネルで設定するように (#16026)
* wip
* Update CHANGELOG.md
* feat: migrate to existing config value (#16030)
* wip
* Update CHANGELOG.md
---------
Co-authored-by: anatawa12 <anatawa12@icloud.com>
Diffstat (limited to 'packages')
12 files changed, 149 insertions, 53 deletions
diff --git a/packages/backend/migration/1746949539915-migrateSomeConfigFileSettingsToMeta.js b/packages/backend/migration/1746949539915-migrateSomeConfigFileSettingsToMeta.js new file mode 100644 index 0000000000..3243f43b91 --- /dev/null +++ b/packages/backend/migration/1746949539915-migrateSomeConfigFileSettingsToMeta.js @@ -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"`); + } +} diff --git a/packages/backend/migration/js/migration-config.js b/packages/backend/migration/js/migration-config.js index 8cfbb21470..853735661b 100644 --- a/packages/backend/migration/js/migration-config.js +++ b/packages/backend/migration/js/migration-config.js @@ -3,6 +3,29 @@ * 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() { 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; +} diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 646fa07911..9031096745 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -79,7 +79,6 @@ type Source = { proxyBypassHosts?: string[]; allowedPrivateNetworks?: string[]; - disallowExternalApRedirect?: boolean; maxFileSize?: number; @@ -100,11 +99,8 @@ type Source = { inboxJobMaxAttempts?: number; mediaProxy?: string; - proxyRemoteFiles?: boolean; videoThumbnailGenerator?: string; - signToActivityPubGet?: boolean; - perChannelMaxNoteCacheCount?: number; perUserNotificationsMaxCount?: number; deactivateAntennaThreshold?: number; @@ -156,7 +152,6 @@ export type Config = { proxySmtp: string | undefined; proxyBypassHosts: string[] | undefined; allowedPrivateNetworks: string[] | undefined; - disallowExternalApRedirect: boolean; maxFileSize: number; clusterLimit: number | undefined; id: string; @@ -170,8 +165,6 @@ export type Config = { relationshipJobPerSec: number | undefined; deliverJobMaxAttempts: number | undefined; inboxJobMaxAttempts: number | undefined; - proxyRemoteFiles: boolean | undefined; - signToActivityPubGet: boolean | undefined; logging?: { sql?: { disableQueryTruncation?: boolean, @@ -229,7 +222,7 @@ const dir = `${_dirname}/../../../.config`; /** * 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) : process.env.NODE_ENV === 'test' ? resolve(dir, 'test.yml') @@ -300,7 +293,6 @@ export function loadConfig(): Config { proxySmtp: config.proxySmtp, proxyBypassHosts: config.proxyBypassHosts, allowedPrivateNetworks: config.allowedPrivateNetworks, - disallowExternalApRedirect: config.disallowExternalApRedirect ?? false, maxFileSize: config.maxFileSize ?? 262144000, clusterLimit: config.clusterLimit, outgoingAddress: config.outgoingAddress, @@ -313,8 +305,6 @@ export function loadConfig(): Config { relationshipJobPerSec: config.relationshipJobPerSec, deliverJobMaxAttempts: config.deliverJobMaxAttempts, inboxJobMaxAttempts: config.inboxJobMaxAttempts, - proxyRemoteFiles: config.proxyRemoteFiles, - signToActivityPubGet: config.signToActivityPubGet ?? true, mediaProxy: externalMediaProxy ?? internalMediaProxy, externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy, videoThumbnailGenerator: config.videoThumbnailGenerator ? diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index 2534899ad1..646150455b 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -104,7 +104,7 @@ export class Resolver { 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'); } diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index c485555f90..a6f7f369a6 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -6,7 +6,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; 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 { Packed } from '@/misc/json-schema.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; @@ -34,6 +34,9 @@ export class DriveFileEntityService { @Inject(DI.config) private config: Config, + @Inject(DI.meta) + private meta: MiMeta, + @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, @@ -95,7 +98,7 @@ export class DriveFileEntityService { 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はメディアプロキシにリダイレクトするようにしたため直接メディアプロキシを指定する @@ -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; if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外 diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index e3625b247c..545173ff3c 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -680,6 +680,21 @@ export class MiMeta { default: false, }) 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 = { diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index c859f1d82c..23c085ee27 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -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 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; fastify.addHook('onSend', (request, reply, _, done) => { 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('link', `<${encodeURI(location)}>; rel="canonical"`); 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}.`, ].join('\n')); }); diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index cd36985485..0cd46b614f 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -555,6 +555,18 @@ export const meta = { enum: ['all', 'local', 'none'], 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; @@ -702,6 +714,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- deliverSuspendedSoftware: instance.deliverSuspendedSoftware, singleUserMode: instance.singleUserMode, ugcVisibilityForVisitor: instance.ugcVisibilityForVisitor, + proxyRemoteFiles: instance.proxyRemoteFiles, + signToActivityPubGet: instance.signToActivityPubGet, + allowExternalApRedirect: instance.allowExternalApRedirect, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index a96fbd759c..0e3569d667 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -201,6 +201,9 @@ export const paramDef = { type: 'string', enum: ['all', 'local', 'none'], }, + proxyRemoteFiles: { type: 'boolean' }, + signToActivityPubGet: { type: 'boolean' }, + allowExternalApRedirect: { type: 'boolean' }, }, required: [], } as const; @@ -703,6 +706,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- 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); await this.metaService.update(set); diff --git a/packages/backend/test-federation/.config/example.default.yml b/packages/backend/test-federation/.config/example.default.yml index 28d51ac86e..fd20613885 100644 --- a/packages/backend/test-federation/.config/example.default.yml +++ b/packages/backend/test-federation/.config/example.default.yml @@ -17,8 +17,6 @@ proxyBypassHosts: - www.recaptcha.net - hcaptcha.com - challenges.cloudflare.com -proxyRemoteFiles: true -signToActivityPubGet: true allowedPrivateNetworks: - 127.0.0.1/32 - 172.20.0.0/16 diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 82083b4e08..8e0c373a7b 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -87,28 +87,6 @@ SPDX-License-Identifier: AGPL-3.0-only </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> <template #icon><i class="ti ti-world-cog"></i></template> <template #label>ServiceWorker</template> <template v-if="serviceWorkerForm.modified.value" #footer> @@ -255,6 +233,36 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </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> </MkFolder> @@ -338,17 +346,6 @@ const pinnedUsersForm = useForm({ 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({ enableServiceWorker: meta.enableServiceWorker, swPublicKey: meta.swPublickey ?? '', @@ -394,11 +391,21 @@ const federationForm = useForm({ federation: meta.federation, federationHosts: meta.federationHosts.join('\n'), deliverSuspendedSoftware: meta.deliverSuspendedSoftware, + signToActivityPubGet: meta.signToActivityPubGet, + proxyRemoteFiles: meta.proxyRemoteFiles, + allowExternalApRedirect: meta.allowExternalApRedirect, + cacheRemoteFiles: meta.cacheRemoteFiles, + cacheRemoteSensitiveFiles: meta.cacheRemoteSensitiveFiles, }, async (state) => { await os.apiWithDialog('admin/update-meta', { federation: state.federation, federationHosts: state.federationHosts.split('\n'), deliverSuspendedSoftware: state.deliverSuspendedSoftware, + signToActivityPubGet: state.signToActivityPubGet, + proxyRemoteFiles: state.proxyRemoteFiles, + allowExternalApRedirect: state.allowExternalApRedirect, + cacheRemoteFiles: state.cacheRemoteFiles, + cacheRemoteSensitiveFiles: state.cacheRemoteSensitiveFiles, }); fetchInstance(true); }); diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 1e38446882..1abc721997 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -8785,6 +8785,9 @@ export type operations = { singleUserMode: boolean; /** @enum {string} */ ugcVisibilityForVisitor: 'all' | 'local' | 'none'; + proxyRemoteFiles: boolean; + signToActivityPubGet: boolean; + allowExternalApRedirect: boolean; }; }; }; @@ -11458,6 +11461,9 @@ export type operations = { singleUserMode?: boolean; /** @enum {string} */ ugcVisibilityForVisitor?: 'all' | 'local' | 'none'; + proxyRemoteFiles?: boolean; + signToActivityPubGet?: boolean; + allowExternalApRedirect?: boolean; }; }; }; |