This commit is contained in:
2025-12-17 13:33:31 -05:00
parent e18aa3f42c
commit c49bfe5a3d
38 changed files with 2436 additions and 436 deletions

View File

@@ -1,10 +1,18 @@
// requireAuthHook.js
import { API_URL } from "@/config";
// Short-term failure cache for auth timeouts/errors
let lastAuthFailureTime = 0;
const AUTH_FAILURE_CACHE_MS = 30000; // 30 seconds
// WeakMap to cache auth promises per Astro context (request)
const authCache = new WeakMap();
export const requireAuthHook = async (Astro) => {
// If we recently failed due to API timeout/unreachability, fail closed quickly
if (Date.now() - lastAuthFailureTime < AUTH_FAILURE_CACHE_MS) {
return null;
}
// Check if we already have a cached promise for this request
if (authCache.has(Astro)) {
return authCache.get(Astro);
@@ -21,17 +29,43 @@ export const requireAuthHook = async (Astro) => {
async function performAuth(Astro) {
try {
const cookieHeader = Astro.request.headers.get("cookie") ?? "";
let res = await fetch(`${API_URL}/auth/id`, {
headers: { Cookie: cookieHeader },
credentials: "include",
});
if (res.status === 401) {
const refreshRes = await fetch(`${API_URL}/auth/refresh`, {
method: "POST",
// Add timeout to avoid hanging SSR render
let controller = new AbortController();
let timeout = setTimeout(() => controller.abort(), 3000);
let res;
try {
res = await fetch(`${API_URL}/auth/id`, {
headers: { Cookie: cookieHeader },
credentials: "include",
signal: controller.signal,
});
} catch (err) {
clearTimeout(timeout);
lastAuthFailureTime = Date.now();
console.error("[SSR] auth/id failed or timed out", err);
return null;
}
clearTimeout(timeout);
if (res.status === 401) {
// Try refresh with timeout
controller = new AbortController();
timeout = setTimeout(() => controller.abort(), 3000);
let refreshRes;
try {
refreshRes = await fetch(`${API_URL}/auth/refresh`, {
method: "POST",
headers: { Cookie: cookieHeader },
credentials: "include",
signal: controller.signal,
});
} catch (err) {
clearTimeout(timeout);
lastAuthFailureTime = Date.now();
console.error("[SSR] auth/refresh failed or timed out", err);
return null;
}
clearTimeout(timeout);
if (!refreshRes.ok) {
console.error("Token refresh failed", refreshRes.status);
@@ -62,10 +96,21 @@ async function performAuth(Astro) {
// Build new cookie header for the retry request
const newCookieHeader = setCookies.map(c => c.split(";")[0].trim()).join("; ");
res = await fetch(`${API_URL}/auth/id`, {
headers: { Cookie: newCookieHeader },
credentials: "include",
});
controller = new AbortController();
timeout = setTimeout(() => controller.abort(), 3000);
try {
res = await fetch(`${API_URL}/auth/id`, {
headers: { Cookie: newCookieHeader },
credentials: "include",
signal: controller.signal,
});
} catch (err) {
clearTimeout(timeout);
lastAuthFailureTime = Date.now();
console.error("[SSR] auth/id retry failed or timed out", err);
return null;
}
clearTimeout(timeout);
}
if (!res.ok) {