GitHub Pages + Cloudflare Worker/D1 app for viewing today's CNU meals and rating each meal with 1-5 stars.
- Today-only meal view (Korean + English)
- 1-5 star one-shot rating per meal per vote day
- Vote day boundary at 04:00 KST
- Weighted leaderboard (Bayesian-style)
- Daily sync from CNU source at 05:30 KST
- Manual sync via GitHub Actions
workflow_dispatch
frontend/GitHub Pages static siteworker/Cloudflare Worker API + D1 schema/migrationsscripts/CNU scraper and payload ingestertests/parser/parser teststests/worker/worker logic tests
- Node.js 20+
- Python 3.11+
- Cloudflare account with Workers + D1
- Install Node dependencies:
npm install- Install Python dependencies:
pip install -r scripts/requirements.txt- Create D1 database (once) and copy database id:
npx wrangler d1 create cnu-meal- Set
worker/wrangler.tomldatabase id:
- Replace
REPLACE_WITH_D1_DATABASE_IDwith your D1 database id.
- Apply migration:
npx wrangler d1 migrations apply DB --local --config worker/wrangler.toml- Set Worker secrets:
npx wrangler secret put SYNC_ADMIN_TOKEN --config worker/wrangler.toml
npx wrangler secret put DEVICE_SALT --config worker/wrangler.toml
npx wrangler secret put IP_SALT --config worker/wrangler.toml- Optional: lock CORS origin:
- Set
ALLOWED_ORIGINinworker/wrangler.tomlto your GitHub Pages origin (for example,https://<username>.github.io).
- Start Worker locally:
npm run worker:dev- Point frontend to API:
- Edit
frontend/config.jsand setAPI_BASE_URLto your Worker URL.
Dry-run without ingest:
python scripts/sync_menu.py --date 2026-03-03 --dry-runIngest to worker:
WORKER_URL="https://<worker>.workers.dev" \
SYNC_ADMIN_TOKEN="<token>" \
python scripts/sync_menu.py --date 2026-03-03 --run-type manualWhen creating the Worker project from the Cloudflare UI with connected GitHub repo, use these exact values:
- Project name:
cnu_meal - Production branch:
main - Build command: leave blank (Cloudflare already installs dependencies automatically). If you must set one, use:
npm ci- Deploy command:
sed -i "s/REPLACE_WITH_D1_DATABASE_ID/$CF_D1_DATABASE_ID/" worker/wrangler.toml && npx wrangler d1 migrations apply DB --remote --config worker/wrangler.toml && npx wrangler deploy --config worker/wrangler.toml- Builds for non-production branches: enabled
- Non-production branch deploy command:
sed -i "s/REPLACE_WITH_D1_DATABASE_ID/$CF_D1_DATABASE_ID/" worker/wrangler.toml && npx wrangler versions upload --config worker/wrangler.toml- Path:
/ - API token:
Create new token
Cloudflare build variables:
CF_D1_DATABASE_ID: your D1 Database UUID (from D1 dashboard)
After first deploy, add Worker encrypted secrets:
SYNC_ADMIN_TOKENDEVICE_SALTIP_SALT
Worker logic tests:
npm run test:workerParser tests:
pytestSet these repository secrets:
CF_API_TOKENCF_ACCOUNT_IDCF_D1_DATABASE_IDCF_WORKER_URLSYNC_ADMIN_TOKEN
- Daily sync:
20:30 UTC(05:30 KST) - Manual sync:
workflow_dispatchwith optionaltarget_date