尚未检测到更新。GitHub 发布版本后会显示在这里。
制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A private NFL pick'em league for friends: a modern, animated take on Yahoo Pick'em.
Stack: TanStack Start (React 19, TanStack Router + Query), Tailwind CSS v4 + shadcn/ui, Motion, Supabase (Postgres with row-level security + magic-link auth), deployed on Vercel. NFL schedule, scores and spreads come from ESPN's public scoreboard feed.
npm install
cp .env.example .env.local # fill in Supabase values
npm run dev # http://localhost:3000
Other scripts: npm test (Vitest), npm run check (Biome), npm run build.
Database: run each file in supabase/migrations/ in order in the Supabase SQL editor. 0005_profile_flair.sql also creates the public avatars storage bucket for uploaded profile photos. 0029_custom_emoji.sql creates the public emoji bucket for custom reaction emoji. GIFs in comments need a free GIPHY API key (developers.giphy.com) set as GIPHY_API_KEY in Vercel.
Auth redirect: in Supabase > Authentication > URL Configuration, set the Site URL to your Vercel URL and add http://localhost:3000/** and https://<your-app>.vercel.app/** as redirect URLs.
Vercel: import the repo and set VITE_SUPABASE_URL, VITE_SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEY and CRON_SECRET (any random string).
Pick reminder emails (optional): they're sent from a Gmail account. Turn on 2-Step Verification for it, create an app password, and set GMAIL_USER (the address) and GMAIL_APP_PASSWORD in Vercel. In GitHub, add the repo secret CRON_SECRET (same value as Vercel) and the repo variable APP_URL (your Vercel URL); the Pick reminders workflow then runs hourly.
Sign-in emails: Supabase's built-in email sender only delivers to your own Supabase team's addresses (and a couple an hour), so friends' sign-in emails won't arrive without your own SMTP. In Supabase > Authentication > Emails > SMTP Settings, turn on custom SMTP with host smtp.gmail.com, port 465, your Gmail address as username and sender, and the same app password. Then replace the Magic Link email template (subject Your Air Bud Pick'em sign-in link) with:
<h2>Sign in to Air Bud Pick'em</h2>
<p><a href="{{ .RedirectTo }}&token_hash={{ .TokenHash }}&type=email">Sign in</a></p>
<p>Or type this code on the sign-in page: <strong>{{ .Token }}</strong></p>
<p>If you didn't ask for this, ignore this email.</p>
The link goes straight to the app (not supabase.co), which spam filters like better, and it works in any browser. The code covers the home-screen app. Friends should mark the first email "Not spam" and add the sender to their contacts.
Backups (recommended): add the repo secret SUPABASE_DB_URL (Supabase > Connect > Session pooler string, with your database password). The Database backup workflow then saves the app's tables every Tuesday as a downloadable artifact.
games table (at most once a minute), and a daily Vercel cron hits /api/sync.locked_spread; ATS scoring only uses that value.Pick reminders GitHub Actions workflow calls /api/reminders hourly. When a game day's first kickoff is 3 hours out or less, everyone with open picks that day gets one email (or, if they chose Discord DMs on their profile, shows up in the bot's /api/bot/reminders). reminder_log keeps it to one email per person per game day. Running the workflow by hand defaults to a dry run.src/lib/scoring.ts: scoring for all three modes (tested)src/lib/nfl/espn.ts: ESPN scoreboard provider (tested)src/lib/standings.ts: season and weekly standings (tested)src/lib/flair.ts: standings badges, streak flame, and last-place trophy (tested)src/lib/reminders.ts: when pick reminders are due and their wording (tested)src/server/: server functions for auth, leagues, picks and the game syncsupabase/migrations/: tables, security rules, and the create/join league functionssrc/routes/: home, sign in, my leagues, new league, league (picks + standings), invite linkssrc/routes/api/bot/: read-only JSON API for a Discord bot (games, picks after kickoff, who still needs to pick, standings); see docs/bot-api.mdMilestone 1: sign in, create and join leagues, weekly picks for all three scoring modes, and standings.