fix: global Unauthorized handling — redirect to /login, suppress log spam
Some checks failed
Build and Push Frontend Image / build (push) Has been cancelled
Some checks failed
Build and Push Frontend Image / build (push) Has been cancelled
- Add UnauthorizedError class exported from services.ts - loggedApiCall now detects Unauthorized GraphQL errors, logs at DEBUG instead of ERROR, and throws UnauthorizedError (no more stack dumps) - hooks.server.ts catches UnauthorizedError from any load function and redirects to /login?redirect=<original-path> - getRecordings, getRecording, getAnalytics now accept an optional token and use getAuthClient server-side so cross-origin cookie forwarding works - Update play/recordings, play/buttplug, me/analytics page.server.ts to pass the session token — prevents Unauthorized on auth-protected pages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,22 @@ import type {
|
||||
} from "$lib/types";
|
||||
import { logger } from "$lib/logger";
|
||||
|
||||
export class UnauthorizedError extends Error {
|
||||
constructor() {
|
||||
super("Unauthorized");
|
||||
this.name = "UnauthorizedError";
|
||||
}
|
||||
}
|
||||
|
||||
function isUnauthorizedError(error: unknown): boolean {
|
||||
if (error && typeof error === "object" && "response" in error) {
|
||||
const resp = (error as { response?: { errors?: { message: string }[] } }).response;
|
||||
if (resp?.errors?.some((e) => e.message === "Unauthorized")) return true;
|
||||
}
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
return msg.startsWith("Unauthorized");
|
||||
}
|
||||
|
||||
// Helper to log API calls
|
||||
async function loggedApiCall<T>(
|
||||
operationName: string,
|
||||
@@ -32,6 +48,10 @@ async function loggedApiCall<T>(
|
||||
return result;
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime;
|
||||
if (isUnauthorizedError(error)) {
|
||||
logger.debug(`🔒 API: ${operationName} unauthorized`, { duration, context });
|
||||
throw new UnauthorizedError();
|
||||
}
|
||||
logger.error(`❌ API: ${operationName} failed`, {
|
||||
duration,
|
||||
context,
|
||||
@@ -816,13 +836,12 @@ const RECORDINGS_QUERY = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export async function getRecordings(fetchFn?: typeof globalThis.fetch) {
|
||||
export async function getRecordings(fetchFn?: typeof globalThis.fetch, token?: string) {
|
||||
return loggedApiCall(
|
||||
"getRecordings",
|
||||
async () => {
|
||||
const data = await getGraphQLClient(fetchFn).request<{ recordings: Recording[] }>(
|
||||
RECORDINGS_QUERY,
|
||||
);
|
||||
const client = token ? getAuthClient(token, fetchFn) : getGraphQLClient(fetchFn);
|
||||
const data = await client.request<{ recordings: Recording[] }>(RECORDINGS_QUERY);
|
||||
return data.recordings;
|
||||
},
|
||||
{},
|
||||
@@ -960,14 +979,12 @@ const RECORDING_QUERY = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export async function getRecording(id: string, fetchFn?: typeof globalThis.fetch) {
|
||||
export async function getRecording(id: string, fetchFn?: typeof globalThis.fetch, token?: string) {
|
||||
return loggedApiCall(
|
||||
"getRecording",
|
||||
async () => {
|
||||
const data = await getGraphQLClient(fetchFn).request<{ recording: Recording | null }>(
|
||||
RECORDING_QUERY,
|
||||
{ id },
|
||||
);
|
||||
const client = token ? getAuthClient(token, fetchFn) : getGraphQLClient(fetchFn);
|
||||
const data = await client.request<{ recording: Recording | null }>(RECORDING_QUERY, { id });
|
||||
return data.recording;
|
||||
},
|
||||
{ id },
|
||||
@@ -1799,13 +1816,12 @@ const ANALYTICS_QUERY = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export async function getAnalytics(fetchFn?: typeof globalThis.fetch) {
|
||||
export async function getAnalytics(fetchFn?: typeof globalThis.fetch, token?: string) {
|
||||
return loggedApiCall(
|
||||
"getAnalytics",
|
||||
async () => {
|
||||
const data = await getGraphQLClient(fetchFn).request<{ analytics: Analytics | null }>(
|
||||
ANALYTICS_QUERY,
|
||||
);
|
||||
const client = token ? getAuthClient(token, fetchFn) : getGraphQLClient(fetchFn);
|
||||
const data = await client.request<{ analytics: Analytics | null }>(ANALYTICS_QUERY);
|
||||
return data.analytics;
|
||||
},
|
||||
{},
|
||||
|
||||
Reference in New Issue
Block a user