A community-built hub for Poke recipes. Front-end + Supabase backend.
index.html— the site (single file, no build step), plusllms.txt,robots.txt,sitemap.xml,og-image.htmlfunctions/api/[[path]].js— Cloudflare Pages Function proxying/api/*to the Supabase Edge Functionssupabase/migrations/— database schema, RLS policies, triggers (apply in filename/timestamp order)supabase/functions/— therecipes-api,scrape-recipe, andadmin-actionEdge FunctionsREADME.md— this file
- Go to supabase.com → New project. Pick a name, region close to you, and set a strong DB password.
- Wait ~1 min for provisioning.
- In the Supabase dashboard, open SQL Editor (left sidebar).
- Run the files in
supabase/migrations/in filename order (oldest timestamp first): paste each one, Run, then the next. Each should report "Success. No rows returned." All are idempotent, so re-running is safe. (If you have the Supabase CLI linked,supabase db pushdoes this for you — see "Deploying with the CLI" below.) - Tables are now set up with RLS enabled.
- Authentication → Providers → Google → toggle on.
- Follow the Supabase link to the Google Cloud Console, create an OAuth 2.0 Client ID (type: Web application).
- Add the Authorized redirect URI shown by Supabase (looks like
https://xxxx.supabase.co/auth/v1/callback). - Copy the Client ID + Client Secret back into Supabase. Save.
- Authentication → Providers → GitHub → toggle on.
- Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.
- Authorization callback URL = the same
/auth/v1/callbackURL from Supabase. - Copy Client ID + generate + copy Client Secret into Supabase. Save.
- Authentication → URL Configuration → Site URL: set this to wherever you deploy (e.g.
https://pokecookbook.com). For local testing usehttp://localhost:8000. - Under Redirect URLs, add both your production URL and any local dev URL you use.
Open index.html, find this block near the bottom:
const SUPABASE_URL = 'YOUR_SUPABASE_URL';
const SUPABASE_ANON_KEY = 'YOUR_SUPABASE_ANON_KEY';Replace both with values from Supabase → Project Settings → API:
- Project URL →
SUPABASE_URL - anon public key →
SUPABASE_ANON_KEY
The anon key is safe to expose in the browser — RLS policies do the real enforcement.
Any static host works. Easiest options:
Cloudflare Pages (free, fast)
- Drag the folder into pages.cloudflare.com or connect a GitHub repo.
Netlify / Vercel
netlify deployor drag-drop, or push to GitHub and connect.
GitHub Pages
- Push
index.htmlto a repo, enable Pages in repo settings.
After deploy, go back to Supabase → Authentication → URL Configuration and make sure your live URL is listed in Site URL and Redirect URLs.
The frontend (index.html, functions/) auto-deploys to Cloudflare Pages on push to main. The Supabase backend (supabase/) is deployed separately with the Supabase CLI.
One-time setup:
supabase login # opens a browser to authorize, or paste a personal access token
supabase link --project-ref hznlynnxfwmnxixxnjnl # prompts for the database passwordThe four migrations were originally applied by hand in the dashboard, so the remote migration history doesn't yet know about them. Tell it they're already applied (this records them without re-running the SQL):
supabase migration list # shows local vs remote; the four will show as local-only
supabase migration repair --status applied 20260414140001 20260414140002 20260414140003 20260414140004After that, normal workflow:
supabase migration new <name> # create a new migration, edit the generated file
supabase db push # apply pending migrations to the remote DB
supabase functions deploy # deploy all three edge functions
supabase functions deploy recipes-api # or deploy just oneverify_jwt = false for all three functions is set in supabase/config.toml, matching their current deployment. The ADMIN_PASSWORD secret lives in the Supabase dashboard (Edge Functions → Secrets) and is not affected by deploys.
- Anyone signed in can flag a recipe (one flag per user per recipe).
- After 5 unique flags, the recipe is auto-hidden (
is_hidden = true). RLS prevents hidden recipes from being fetched. - To review flagged content, run this in SQL Editor:
select r.id, r.title, r.flag_count, r.url, r.created_at
from recipes r
where is_hidden = true
order by r.flag_count desc;To un-hide something (e.g. a false-flag storm):
update recipes set is_hidden = false, flag_count = 0 where id = 'recipe-uuid-here';Want to change the flag threshold? Edit the 5 in bump_flag_count() inside supabase/migrations/20260414140001_initial_schema.sql and re-run it.
Front-end only, purely visual: vote_count / hours_since_post^0.6. Tune the exponent in hotScore() in index.html:
- Higher (e.g.
0.8) = newer posts rise faster - Lower (e.g.
0.4) = votes matter more, decay is slower
In your host (Cloudflare Pages / Netlify / Vercel), add the custom domain, point DNS at them. Then add the new domain to Supabase's Site URL / Redirect URLs.
- Rate limiting on inserts — edge function that checks submissions per user per hour
- Search — Postgres full-text index on
title + description - Profile pages —
/u/handleshowing a user's recipes - Comments — add a
commentstable with same RLS pattern as flags/votes - Admin role — a
profiles.is_adminboolean + policy allowing update onrecipes.is_hidden - Image preview — og:image scraping via edge function on submit
Auth redirect loops or "Invalid redirect URL" → Add the exact URL (with scheme, no trailing slash) to Supabase → Auth → URL Configuration → Redirect URLs.
"new row violates row-level security policy"
→ Usually means the user isn't signed in, or the user_id in the payload doesn't match auth.uid(). The frontend uses state.user.id which should match.
Votes / flags aren't updating the counter
→ Check the triggers in the schema. Re-run supabase/migrations/20260414140001_initial_schema.sql (it's idempotent).
OAuth "redirect_uri_mismatch" from Google → The callback URL in Google Cloud must match Supabase's exactly. Copy-paste from the Supabase provider settings.