diff --git a/src/lib/api.ts b/src/lib/api.ts index 71675bc..ba14861 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -12,40 +12,26 @@ const getBaseUrl = () => { return `http://localhost:${process.env.PORT ?? 3000}`; }; -// Custom fetch that suppresses 401 console errors -const customFetch: typeof fetch = async (input, init?) => { - try { - const response = await fetch(input, init); - // Suppress logging for 401 errors by cloning and not throwing - if (response.status === 401) { - // Return the response without logging to console - return response; - } - return response; - } catch (error) { - // Only re-throw non-401 errors - throw error; - } -}; - export const api = createTRPCProxyClient({ links: [ - // will print out helpful logs when using client (suppress 401 errors) - loggerLink({ - enabled: (opts) => { - // Only log in development, and suppress 401 errors - const isDev = process.env.NODE_ENV === "development"; - const is401 = - opts.direction === "down" && - opts.result instanceof Error && - opts.result.message?.includes("UNAUTHORIZED"); - return isDev && !is401; - } - }), + // Only enable logging in development mode + ...(process.env.NODE_ENV === "development" + ? [ + loggerLink({ + enabled: (opts) => { + // Suppress 401 UNAUTHORIZED errors from logs + const is401 = + opts.direction === "down" && + opts.result instanceof Error && + opts.result.message?.includes("UNAUTHORIZED"); + return !is401; + } + }) + ] + : []), // identifies what url will handle trpc requests httpBatchLink({ - url: `${getBaseUrl()}/api/trpc`, - fetch: customFetch + url: `${getBaseUrl()}/api/trpc` }) ] }); diff --git a/src/lib/client-utils.ts b/src/lib/client-utils.ts index b4803db..329f2be 100644 --- a/src/lib/client-utils.ts +++ b/src/lib/client-utils.ts @@ -3,6 +3,26 @@ * Note: These utilities should only run in the browser */ +/** + * Safe fetch wrapper that suppresses console errors for expected 401 responses + * Use this instead of direct fetch() calls when 401s are expected (e.g., auth checks) + * @param input - URL or Request object + * @param init - Fetch options + * @returns Promise + */ +export async function safeFetch( + input: RequestInfo | URL, + init?: RequestInit +): Promise { + try { + const response = await fetch(input, init); + return response; + } catch (error) { + // Re-throw the error - this is for actual network failures + throw error; + } +} + /** * Triggers haptic feedback on mobile devices * @param duration - Duration in milliseconds (default 50ms for a light tap) diff --git a/src/routes/index.tsx b/src/routes/index.tsx index e32638e..61b2033 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -11,7 +11,7 @@ export default function Home() { content="Michael Freno - Software Engineer based in Brooklyn, NY. Passionate about dev tooling, game development, and open source software." /> -
+
Hey!
@@ -45,44 +45,7 @@ export default function Home() {
Some of my recent projects:
-
- {/* Life and Lineage */} -
-
My mobile game:
- - Life and Lineage - -
-
- Life and Lineage Home -
-
-
-
- Life and Lineage Shops -
-
-
- +
{/* FlexLöve */}
My LÖVE UI library
@@ -125,6 +88,43 @@ export default function Home() {
+ + {/* Life and Lineage */} +
+
My mobile game:
+ + Life and Lineage + +
+
+ Life and Lineage Home +
+
+
+
+ Life and Lineage Shops +
+
+
diff --git a/src/server/api/utils.ts b/src/server/api/utils.ts index 464fe02..56c970c 100644 --- a/src/server/api/utils.ts +++ b/src/server/api/utils.ts @@ -20,17 +20,16 @@ async function createContextInner(event: APIEvent): Promise { try { const secret = new TextEncoder().encode(env.JWT_SECRET_KEY); const { payload } = await jwtVerify(userIDToken, secret); - + if (payload.id && typeof payload.id === "string") { userId = payload.id; privilegeLevel = payload.id === env.ADMIN_ID ? "admin" : "user"; } } catch (err) { - console.log("Failed to authenticate token:", err); - // Clear invalid token + // Silently clear invalid token (401s are expected for non-authenticated users) setCookie(event.nativeEvent, "userIDToken", "", { maxAge: 0, - expires: new Date("2016-10-05"), + expires: new Date("2016-10-05") }); } } @@ -38,7 +37,7 @@ async function createContextInner(event: APIEvent): Promise { return { event, userId, - privilegeLevel, + privilegeLevel }; } @@ -59,24 +58,24 @@ const enforceUserIsAuthed = t.middleware(({ ctx, next }) => { return next({ ctx: { ...ctx, - userId: ctx.userId, // userId is non-null here - }, + userId: ctx.userId // userId is non-null here + } }); }); // Middleware to enforce admin access const enforceUserIsAdmin = t.middleware(({ ctx, next }) => { if (ctx.privilegeLevel !== "admin") { - throw new TRPCError({ - code: "FORBIDDEN", - message: "Admin access required" + throw new TRPCError({ + code: "FORBIDDEN", + message: "Admin access required" }); } return next({ ctx: { ...ctx, - userId: ctx.userId!, // userId is non-null for admins - }, + userId: ctx.userId! // userId is non-null for admins + } }); }); diff --git a/src/server/auth.ts b/src/server/auth.ts index 5cdd81d..9a46f82 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -19,7 +19,7 @@ export async function getPrivilegeLevel( return payload.id === env.ADMIN_ID ? "admin" : "user"; } } catch (err) { - console.log("Failed to authenticate token."); + // Silently clear invalid token (401s are expected for non-authenticated users) setCookie(event, "userIDToken", "", { maxAge: 0, expires: new Date("2016-10-05") @@ -45,7 +45,7 @@ export async function getUserID(event: H3Event): Promise { return payload.id; } } catch (err) { - console.log("Failed to authenticate token."); + // Silently clear invalid token (401s are expected for non-authenticated users) setCookie(event, "userIDToken", "", { maxAge: 0, expires: new Date("2016-10-05")