fix: resolve token refresh race condition and pdf viewer empty token rendering
All checks were successful
Automated Container Build / build-and-push (push) Successful in 24s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 24s
This commit is contained in:
parent
f3ea79ab1f
commit
e88243f097
2 changed files with 43 additions and 21 deletions
|
|
@ -152,7 +152,11 @@ export default function FilePreview({ file, onClose, downloadToken }: FilePrevie
|
|||
)}
|
||||
|
||||
{previewType === 'pdf' && (
|
||||
<DynamicPdfViewer url={downloadUrl} />
|
||||
!downloadToken ? (
|
||||
<div className="flex items-center justify-center p-8"><Loader2 className="w-8 h-8 animate-spin text-accent" /></div>
|
||||
) : (
|
||||
<DynamicPdfViewer url={downloadUrl} />
|
||||
)
|
||||
)}
|
||||
|
||||
{(previewType === 'text' || previewType === 'code') && (
|
||||
|
|
|
|||
|
|
@ -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<boolean> | null = null;
|
||||
|
||||
private async refreshAccessToken(): Promise<boolean> {
|
||||
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<boolean> {
|
||||
|
|
|
|||
Reference in a new issue