diff --git a/frontend/src/components/FilePreview.tsx b/frontend/src/components/FilePreview.tsx
index 0dfd792..4c7677b 100644
--- a/frontend/src/components/FilePreview.tsx
+++ b/frontend/src/components/FilePreview.tsx
@@ -152,7 +152,11 @@ export default function FilePreview({ file, onClose, downloadToken }: FilePrevie
)}
{previewType === 'pdf' && (
-
+ !downloadToken ? (
+
+ ) : (
+
+ )
)}
{(previewType === 'text' || previewType === 'code') && (
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index de77b62..b1b92ad 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -68,7 +68,7 @@ class ApiClient {
const refreshed = await this.refreshAccessToken();
if (refreshed) {
headers['Authorization'] = `Bearer ${this.accessToken}`;
- return fetch(`${API_BASE}${endpoint}`, {
+ const retryResponse = await fetch(`${API_BASE}${endpoint}`, {
method: options.method || 'GET',
headers,
body: options.body instanceof FormData
@@ -77,6 +77,13 @@ class ApiClient {
? JSON.stringify(options.body)
: undefined,
});
+
+ if (!retryResponse.ok && retryResponse.status === 401) {
+ if (typeof window !== 'undefined') window.dispatchEvent(new Event('auth_error'));
+ throw new Error(`HTTP 401`);
+ }
+
+ return retryResponse;
}
}
@@ -100,29 +107,40 @@ class ApiClient {
return response;
}
+ private refreshPromise: Promise | null = null;
+
private async refreshAccessToken(): Promise {
if (!this.refreshToken) return false;
- try {
- const response = await fetch(`${API_BASE}/api/auth/refresh`, {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${this.refreshToken}`,
- },
- });
- if (response.ok) {
- const data = await response.json();
- this.setTokens(data.access_token, data.refresh_token || this.refreshToken!);
- return true;
- }
-
- // Refresh failed
- this.clearTokens();
- return false;
- } catch (e) {
- this.clearTokens();
- return false;
+ if (this.refreshPromise) {
+ return this.refreshPromise;
}
+
+ this.refreshPromise = (async () => {
+ try {
+ const response = await fetch(`${API_BASE}/api/auth/refresh`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${this.refreshToken}`,
+ },
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ this.setTokens(data.access_token, data.refresh_token || this.refreshToken!);
+ return true;
+ }
+ this.clearTokens();
+ return false;
+ } catch {
+ this.clearTokens();
+ return false;
+ } finally {
+ this.refreshPromise = null;
+ }
+ })();
+
+ return this.refreshPromise;
}
async ensureTokenFresh(): Promise {