fix(node-player): auto-refresh expired JWT tokens on 401
Publish Metadata Agent Image (dev) / build-and-push-image (push) Has been cancelled
Publish Node Player Image (dev) / build-and-push-image (push) Failing after 28s
Publish Web Player Image (dev) / build-and-push-image (push) Has been cancelled

Adds an axios response interceptor that catches 401 errors, fetches a
fresh access token from /auth/token, and retries the original request.
Concurrent refresh attempts are deduplicated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ultradesu
2026-04-08 15:41:47 +01:00
parent a9a8ee81b8
commit befba57374
@@ -16,6 +16,38 @@ export function clearAuthToken() {
delete furumiApi.defaults.headers.common['Authorization'] delete furumiApi.defaults.headers.common['Authorization']
} }
async function refreshToken(): Promise<boolean> {
try {
const res = await fetch('/auth/token', { credentials: 'include' })
if (!res.ok) return false
const data = await res.json()
if (data.access_token) {
setAuthToken(data.access_token)
return true
}
} catch { /* ignore */ }
return false
}
let refreshPromise: Promise<boolean> | null = null
furumiApi.interceptors.response.use(
(response) => response,
async (error) => {
const original = error.config
if (error.response?.status === 401 && !original._retried) {
original._retried = true
// Deduplicate concurrent refresh attempts
if (!refreshPromise) {
refreshPromise = refreshToken().finally(() => { refreshPromise = null })
}
const ok = await refreshPromise
if (ok) return furumiApi(original)
}
return Promise.reject(error)
},
)
export async function getArtists(): Promise<Artist[] | null> { export async function getArtists(): Promise<Artist[] | null> {
const res = await furumiApi.get<Artist[]>('/artists').catch(() => null) const res = await furumiApi.get<Artist[]>('/artists').catch(() => null)
return res?.data ?? null return res?.data ?? null