diff --git a/.config/docker_example.yml b/.config/docker_example.yml index 2ec6cf8734..59a0ff7187 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -56,17 +56,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -154,6 +154,9 @@ id: 'aidx' # saKeyPath: /path/to/service-account-key.json # logName: cherrypick +# Override the file URL rendering in ActivityPub (Object Storage file only) +#apFileBaseUrl: https://example.com/ + # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 diff --git a/.config/example.yml b/.config/example.yml index a74996e463..b38756d1fa 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -172,6 +172,9 @@ id: 'aidx' # saKeyPath: /path/to/service-account-key.json # logName: cherrypick +# Override the file URL rendering in ActivityPub (Object Storage file only) +#apFileBaseUrl: https://example.com/ + # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml index 741f768cf0..5f9b66a40d 100644 --- a/.devcontainer/devcontainer.yml +++ b/.devcontainer/devcontainer.yml @@ -56,17 +56,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -154,6 +154,9 @@ id: 'aidx' # saKeyPath: /path/to/service-account-key.json # logName: cherrypick +# Override the file URL rendering in ActivityPub (Object Storage file only) +#apFileBaseUrl: https://example.com/ + # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 diff --git a/chart/files/default.yml b/chart/files/default.yml index f209e90a31..178d87026e 100644 --- a/chart/files/default.yml +++ b/chart/files/default.yml @@ -77,17 +77,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -174,6 +174,9 @@ id: "aidx" # saKeyPath: /path/to/service-account-key.json # logName: cherrypick +# Override the file URL rendering in ActivityPub (Object Storage file only) +#apFileBaseUrl: https://example.com/ + # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 0798fd1914..ccf7e18f23 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -86,6 +86,8 @@ type Source = { logName?: string; } + apFileBaseUrl?: string; + mediaProxy?: string; proxyRemoteFiles?: boolean; videoThumbnailGenerator?: string; @@ -145,6 +147,7 @@ export type Config = { relashionshipJobPerSec: number | undefined; deliverJobMaxAttempts: number | undefined; inboxJobMaxAttempts: number | undefined; + apFileBaseUrl: string | undefined; proxyRemoteFiles: boolean | undefined; signToActivityPubGet: boolean | undefined; @@ -250,6 +253,7 @@ export function loadConfig(): Config { inboxJobMaxAttempts: config.inboxJobMaxAttempts, proxyRemoteFiles: config.proxyRemoteFiles, signToActivityPubGet: config.signToActivityPubGet, + apFileBaseUrl: config.apFileBaseUrl, mediaProxy: externalMediaProxy ?? internalMediaProxy, externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy, videoThumbnailGenerator: config.videoThumbnailGenerator ? diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index c967439c12..8e0ae86cb5 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -166,7 +166,7 @@ export class ApRendererService { return { type: 'Document', mediaType: file.webpublicType ?? file.type, - url: this.driveFileEntityService.getPublicUrl(file), + url: this.driveFileEntityService.getPublicUrl(file, undefined, true), name: file.comment, }; } @@ -245,7 +245,7 @@ export class ApRendererService { public renderImage(file: MiDriveFile): IApImage { return { type: 'Image', - url: this.driveFileEntityService.getPublicUrl(file), + url: this.driveFileEntityService.getPublicUrl(file, undefined, true), sensitive: file.isSensitive, name: file.comment, }; diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 679715f239..52d110f840 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -250,8 +250,8 @@ export class ApPersonService implements OnModuleInit { return { avatarId: avatar?.id ?? null, bannerId: banner?.id ?? null, - avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, - bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null, + avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar', true) : null, + bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner, undefined, true) : null, avatarBlurhash: avatar?.blurhash ?? null, bannerBlurhash: banner?.blurhash ?? null, }; diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 837a7ab13d..c3ad6746cd 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -107,7 +107,7 @@ export class DriveFileEntityService { } @bindThis - public getPublicUrl(file: MiDriveFile, mode?: 'avatar'): string { // static = thumbnail + public getPublicUrl(file: MiDriveFile, mode?: 'avatar', ap?: boolean): string { // static = thumbnail // リモートかつメディアプロキシ if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) { return this.getProxiedUrl(file.uri, mode); @@ -129,6 +129,16 @@ export class DriveFileEntityService { if (mode === 'avatar') { return this.getProxiedUrl(url, 'avatar'); } + + if (ap && this.config.apFileBaseUrl) { + const baseUrl = this.config.apFileBaseUrl; + const isValidBaseUrl = /^https?:\/\/[\w.-]+\.[a-zA-Z]{2,}(\/.*)?$/i.test(baseUrl); + if (isValidBaseUrl) { + const trimmedBaseUrl = baseUrl.replace(/\/$/, ''); + return url.replace(/^https?:\/\/[\w.-]+\.[a-zA-Z]{2,}/, trimmedBaseUrl); + } + } + return url; }