Visualizador “pro” para leaderboards privados de Advent of Code: podium, tabla interactiva, detalle por miembro, modo Versus y una página de estadísticas.
Proyecto pensado para portafolio: mezcla frontend (UX + estado en cliente) con backend (API route + cache + variables de entorno) y performance (revalidación controlada).
- Consume el JSON oficial del leaderboard privado de AoC.
- Normaliza y ordena miembros por estrellas y score.
- UI con:
- Podium (top 3)
- Tabla con modal de miembro
- “Daily champion” (quién completó más rápido el último día activo)
- Modo Versus (comparar 2 miembros)
- Página
/statscon KPIs + distribución por día y por hora
Flujo de datos
- El cliente pide
GET /api/aoc. - El backend (Route Handler de Next) hace
fetchal endpoint de AoC usando la cookiesession. - Para no spamear AoC y mantener la app rápida, la respuesta se cachea 15 minutos.
- La UI usa SWR para revalidar cada 15 min y al volver a enfocar la pestaña.
Piezas clave
app/api/aoc/route.ts: backend que obtiene + transforma + cachea el leaderboard.app/page.tsx: UI principal (SWR, podium, tabla, detección de cambios conlocalStorage).app/stats/page.tsx: estadísticas agregadas en cliente.
Este handler encapsula el fetch a AoC y lo cachea con unstable_cache para reducir latencia y llamadas:
import { unstable_cache } from "next/cache";
const getCachedLeaderboard = unstable_cache(
async () => {
const res = await fetch(
`https://adventofcode.com/${process.env.AOC_YEAR ?? "2024"}/leaderboard/private/view/${process.env.AOC_BOARD}.json`,
{ headers: { Cookie: `session=${process.env.AOC_SESSION}` } }
);
if (!res.ok) {
throw new Error(`Error AoC: ${res.status} ${await res.text()}`);
}
const data = await res.json();
const members = Object.values(data.members || {}).map((m: any) => ({
id: m.id,
name: m.name,
stars: m.stars,
score: m.local_score,
completion_day_level: m.completion_day_level,
}));
members.sort((a: any, b: any) => b.stars - a.stars || b.score - a.score);
return { ok: true, updated: new Date().toISOString(), members };
},
["aoc-leaderboard-data"],
{ revalidate: 900 }
);- Next.js (App Router)
- React + TypeScript
- Tailwind CSS
- SWR (cache/revalidate en cliente)
- Node.js (recomendado 18+)
- Una cookie de sesión válida de Advent of Code
Crea un archivo .env.local en la raíz:
AOC_SESSION=tu_cookie_session_de_aoc
AOC_BOARD=123456
# Opcional
AOC_YEAR=2024- Inicia sesión en https://adventofcode.com
- Abre DevTools → Application/Storage → Cookies →
session - Copia el valor
Importante:
- No comitees
.env.local. - La cookie da acceso a tu cuenta, trátala como un secreto.
Instalar dependencias:
npm installModo desarrollo:
npm run devAbrir:
Build de producción:
npm run build
npm run start- Importa el repo en Vercel (New Project → Import Git Repository).
- Configura estas Environment Variables (Project Settings → Environment Variables):
AOC_SESSIONAOC_BOARDAOC_YEAR(opcional)
- Deploy.
Notas:
- Este proyecto hace el fetch a AoC desde el backend (Route Handler). En Vercel, las env vars se inyectan en el runtime del servidor, así que no se exponen al cliente.
- Hay un
vercel.jsoncon un cron apuntando a/api/daily-task. Si quieres usarlo como “warm up”, cambia ese path a/api/aoco crea ese endpoint.
ok: false/ error 500 en/api/aoc:- Revisa
AOC_SESSION(expirada o incorrecta) - Revisa
AOC_BOARD(id del leaderboard) - Verifica que el año (
AOC_YEAR) exista y que el board sea de ese evento
- Revisa
Este proyecto intenta ser “buen ciudadano” con AoC:
- Backend cacheado 15 minutos.
- Cliente revalida cada 15 minutos y al volver al foco.
Si quieres, también puedo añadir una sección de “Deploy en Vercel” basada en vercel.json y las env vars del proyecto.