okok
This commit is contained in:
@@ -12,40 +12,26 @@ const getBaseUrl = () => {
|
|||||||
return `http://localhost:${process.env.PORT ?? 3000}`;
|
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>({
|
export const api = createTRPCProxyClient<AppRouter>({
|
||||||
links: [
|
links: [
|
||||||
// will print out helpful logs when using client (suppress 401 errors)
|
// Only enable logging in development mode
|
||||||
|
...(process.env.NODE_ENV === "development"
|
||||||
|
? [
|
||||||
loggerLink({
|
loggerLink({
|
||||||
enabled: (opts) => {
|
enabled: (opts) => {
|
||||||
// Only log in development, and suppress 401 errors
|
// Suppress 401 UNAUTHORIZED errors from logs
|
||||||
const isDev = process.env.NODE_ENV === "development";
|
|
||||||
const is401 =
|
const is401 =
|
||||||
opts.direction === "down" &&
|
opts.direction === "down" &&
|
||||||
opts.result instanceof Error &&
|
opts.result instanceof Error &&
|
||||||
opts.result.message?.includes("UNAUTHORIZED");
|
opts.result.message?.includes("UNAUTHORIZED");
|
||||||
return isDev && !is401;
|
return !is401;
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
]
|
||||||
|
: []),
|
||||||
// identifies what url will handle trpc requests
|
// identifies what url will handle trpc requests
|
||||||
httpBatchLink({
|
httpBatchLink({
|
||||||
url: `${getBaseUrl()}/api/trpc`,
|
url: `${getBaseUrl()}/api/trpc`
|
||||||
fetch: customFetch
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,26 @@
|
|||||||
* Note: These utilities should only run in the browser
|
* 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
|
* Triggers haptic feedback on mobile devices
|
||||||
* @param duration - Duration in milliseconds (default 50ms for a light tap)
|
* @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."
|
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">
|
<div class="flex-1">
|
||||||
<Typewriter speed={30} keepAlive={2000}>
|
<Typewriter speed={30} keepAlive={2000}>
|
||||||
<div class="text-4xl">Hey!</div>
|
<div class="text-4xl">Hey!</div>
|
||||||
@@ -45,44 +45,7 @@ export default function Home() {
|
|||||||
</Typewriter>
|
</Typewriter>
|
||||||
<div class="pt-8 text-center">
|
<div class="pt-8 text-center">
|
||||||
<div class="pb-4">Some of my recent projects:</div>
|
<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">
|
<div class="flex flex-col items-center gap-2 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>
|
|
||||||
|
|
||||||
{/* FlexLöve */}
|
{/* 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 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>
|
<div>My LÖVE UI library</div>
|
||||||
@@ -125,6 +88,43 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<div class="max-w-3/4 pt-8 md:max-w-1/2">
|
<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";
|
privilegeLevel = payload.id === env.ADMIN_ID ? "admin" : "user";
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed to authenticate token:", err);
|
// Silently clear invalid token (401s are expected for non-authenticated users)
|
||||||
// Clear invalid token
|
|
||||||
setCookie(event.nativeEvent, "userIDToken", "", {
|
setCookie(event.nativeEvent, "userIDToken", "", {
|
||||||
maxAge: 0,
|
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 {
|
return {
|
||||||
event,
|
event,
|
||||||
userId,
|
userId,
|
||||||
privilegeLevel,
|
privilegeLevel
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +58,8 @@ const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
|
|||||||
return next({
|
return next({
|
||||||
ctx: {
|
ctx: {
|
||||||
...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({
|
return next({
|
||||||
ctx: {
|
ctx: {
|
||||||
...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";
|
return payload.id === env.ADMIN_ID ? "admin" : "user";
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed to authenticate token.");
|
// Silently clear invalid token (401s are expected for non-authenticated users)
|
||||||
setCookie(event, "userIDToken", "", {
|
setCookie(event, "userIDToken", "", {
|
||||||
maxAge: 0,
|
maxAge: 0,
|
||||||
expires: new Date("2016-10-05")
|
expires: new Date("2016-10-05")
|
||||||
@@ -45,7 +45,7 @@ export async function getUserID(event: H3Event): Promise<string | null> {
|
|||||||
return payload.id;
|
return payload.id;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed to authenticate token.");
|
// Silently clear invalid token (401s are expected for non-authenticated users)
|
||||||
setCookie(event, "userIDToken", "", {
|
setCookie(event, "userIDToken", "", {
|
||||||
maxAge: 0,
|
maxAge: 0,
|
||||||
expires: new Date("2016-10-05")
|
expires: new Date("2016-10-05")
|
||||||
|
|||||||
Reference in New Issue
Block a user