From 4e6e22633e67c7d6107f27da7337c5868c0c60a6 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Wed, 22 Nov 2023 18:47:32 +0100 Subject: [PATCH] [backend] Rework media proxying for better performance --- packages/backend/src/misc/fetch-meta.ts | 5 +++ .../src/models/repositories/drive-file.ts | 31 ++++++++++++++----- .../backend/src/models/repositories/user.ts | 6 ++-- .../server/api/mastodon/converters/user.ts | 8 +++-- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/misc/fetch-meta.ts b/packages/backend/src/misc/fetch-meta.ts index 3d66f2d63..14372a792 100644 --- a/packages/backend/src/misc/fetch-meta.ts +++ b/packages/backend/src/misc/fetch-meta.ts @@ -31,6 +31,11 @@ export function metaToPugArgs(meta: Meta): object { }; } +export function fetchMetaSync(): Meta | null { + return cache; +} + + export async function fetchMeta(noCache = false): Promise { if (!noCache && cache) return cache; diff --git a/packages/backend/src/models/repositories/drive-file.ts b/packages/backend/src/models/repositories/drive-file.ts index 68f5f1081..4d6567e81 100644 --- a/packages/backend/src/models/repositories/drive-file.ts +++ b/packages/backend/src/models/repositories/drive-file.ts @@ -8,6 +8,7 @@ import config from "@/config/index.js"; import { appendQuery, query } from "@/prelude/url.js"; import { DriveFolders, Users } from "../index.js"; import { deepClone } from "@/misc/clone.js"; +import { fetchMetaSync } from "@/misc/fetch-meta.js"; type PackOptions = { detail?: boolean; @@ -71,14 +72,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ ); } - // リモートかつ期限切れはローカルプロキシを試みる - if (file.uri != null && file.isLink && config.proxyRemoteFiles) { - const key = thumbnail ? file.thumbnailAccessKey : file.webpublicAccessKey; - - if (key && !key.match("/")) { - // 古いものはここにオブジェクトストレージキーが入ってるので除外 - return `${config.url}/files/${key}`; - } + if (file.isLink && config.proxyRemoteFiles) { + const url = this.getDatabasePrefetchUrl(file, thumbnail); + if (url != null) return `${config.url}/proxy/${encodeURIComponent(new URL(url).pathname)}?${query({ url: url })}`; } return thumbnail @@ -92,6 +88,25 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ : file.webpublicUrl ?? file.url; }, + getFinalUrl(url: string): string { + if (!config.proxyRemoteFiles) return url; + if (url.startsWith(`${config.url}/files`)) return url; + if (url.startsWith(`${config.url}/static-assets`)) return url; + if (url.startsWith(`${config.url}/identicon`)) return url; + if (url.startsWith(`${config.url}/avatar`)) return url; + + const meta = fetchMetaSync(); + const baseUrl = meta ? meta.objectStorageBaseUrl ?? `${meta.objectStorageUseSSL ? "https" : "http"}://${meta.objectStorageEndpoint}${meta.objectStoragePort ? `:${meta.objectStoragePort}` : ""}/${meta.objectStorageBucket}` : null; + if (baseUrl !== null && url.startsWith(baseUrl)) return url; + + return `${config.url}/proxy/${encodeURIComponent(new URL(url).pathname)}?${query({ url: url })}`; + }, + + getFinalUrlMaybe(url?: string | null): string | null { + if (url == null) return null; + return this.getFinalUrl(url); + }, + async calcDriveUsageOf( user: User["id"] | { id: User["id"] }, ): Promise { diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 43523c95b..3a8b7874a 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -339,7 +339,7 @@ export const UserRepository = db.getRepository(User).extend({ this.getIdenticonUrl(user.id) ); } else if (user.avatarId) { - if (user.avatarUrl) return user.avatarUrl; + if (user.avatarUrl) return DriveFiles.getFinalUrl(user.avatarUrl); const avatar = await DriveFiles.findOneByOrFail({ id: user.avatarId }); return ( DriveFiles.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id) @@ -351,7 +351,7 @@ export const UserRepository = db.getRepository(User).extend({ getAvatarUrlSync(user: User): string { if (user.avatarId && user.avatarUrl) { - return user.avatarUrl; + return DriveFiles.getFinalUrl(user.avatarUrl); } else if (user.avatar) { return ( DriveFiles.getPublicUrl(user.avatar, true) || @@ -514,7 +514,7 @@ export const UserRepository = db.getRepository(User).extend({ lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, - bannerUrl: user.bannerId ? (user.bannerUrl ?? (user.banner + bannerUrl: user.bannerId ? (DriveFiles.getFinalUrlMaybe(user.bannerUrl) ?? (user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null)) : null, bannerBlurhash: user.bannerId ? (user.bannerBlurhash ?? user.banner?.blurhash ?? null) : null, diff --git a/packages/backend/src/server/api/mastodon/converters/user.ts b/packages/backend/src/server/api/mastodon/converters/user.ts index 07b9add43..5ac37e805 100644 --- a/packages/backend/src/server/api/mastodon/converters/user.ts +++ b/packages/backend/src/server/api/mastodon/converters/user.ts @@ -35,12 +35,14 @@ export class UserConverter { const profile = UserProfiles.findOneBy({ userId: u.id }); const bio = profile.then(profile => MfmHelpers.toHtml(mfm.parse(profile?.description ?? ""), profile?.mentions, u.host).then(p => p ?? escapeMFM(profile?.description ?? ""))); const avatar = u.avatarId - ? u.avatarUrl ?? (DriveFiles.findOneBy({ id: u.avatarId })) + ? DriveFiles.getFinalUrlMaybe(u.avatarUrl) ?? (DriveFiles.findOneBy({ id: u.avatarId })) .then(p => p?.url ?? Users.getIdenticonUrl(u.id)) + .then(p => DriveFiles.getFinalUrl(p)) : Users.getIdenticonUrl(u.id); const banner = u.bannerId - ? u.bannerUrl ?? (DriveFiles.findOneBy({ id: u.bannerId })) - .then(p => p?.url ?? `${config.url}/static-assets/transparent.png`) + ? DriveFiles.getFinalUrlMaybe(u.bannerUrl) ?? (DriveFiles.findOneBy({ id: u.bannerId })) + .then(p => p?.url ?? `${config.url}/static-assets/transparent.png`) + .then(p => DriveFiles.getFinalUrl(p)) : `${config.url}/static-assets/transparent.png`; const isFollowedOrSelf = !!localUser &&