Update project including dist
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { DEMO_AUTH_ENABLED, getValidAccessToken } from "@/lib/auth";
|
||||
|
||||
const SUPABASE_URL = (import.meta.env["VITE_SUPABASE_URL"] ?? "").replace(/\/+$/, "");
|
||||
const SUPABASE_ANON_KEY = import.meta.env["VITE_SUPABASE_ANON_KEY"] ?? "";
|
||||
|
||||
function parseErrorMessage(payload: unknown, fallback: string) {
|
||||
if (!payload || typeof payload !== "object") return fallback;
|
||||
const record = payload as Record<string, unknown>;
|
||||
const message = record["message"];
|
||||
const error = record["error"];
|
||||
const details = record["details"];
|
||||
|
||||
if (typeof message === "string" && message.trim()) return message;
|
||||
if (typeof error === "string" && error.trim()) return error;
|
||||
if (typeof details === "string" && details.trim()) return details;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function isExpiredJwt(status: number, payload: unknown) {
|
||||
if (status === 401) return true;
|
||||
|
||||
const text =
|
||||
typeof payload === "string"
|
||||
? payload
|
||||
: payload && typeof payload === "object"
|
||||
? JSON.stringify(payload)
|
||||
: "";
|
||||
|
||||
return /jwt\s+expired|token\s+.*expired|expired\s+jwt/i.test(text);
|
||||
}
|
||||
|
||||
async function rpcRequest(functionName: string, payload: Record<string, unknown>, accessToken: string) {
|
||||
const response = await fetch(`${SUPABASE_URL}/rest/v1/rpc/${functionName}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
apikey: SUPABASE_ANON_KEY,
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
let parsed: unknown = null;
|
||||
|
||||
if (text) {
|
||||
try {
|
||||
parsed = JSON.parse(text) as unknown;
|
||||
} catch {
|
||||
parsed = text;
|
||||
}
|
||||
}
|
||||
|
||||
return { response, parsed };
|
||||
}
|
||||
|
||||
export async function hasRealSupabaseSession() {
|
||||
if (DEMO_AUTH_ENABLED) return false;
|
||||
return Boolean(await getValidAccessToken());
|
||||
}
|
||||
|
||||
export async function callAuthenticatedRpc<T>(
|
||||
functionName: string,
|
||||
payload: Record<string, unknown> = {},
|
||||
): Promise<T> {
|
||||
if (DEMO_AUTH_ENABLED) {
|
||||
throw new Error("Esta operación de Supabase no está disponible en modo demostrativo.");
|
||||
}
|
||||
|
||||
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
|
||||
throw new Error("Faltan las variables VITE_SUPABASE_URL o VITE_SUPABASE_ANON_KEY.");
|
||||
}
|
||||
|
||||
let accessToken = await getValidAccessToken();
|
||||
if (!accessToken) {
|
||||
throw new Error("La sesión terminó. Vuelve a iniciar sesión.");
|
||||
}
|
||||
|
||||
let result = await rpcRequest(functionName, payload, accessToken);
|
||||
|
||||
// Cinturón de seguridad: si una pestaña estuvo suspendida y el JWT venció
|
||||
// entre comprobaciones, renovamos la sesión y repetimos la RPC una sola vez.
|
||||
if (!result.response.ok && isExpiredJwt(result.response.status, result.parsed)) {
|
||||
accessToken = await getValidAccessToken(true);
|
||||
if (!accessToken) {
|
||||
throw new Error("La sesión terminó. Vuelve a iniciar sesión.");
|
||||
}
|
||||
result = await rpcRequest(functionName, payload, accessToken);
|
||||
}
|
||||
|
||||
if (!result.response.ok) {
|
||||
throw new Error(
|
||||
parseErrorMessage(
|
||||
result.parsed,
|
||||
`Supabase rechazó la operación ${functionName} (${result.response.status}).`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result.parsed as T;
|
||||
}
|
||||
Reference in New Issue
Block a user