Zonara Trainer
Zonara Trainer – Fitness Coach: support, privacy policy and Health & Safety terms
翻译准备中
尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
Personal Mountain Project route tracker — Vercel + Neon + Resend
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗A personal Mountain Project route tracker. Paste an MP route URL, and it scrapes ticks, comments, star rating, suggested grade, to-do count, and page views on a daily schedule, stores time-series snapshots, emails you when anything new shows up, and renders sortable trends.
5.9)5.9 (29))2.9 (44))Sent once per scrape that finds new activity. Per route, it includes:
The scraper uses Mountain Project's public JSON endpoints wherever possible (no auth required, stable item IDs for dedup):
GET /api/v2/routes/{id}/ticks — tick total + per-tick metadataGET /api/v2/routes/{id}/stars — star vote total + individual votesGET /api/v2/routes/{id}/ratings — suggested grades (used for consensus)GET /api/v2/routes/{id}/todos — to-do list countFor things not exposed via API (name, location, official grade, page views,
comment count) it falls back to parsing the route page HTML. The official grade
comes from the .rateYDS element; location uses the JSON-LD BreadcrumbList;
star rating average uses the JSON-LD aggregateRating.
npm install
Create a Neon project at https://neon.tech, copy the connection string into
.env.local:
cp .env.example .env.local
# Edit .env.local
Push the initial schema (this uses Drizzle Kit; for additive changes the SQL
in scripts/migrate-add-columns.ts is run directly):
npm run db:push
If db:push prompts about column renames you've already applied, run the
direct SQL migration instead:
npx tsx --env-file=.env.local scripts/migrate-add-columns.ts
The digest is sent through a Gmail account over SMTP (free, ~500 emails/day).
GMAIL_USER and the 16-character app password
(no spaces) in GMAIL_APP_PASSWORD.NOTIFY_EMAIL to one or more recipients, comma-separated.npm run dev
# open http://localhost:3000
Paste any MP route URL like
https://www.mountainproject.com/route/201171367/i-cant-believe-its-not-butte-r
into the form. It scrapes immediately, stores the first snapshot, and shows
up in the table.
npx vercel
Set the same env vars in the Vercel dashboard (Project → Settings →
Environment Variables) for Production. Generate a long random string for
CRON_SECRET — Vercel Cron will send it automatically as a Bearer token,
and we reject requests without it:
openssl rand -hex 32
The cron is declared in vercel.json and runs at 13:00 UTC daily. Change
the schedule string there if you want different timing.
You can force a scrape any time:
curl "https://your-app.vercel.app/api/cron/scrape?secret=YOUR_CRON_SECRET"
Note: Vercel's Hobby plan limits cron jobs to once-per-day max. For more frequent runs you'd need Pro.
By default the URL is public. To keep it private: Vercel dashboard → Project → Settings → Deployment Protection → Vercel Authentication. That gates the whole site behind your Vercel login — no auth code needed.
Every item from the public API has a stable MP ID. We store a fingerprint
(mp-tick-{id}, mp-star-{id}, etc.) in a seen_* table per route, and
anything whose fingerprint isn't there yet gets flagged as "new" and quoted
in the digest email. Comments are parsed from HTML but each <table id="Comment-N">
also has a stable ID, so the same scheme works.
This means deltas are exact, not heuristic — if 50 new ticks land in a day, all 50 are detected as new (though only the first ~20 from the API's recent page get inline samples in the email; the rest are summarized as "50 new ticks").
lib/scraper.ts — fetches the route HTML + the four JSON APIs, returns one
unified ScrapedRoute.lib/grade.ts — YDS-grade-to-numeric conversion (for plotting + sorting).lib/scrape-runner.ts — per-route scrape → snapshot → diff → email digest.lib/notify.ts — email rendering + sending via Gmail SMTP.app/api/cron/scrape/route.ts — the cron handler.app/page.tsx + components/routes-table.tsx — main UI: sortable table,
expandable rows, charts.scripts/migrate-add-columns.ts — direct-SQL migration script for the
additive schema changes that db:push finds ambiguous.lib/scraper.ts will need updating.