okok
This commit is contained in:
@@ -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<AppRouter>({
|
||||
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`
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
@@ -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<Response>
|
||||
*/
|
||||
export async function safeFetch(
|
||||
input: RequestInfo | URL,
|
||||
init?: RequestInit
|
||||
): Promise<Response> {
|
||||
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)
|
||||
|
||||
@@ -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."
|
||||
/>
|
||||
|
||||
<main class="flex h-full flex-col gap-8 p-4 text-xl">
|
||||
<main class="flex h-full flex-col gap-8 p-4 pt-16 text-xl">
|
||||
<div class="flex-1">
|
||||
<Typewriter speed={30} keepAlive={2000}>
|
||||
<div class="text-4xl">Hey!</div>
|
||||
@@ -45,44 +45,7 @@ export default function Home() {
|
||||
</Typewriter>
|
||||
<div class="pt-8 text-center">
|
||||
<div class="pb-4">Some of my recent projects:</div>
|
||||
<div class="flex flex-col items-center gap-6 2xl:flex-row 2xl:items-start 2xl:justify-center">
|
||||
{/* Life and Lineage */}
|
||||
<div class="border-surface0 flex w-full max-w-2xl flex-col gap-2 rounded-md border-2 p-4 text-center">
|
||||
<div>My mobile game:</div>
|
||||
<a
|
||||
class="text-blue hover-underline-animation"
|
||||
href="https://apps.apple.com/us/app/life-and-lineage/id6737252442"
|
||||
>
|
||||
Life and Lineage
|
||||
</a>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<div class="aspect-auto w-full overflow-hidden rounded-lg">
|
||||
<img
|
||||
src="/lineage-home.png"
|
||||
alt="Life and Lineage Home"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div class="aspect-auto w-full overflow-hidden rounded-lg">
|
||||
<video
|
||||
src="/lineage-preview.mp4"
|
||||
class="h-full w-full object-cover"
|
||||
autoplay
|
||||
loop
|
||||
muted
|
||||
playsinline
|
||||
/>
|
||||
</div>
|
||||
<div class="aspect-auto w-full overflow-hidden rounded-lg">
|
||||
<img
|
||||
src="/lineage-shops.png"
|
||||
alt="Life and Lineage Shops"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center gap-2 2xl:flex-row 2xl:items-start 2xl:justify-center">
|
||||
{/* FlexLöve */}
|
||||
<div class="border-surface0 flex w-full max-w-md flex-col gap-2 rounded-md border-2 p-4 text-center">
|
||||
<div>My LÖVE UI library</div>
|
||||
@@ -125,6 +88,43 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Life and Lineage */}
|
||||
<div class="border-surface0 flex w-full max-w-3/4 flex-col gap-2 rounded-md border-2 p-4 text-center">
|
||||
<div>My mobile game:</div>
|
||||
<a
|
||||
class="text-blue hover-underline-animation"
|
||||
href="https://apps.apple.com/us/app/life-and-lineage/id6737252442"
|
||||
>
|
||||
Life and Lineage
|
||||
</a>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
||||
<div class="aspect-auto w-full overflow-hidden rounded-lg">
|
||||
<img
|
||||
src="/lineage-home.png"
|
||||
alt="Life and Lineage Home"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div class="aspect-auto w-full overflow-hidden rounded-lg">
|
||||
<video
|
||||
src="/lineage-preview.mp4"
|
||||
class="h-full w-full object-cover"
|
||||
autoplay
|
||||
loop
|
||||
muted
|
||||
playsinline
|
||||
/>
|
||||
</div>
|
||||
<div class="aspect-auto w-full overflow-hidden rounded-lg">
|
||||
<img
|
||||
src="/lineage-shops.png"
|
||||
alt="Life and Lineage Shops"
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-3/4 pt-8 md:max-w-1/2">
|
||||
|
||||
@@ -26,11 +26,10 @@ async function createContextInner(event: APIEvent): Promise<Context> {
|
||||
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<Context> {
|
||||
return {
|
||||
event,
|
||||
userId,
|
||||
privilegeLevel,
|
||||
privilegeLevel
|
||||
};
|
||||
}
|
||||
|
||||
@@ -59,8 +58,8 @@ 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
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,8 +74,8 @@ const enforceUserIsAdmin = t.middleware(({ ctx, next }) => {
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
userId: ctx.userId!, // userId is non-null for admins
|
||||
},
|
||||
userId: ctx.userId! // userId is non-null for admins
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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<string | null> {
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user