From c2458575d526c2e9b973b4fd478b1b929a1ad7f4 Mon Sep 17 00:00:00 2001 From: sahana Date: Wed, 5 Nov 2025 10:44:17 -0500 Subject: [PATCH 1/9] refactor: update ButtonProps to use ReactNode type for children --- src/components/Button.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 205b6bc..799d200 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,11 +1,12 @@ import clsx from "clsx"; +import { ReactNode } from "react"; interface ButtonProps extends React.ButtonHTMLAttributes { color: "rose" | "red" | "white"; invert?: boolean; href?: string; disable?: boolean; - children: React.ReactNode; + children: ReactNode; } const baseClasses = From 464dba5c5eee787f9f2ba9a97dbd77c5225cc72a Mon Sep 17 00:00:00 2001 From: sahana Date: Wed, 5 Nov 2025 10:44:23 -0500 Subject: [PATCH 2/9] feat: implement background cache updater for RSVP count --- src/pages/api/rsvp.ts | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/pages/api/rsvp.ts b/src/pages/api/rsvp.ts index ee5ab2e..1681e3f 100644 --- a/src/pages/api/rsvp.ts +++ b/src/pages/api/rsvp.ts @@ -1,3 +1,4 @@ +// src/pages/api/rsvp.ts import { NextApiRequest, NextApiResponse } from "next"; async function getCount() { @@ -32,17 +33,33 @@ let cached = { value: -1, updated: 0 }; // Cache duration in milliseconds const CACHE_DURATION = 30000; -export default async function handler(_req: NextApiRequest, res: NextApiResponse) { - const now = Date.now(); - if (now - cached.updated > CACHE_DURATION) { - try { - const count = await getCount(); - cached = { value: count, updated: now }; - console.log("cached value", cached.value, "updatedAt", new Date(cached.updated).toISOString()); - } catch (err: unknown) { - console.error("getCount failed:", err); - res.status(500).json({ error: "couldnt get count" }); - } +// Extend global to hold a single timer across module reloads (avoid duplicate timers in dev) +declare global { + var __RSVP_CACHE_TIMER__: NodeJS.Timeout | undefined; +} + +async function updateCache() { + try { + const count = await getCount(); + cached = { value: count, updated: Date.now() }; + console.log("cached value", cached.value, "updatedAt", new Date(cached.updated).toISOString()); + } catch (err: unknown) { + console.error("updateCache failed:", err); + // keep previous cached value } - res.status(200).json({ count: cached.value }); -} \ No newline at end of file +} + +// Start background updater once per server instance +if (!global.__RSVP_CACHE_TIMER__) { + // initial immediate update (fire-and-forget) + updateCache().catch((e) => console.error("initial update failed:", e)); + + // schedule periodic updates + global.__RSVP_CACHE_TIMER__ = setInterval(() => { + updateCache().catch((e) => console.error("scheduled update failed:", e)); + }, CACHE_DURATION); +} + +export default function handler(_req: NextApiRequest, res: NextApiResponse) { + res.status(200).json({ count: cached.value, updatedAt: cached.updated }); +} From 59b75da4075dfd72ded0fa227aaf085c50418d32 Mon Sep 17 00:00:00 2001 From: sahana Date: Wed, 5 Nov 2025 11:01:47 -0500 Subject: [PATCH 3/9] positive test instead of negative one for clarity --- src/pages/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 1a22bb7..7048ebe 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -69,7 +69,7 @@ export default function Home() { let currRef = rawRef ? (rawRef as string) : null; - if (currRef && !/^\d+$/.test(currRef)) { + if (currRef && /^\d+$/.test(currRef)) { currRef = null; } From 52d8515c5c1eafb62eb8d569f9c7ed9445517435 Mon Sep 17 00:00:00 2001 From: jeninh Date: Sat, 1 Nov 2025 21:07:01 -0400 Subject: [PATCH 4/9] potato --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c0c5bc..753f43f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This is the site for [Aces](https://aces.hackclub.com), a 48 hour hackathon run If you're here to contribute to the Gallery, hold on tight! - + ## Development - Getting Started From b22874244381dddc0ca5d88b3ec5008bbb8ec8b4 Mon Sep 17 00:00:00 2001 From: Sahana Date: Wed, 5 Nov 2025 11:32:50 -0500 Subject: [PATCH 5/9] Update src/pages/index.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/pages/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 7048ebe..1a22bb7 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -69,7 +69,7 @@ export default function Home() { let currRef = rawRef ? (rawRef as string) : null; - if (currRef && /^\d+$/.test(currRef)) { + if (currRef && !/^\d+$/.test(currRef)) { currRef = null; } From a4c06e2a48792afef85a9e155f7bedd8e78bf91f Mon Sep 17 00:00:00 2001 From: Sahana Date: Wed, 5 Nov 2025 11:33:10 -0500 Subject: [PATCH 6/9] Update src/pages/api/rsvp.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/pages/api/rsvp.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/api/rsvp.ts b/src/pages/api/rsvp.ts index 1681e3f..da01d33 100644 --- a/src/pages/api/rsvp.ts +++ b/src/pages/api/rsvp.ts @@ -61,5 +61,9 @@ if (!global.__RSVP_CACHE_TIMER__) { } export default function handler(_req: NextApiRequest, res: NextApiResponse) { + if (cached.value === -1) { + res.status(503).json({ error: "Service Unavailable: RSVP count not yet available." }); + return; + } res.status(200).json({ count: cached.value, updatedAt: cached.updated }); } From 5eef35f36ba91e35992b34c5e79a3649fbf6ca62 Mon Sep 17 00:00:00 2001 From: Sahana Date: Wed, 5 Nov 2025 11:33:41 -0500 Subject: [PATCH 7/9] Update src/pages/api/rsvp.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/pages/api/rsvp.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/pages/api/rsvp.ts b/src/pages/api/rsvp.ts index da01d33..42266f4 100644 --- a/src/pages/api/rsvp.ts +++ b/src/pages/api/rsvp.ts @@ -60,6 +60,21 @@ if (!global.__RSVP_CACHE_TIMER__) { }, CACHE_DURATION); } +// Cleanup function to clear the background timer +export function shutdownRSVPTimer() { + if (global.__RSVP_CACHE_TIMER__) { + clearInterval(global.__RSVP_CACHE_TIMER__); + global.__RSVP_CACHE_TIMER__ = undefined; + console.log("RSVP cache timer cleared."); + } +} + +// Optionally clear timer on process exit (for dev/hot-reload environments) +if (typeof process !== "undefined" && process.on) { + process.on("SIGTERM", shutdownRSVPTimer); + process.on("SIGINT", shutdownRSVPTimer); + process.on("exit", shutdownRSVPTimer); +} export default function handler(_req: NextApiRequest, res: NextApiResponse) { if (cached.value === -1) { res.status(503).json({ error: "Service Unavailable: RSVP count not yet available." }); From cef6a5cf08b11b285ae466a59879227bf973abd7 Mon Sep 17 00:00:00 2001 From: sahana Date: Wed, 5 Nov 2025 23:31:59 -0500 Subject: [PATCH 8/9] feat: implement background cache updater for RSVP count with improved error handling --- src/pages/api/rsvp.ts | 54 ++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/pages/api/rsvp.ts b/src/pages/api/rsvp.ts index 42266f4..d4be00d 100644 --- a/src/pages/api/rsvp.ts +++ b/src/pages/api/rsvp.ts @@ -1,71 +1,78 @@ // src/pages/api/rsvp.ts import { NextApiRequest, NextApiResponse } from "next"; -async function getCount() { - let offset; +// Rewritten to ensure a single background updater reloads the cached count every 30s. + +async function getCount(): Promise { + let offset: string | undefined; let count = 0; do { const url = new URL(`https://api.airtable.com/v0/${process.env.RSVP_AIRTABLE_BASE_ID}/RSVPs`); if (offset) url.searchParams.set("offset", offset); - const res = await fetch(url, { + const res = await fetch(url.toString(), { headers: { Authorization: `Bearer ${process.env.RSVP_AIRTABLE_API_KEY}`, }, }); + if (!res.ok) { - console.error(res.statusText); - throw new Error(res.statusText); + const text = await res.text().catch(() => res.statusText); + console.error("Airtable request failed:", res.status, text); + throw new Error(text || "Airtable request failed"); } + const data = await res.json(); - count += data.records?.length ?? 0; + count += (data.records?.length ?? 0); offset = data.offset; - if (offset) await new Promise((resolve) => setTimeout(resolve, 200)); + if (offset) await new Promise((r) => setTimeout(r, 200)); } while (offset); return count; } -let cached = { value: -1, updated: 0 }; +type Cache = { value: number | null; updated: number }; +let cached: Cache = { value: null, updated: 0 }; -// Cache duration in milliseconds -const CACHE_DURATION = 30000; +// Default cache duration 30 seconds; can be overridden with env var (ms) +const CACHE_DURATION_MS = Number(process.env.RSVP_CACHE_DURATION_MS) || 30_000; // Extend global to hold a single timer across module reloads (avoid duplicate timers in dev) declare global { + var __RSVP_CACHE_TIMER__: NodeJS.Timeout | undefined; } -async function updateCache() { +async function updateCache(): Promise { try { const count = await getCount(); cached = { value: count, updated: Date.now() }; - console.log("cached value", cached.value, "updatedAt", new Date(cached.updated).toISOString()); + console.info("[rsvp] cache updated:", cached.value, new Date(cached.updated).toISOString()); } catch (err: unknown) { - console.error("updateCache failed:", err); - // keep previous cached value + console.error("[rsvp] updateCache failed:", err); + // keep previous cached value if any } } // Start background updater once per server instance if (!global.__RSVP_CACHE_TIMER__) { // initial immediate update (fire-and-forget) - updateCache().catch((e) => console.error("initial update failed:", e)); + updateCache().catch((e) => console.error("[rsvp] initial update failed:", e)); // schedule periodic updates global.__RSVP_CACHE_TIMER__ = setInterval(() => { - updateCache().catch((e) => console.error("scheduled update failed:", e)); - }, CACHE_DURATION); + updateCache().catch((e) => console.error("[rsvp] scheduled update failed:", e)); + }, CACHE_DURATION_MS); } // Cleanup function to clear the background timer -export function shutdownRSVPTimer() { +export function shutdownRSVPTimer(): void { if (global.__RSVP_CACHE_TIMER__) { clearInterval(global.__RSVP_CACHE_TIMER__); global.__RSVP_CACHE_TIMER__ = undefined; - console.log("RSVP cache timer cleared."); + console.info("[rsvp] RSVP cache timer cleared."); } } @@ -75,10 +82,15 @@ if (typeof process !== "undefined" && process.on) { process.on("SIGINT", shutdownRSVPTimer); process.on("exit", shutdownRSVPTimer); } + export default function handler(_req: NextApiRequest, res: NextApiResponse) { - if (cached.value === -1) { + if (cached.value === null) { res.status(503).json({ error: "Service Unavailable: RSVP count not yet available." }); return; } - res.status(200).json({ count: cached.value, updatedAt: cached.updated }); + + res.status(200).json({ + count: cached.value, + updatedAt: new Date(cached.updated).toISOString(), + }); } From 168403ff6756f50c61b1dda2ea03cb4bb191fede Mon Sep 17 00:00:00 2001 From: sahana Date: Wed, 5 Nov 2025 23:39:14 -0500 Subject: [PATCH 9/9] hana has had fucking enough. --- src/pages/api/rsvp.ts | 60 +------------------------------------------ 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/src/pages/api/rsvp.ts b/src/pages/api/rsvp.ts index d4be00d..b23b07d 100644 --- a/src/pages/api/rsvp.ts +++ b/src/pages/api/rsvp.ts @@ -33,64 +33,6 @@ async function getCount(): Promise { return count; } -type Cache = { value: number | null; updated: number }; -let cached: Cache = { value: null, updated: 0 }; - -// Default cache duration 30 seconds; can be overridden with env var (ms) -const CACHE_DURATION_MS = Number(process.env.RSVP_CACHE_DURATION_MS) || 30_000; - -// Extend global to hold a single timer across module reloads (avoid duplicate timers in dev) -declare global { - - var __RSVP_CACHE_TIMER__: NodeJS.Timeout | undefined; -} - -async function updateCache(): Promise { - try { - const count = await getCount(); - cached = { value: count, updated: Date.now() }; - console.info("[rsvp] cache updated:", cached.value, new Date(cached.updated).toISOString()); - } catch (err: unknown) { - console.error("[rsvp] updateCache failed:", err); - // keep previous cached value if any - } -} - -// Start background updater once per server instance -if (!global.__RSVP_CACHE_TIMER__) { - // initial immediate update (fire-and-forget) - updateCache().catch((e) => console.error("[rsvp] initial update failed:", e)); - - // schedule periodic updates - global.__RSVP_CACHE_TIMER__ = setInterval(() => { - updateCache().catch((e) => console.error("[rsvp] scheduled update failed:", e)); - }, CACHE_DURATION_MS); -} - -// Cleanup function to clear the background timer -export function shutdownRSVPTimer(): void { - if (global.__RSVP_CACHE_TIMER__) { - clearInterval(global.__RSVP_CACHE_TIMER__); - global.__RSVP_CACHE_TIMER__ = undefined; - console.info("[rsvp] RSVP cache timer cleared."); - } -} - -// Optionally clear timer on process exit (for dev/hot-reload environments) -if (typeof process !== "undefined" && process.on) { - process.on("SIGTERM", shutdownRSVPTimer); - process.on("SIGINT", shutdownRSVPTimer); - process.on("exit", shutdownRSVPTimer); -} - export default function handler(_req: NextApiRequest, res: NextApiResponse) { - if (cached.value === null) { - res.status(503).json({ error: "Service Unavailable: RSVP count not yet available." }); - return; - } - - res.status(200).json({ - count: cached.value, - updatedAt: new Date(cached.updated).toISOString(), - }); + getCount().then(count => res.status(200).json({ count })); }