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' && (
|
{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') && (
|
{(previewType === 'text' || previewType === 'code') && (
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class ApiClient {
|
||||||
const refreshed = await this.refreshAccessToken();
|
const refreshed = await this.refreshAccessToken();
|
||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
headers['Authorization'] = `Bearer ${this.accessToken}`;
|
headers['Authorization'] = `Bearer ${this.accessToken}`;
|
||||||
return fetch(`${API_BASE}${endpoint}`, {
|
const retryResponse = await fetch(`${API_BASE}${endpoint}`, {
|
||||||
method: options.method || 'GET',
|
method: options.method || 'GET',
|
||||||
headers,
|
headers,
|
||||||
body: options.body instanceof FormData
|
body: options.body instanceof FormData
|
||||||
|
|
@ -77,6 +77,13 @@ class ApiClient {
|
||||||
? JSON.stringify(options.body)
|
? JSON.stringify(options.body)
|
||||||
: undefined,
|
: 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;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private refreshPromise: Promise<boolean> | null = null;
|
||||||
|
|
||||||
private async refreshAccessToken(): Promise<boolean> {
|
private async refreshAccessToken(): Promise<boolean> {
|
||||||
if (!this.refreshToken) return false;
|
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) {
|
if (this.refreshPromise) {
|
||||||
const data = await response.json();
|
return this.refreshPromise;
|
||||||
this.setTokens(data.access_token, data.refresh_token || this.refreshToken!);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh failed
|
|
||||||
this.clearTokens();
|
|
||||||
return false;
|
|
||||||
} catch (e) {
|
|
||||||
this.clearTokens();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
async ensureTokenFresh(): Promise<boolean> {
|
||||||
|
|
|
||||||
Reference in a new issue