diff --git a/app/components/shared/FilePreviewer.vue b/app/components/shared/FilePreviewer.vue index e3eaf3a..9dbb177 100644 --- a/app/components/shared/FilePreviewer.vue +++ b/app/components/shared/FilePreviewer.vue @@ -122,6 +122,8 @@ const errorText = computed(() => error.value?.message ?? null); const fileMeta = computed(() => props.file ?? data.value ?? null); + logger.debug('FilePreviewer - fileMeta:', fileMeta.value); + /** 预览源地址:支持在 file.url 上追加额外 query(如临时 token、inline) */ const src = computed(() => { if (!fileMeta.value) return ''; diff --git a/app/composables/directus/useDirectusFiles.ts b/app/composables/directus/useDirectusFiles.ts index 38af82b..69ee3e7 100644 --- a/app/composables/directus/useDirectusFiles.ts +++ b/app/composables/directus/useDirectusFiles.ts @@ -1,19 +1,14 @@ export const useDirectusFiles = () => { - const config = useRuntimeConfig(); - const baseUrl = config.public.directus.url; - const getFileUrl = ( id?: string | null, options?: Record ): string => { if (!id) return ''; - const query = options - ? '?' + - new URLSearchParams( - Object.entries(options).map(([key, value]) => [key, String(value)]) - ).toString() - : ''; - return `${baseUrl}/assets/${id}${query}`; + + const params = new URLSearchParams( + options as Record + ).toString(); + return `/api/assets/${id}${params ? `?${params}` : ''}`; }; return { diff --git a/app/composables/directus/useDirectusImage.ts b/app/composables/directus/useDirectusImage.ts index 08215d6..14b4143 100644 --- a/app/composables/directus/useDirectusImage.ts +++ b/app/composables/directus/useDirectusImage.ts @@ -1,8 +1,4 @@ export const useDirectusImage = () => { - const config = useRuntimeConfig(); - const baseUrl = config.public.directus.url; - const token = config.public.directus.token; - type DirectusAssetParams = { width?: number; height?: number; @@ -13,19 +9,11 @@ export const useDirectusImage = () => { const getImageUrl = (id?: string | null, options?: DirectusAssetParams) => { if (!id) return ''; - const queryToken = token ? 'access_token=' + token : ''; - const queryOptions = options - ? new URLSearchParams( - Object.fromEntries( - Object.entries(options).map(([key, value]) => [key, String(value)]) - ) - ).toString - : ''; - const query = - queryToken || queryOptions - ? `?${[queryToken, queryOptions].filter(Boolean).join('&')}` - : ''; - return `${baseUrl}/assets/${id}${query}`; + + const params = new URLSearchParams( + options as Record + ).toString(); + return `/api/assets/${id}${params ? `?${params}` : ''}`; }; return { getImageUrl }; diff --git a/server/api/assets/[id].ts b/server/api/assets/[id].ts new file mode 100644 index 0000000..8975391 --- /dev/null +++ b/server/api/assets/[id].ts @@ -0,0 +1,36 @@ +export default defineEventHandler(async (event) => { + const id = getRouterParam(event, 'id'); + if (!id) + throw createError({ + statusCode: 400, + statusMessage: 'ID parameter is required', + }); + + const url = new URL(`${useRuntimeConfig().public.directus.url}/assets/${id}`); + + const query = getQuery(event); + Object.entries(query).forEach(([key, value]) => { + url.searchParams.set(key, String(value)); + }); + + const token = useRuntimeConfig().public.directus.token || ''; + const res = await fetch(url.toString(), { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!res.ok) { + throw createError({ + statusCode: res.status, + statusMessage: `Failed to fetch asset: ${res.statusText}`, + }); + } + + const contentType = + res.headers.get('Content-Type') || 'application/octet-stream'; + setHeader(event, 'Content-Type', contentType); + setHeader(event, 'Cache-Control', 'public, max-age=86400, immutable'); + + return res.body; +}); diff --git a/server/utils/file.ts b/server/utils/file.ts index 117d151..14dc5ac 100644 --- a/server/utils/file.ts +++ b/server/utils/file.ts @@ -41,7 +41,7 @@ export async function getFileMeta(id: string): Promise { width: file.width ?? undefined, height: file.height ?? undefined, uploaded_on: file.uploaded_on ?? undefined, - url: `${baseUrl}/assets/${file.id}`, + url: `/api/assets/${file.id}`, previewable: isPreviewable(file.type), }; } catch (error) {