fix(web): stabilize homepage live status
All checks were successful
Code Review / ai-code-review (push) Successful in 10s
CD Pipeline / tests (push) Successful in 1m8s
CD Pipeline / build-and-deploy (push) Successful in 3m30s
CD Pipeline / post-deploy-checks (push) Successful in 1m22s

This commit is contained in:
Your Name
2026-05-19 11:12:09 +08:00
parent 504d038a9e
commit 10f2f1abaf
6 changed files with 112 additions and 49 deletions

View File

@@ -50,6 +50,36 @@ interface UseCSRFReturn {
refresh: () => Promise<void>;
}
let cachedCSRFToken: string | null = null;
let csrfInFlight: Promise<string> | null = null;
async function requestCSRFToken(force = false): Promise<string> {
if (cachedCSRFToken && !force) return cachedCSRFToken;
if (csrfInFlight) return csrfInFlight;
csrfInFlight = (async () => {
const apiUrl = getApiUrl();
const response = await fetch(`${apiUrl}/api/v1/csrf/token`, {
method: "GET",
credentials: "include", // 確保 cookie 被設定
});
if (!response.ok) {
throw new Error(`Failed to fetch CSRF token: ${response.status}`);
}
const data: CSRFTokenResponse = await response.json();
cachedCSRFToken = data.token;
return data.token;
})();
try {
return await csrfInFlight;
} finally {
csrfInFlight = null;
}
}
/**
* CSRF Token Hook
*
@@ -57,27 +87,24 @@ interface UseCSRFReturn {
* 供敏感請求使用。
*/
export function useCSRF(): UseCSRFReturn {
const [csrfToken, setCSRFToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [csrfToken, setCSRFToken] = useState<string | null>(cachedCSRFToken);
const [isLoading, setIsLoading] = useState(!cachedCSRFToken);
const [error, setError] = useState<Error | null>(null);
const fetchToken = useCallback(async () => {
const fetchToken = useCallback(async (force = false) => {
if (cachedCSRFToken && !force) {
setCSRFToken(cachedCSRFToken);
setIsLoading(false);
setError(null);
return;
}
setIsLoading(true);
setError(null);
try {
const apiUrl = getApiUrl();
const response = await fetch(`${apiUrl}/api/v1/csrf/token`, {
method: "GET",
credentials: "include", // 確保 cookie 被設定
});
if (!response.ok) {
throw new Error(`Failed to fetch CSRF token: ${response.status}`);
}
const data: CSRFTokenResponse = await response.json();
setCSRFToken(data.token);
const token = await requestCSRFToken(force);
setCSRFToken(token);
} catch (err) {
setError(err instanceof Error ? err : new Error(String(err)));
console.error("[useCSRF] Failed to fetch CSRF token:", err);
@@ -107,7 +134,7 @@ export function useCSRF(): UseCSRFReturn {
isLoading,
error,
getHeaders,
refresh: fetchToken,
refresh: () => fetchToken(true),
};
}