Fix: Re-Try UI Login Token + GQL Wait

This commit is contained in:
Youwes09
2026-05-03 11:47:18 -05:00
parent efdd8ff95d
commit 093b395cc1
2 changed files with 40 additions and 19 deletions
+38 -19
View File
@@ -4,6 +4,19 @@ import { boot } from "@store/boot.svelte";
const DEFAULT_URL = "http://127.0.0.1:4567"; const DEFAULT_URL = "http://127.0.0.1:4567";
type ReauthResolver = () => void;
let _reauthQueue: ReauthResolver[] = [];
export function notifyReauthSuccess() {
const queue = _reauthQueue;
_reauthQueue = [];
queue.forEach(resolve => resolve());
}
function waitForReauth(): Promise<void> {
return new Promise(resolve => { _reauthQueue.push(resolve); });
}
export function getServerUrl(): string { export function getServerUrl(): string {
const url = store.settings.serverUrl; const url = store.settings.serverUrl;
return typeof url === "string" && url.trim() ? url.replace(/\/$/, "") : DEFAULT_URL; return typeof url === "string" && url.trim() ? url.replace(/\/$/, "") : DEFAULT_URL;
@@ -95,24 +108,30 @@ export async function gql<T>(
variables?: Record<string, unknown>, variables?: Record<string, unknown>,
signal?: AbortSignal, signal?: AbortSignal,
): Promise<T> { ): Promise<T> {
const res = await fetchWithRetry( const attempt = async (): Promise<T> => {
`${getServerUrl()}/api/graphql`, const res = await fetchWithRetry(
{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }) }, `${getServerUrl()}/api/graphql`,
signal, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }) },
); signal,
if (signal?.aborted) throw new DOMException("Aborted", "AbortError"); );
if (!res.ok) throw new Error(`Suwayomi HTTP ${res.status}`); if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
const json: GQLResponse<T> = await res.json(); if (!res.ok) throw new Error(`Suwayomi HTTP ${res.status}`);
if (signal?.aborted) throw new DOMException("Aborted", "AbortError"); const json: GQLResponse<T> = await res.json();
if (json.errors?.length) { if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
const isAuthError = json.errors.some(e => /unauthorized|unauthenticated/i.test(e.message)); if (json.errors?.length) {
if (isAuthError && !boot.skipped) { const isAuthError = json.errors.some(e => /unauthorized|unauthenticated/i.test(e.message));
boot.sessionExpired = true; if (isAuthError && !boot.skipped) {
boot.loginRequired = true; boot.sessionExpired = true;
boot.loginUser = store.settings.serverAuthUser ?? ""; boot.loginRequired = true;
throw new AuthRequiredError(json.errors[0].message); boot.loginUser = store.settings.serverAuthUser ?? "";
await waitForReauth();
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
return attempt();
}
throw new Error(json.errors[0].message);
} }
throw new Error(json.errors[0].message); return json.data;
} };
return json.data;
return attempt();
} }
+2
View File
@@ -2,6 +2,7 @@ import { store } from "@store/state.svelte";
import { probeServer, loginBasic, loginUI } from "@core/auth"; import { probeServer, loginBasic, loginUI } from "@core/auth";
import { trackingState } from "@features/tracking/store/trackingState.svelte"; import { trackingState } from "@features/tracking/store/trackingState.svelte";
import { loadAllStores } from "@core/persistence/persist"; import { loadAllStores } from "@core/persistence/persist";
import { notifyReauthSuccess } from "@api/client";
const MAX_ATTEMPTS = 40; const MAX_ATTEMPTS = 40;
@@ -107,6 +108,7 @@ export async function submitLogin(onSuccess: () => void): Promise<void> {
boot.skipped = false; boot.skipped = false;
boot.loginPass = ""; boot.loginPass = "";
boot.loginError = null; boot.loginError = null;
notifyReauthSuccess();
trackingState.bootSync().catch(() => {}); trackingState.bootSync().catch(() => {});
onSuccess(); onSuccess();
} catch (e: any) { } catch (e: any) {