diff --git a/src/api/tickets.ts b/src/api/tickets.ts
index 2794127..86a3d50 100644
--- a/src/api/tickets.ts
+++ b/src/api/tickets.ts
@@ -79,9 +79,11 @@ export const ticketsApi = {
return response.data;
},
- // Get media URL for display
- getMediaUrl: (fileId: string): string => {
+ // Get media URL for display. The signed `token` comes from the ticket
+ // response and is required by the backend (a raw file_id alone 404s).
+ getMediaUrl: (fileId: string, token?: string | null): string => {
const baseUrl = import.meta.env.VITE_API_URL || '';
- return `${baseUrl}/cabinet/media/${fileId}`;
+ const suffix = token ? `?token=${encodeURIComponent(token)}` : '';
+ return `${baseUrl}/cabinet/media/${fileId}${suffix}`;
},
};
diff --git a/src/components/tickets/MessageMediaGrid.tsx b/src/components/tickets/MessageMediaGrid.tsx
index df3496f..e76fb47 100644
--- a/src/components/tickets/MessageMediaGrid.tsx
+++ b/src/components/tickets/MessageMediaGrid.tsx
@@ -9,12 +9,15 @@ export interface MediaItem {
type: string;
file_id: string;
caption?: string | null;
+ /** Signed, expiring download token from the ticket response. */
+ token?: string | null;
}
interface MessageLike {
has_media?: boolean;
media_type?: string | null;
media_file_id?: string | null;
+ media_token?: string | null;
media_caption?: string | null;
media_items?: MediaItem[] | null;
}
@@ -33,6 +36,7 @@ function getItems(message: MessageLike): MediaItem[] {
type: message.media_type,
file_id: message.media_file_id,
caption: message.media_caption,
+ token: message.media_token,
},
];
}
@@ -113,7 +117,7 @@ export function MessageMediaGrid({
onClick={() => openFullscreen(originalIdx)}
>
{
- const mediaUrl = ticketsApi.getMediaUrl(item.file_id);
+ const mediaUrl = ticketsApi.getMediaUrl(item.file_id, item.token);
if (item.type === 'video') {
return (