OpenAthlete
AGPLv3 endurance training platform: EU-oriented hosting, transparent CTL/ATL/TSB logic in code, self-hosting, and full export. Built in Grenoble.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Turn Facebook posts into a personal travel blog with an interactive world map, documenting my worldwide travel and marathon footprint.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
The Next.js web app for MaraMap — a running and travel log that turns years of
Facebook posts into an interactive map, timeline and list.
Pairs with MaraMap-Backend
(NestJS Content API + Supabase + Cloudflare R2).
The app needs the backend running on port 3016 for anything data-driven.查看完整 README ↗The Next.js web app for MaraMap — a running and travel log that turns years of Facebook posts into an interactive map, timeline and list.
Pairs with MaraMap-Backend (NestJS Content API + Supabase + Cloudflare R2).
| Framework | Next.js 16 (App Router, Turbopack) + React 19 |
| Styling | Tailwind CSS v4 |
| Map | Leaflet + react-leaflet + marker clustering |
| Motion / icons | framer-motion, lucide-react |
| Tests | Jest + Testing Library (unit), Playwright (E2E) |
| Package manager | Bun (what CI uses) |
| Hosting | Vercel |
bun install # also installs the pre-push git hook (see Testing)
bun run dev # http://localhost:3006
The app needs the backend running on port 3016 for anything data-driven.
A single gitignored .env holds everything this machine needs: NEXT_PUBLIC_API_URL
and NEXT_PUBLIC_CARTO_BASEMAP_API_KEY. There's no .env.development /
.env.production split locally — this repo only ever runs one environment
on a dev machine, and the deployed values on Vercel are configured in its own
dashboard rather than from a committed file (these were always gitignored, so
they never reached a Vercel build in the first place).
Leave NEXT_PUBLIC_API_URL empty for local dev. src/utils/apiBase.ts then
derives the backend URL from the page's own hostname, so the app works from
both the laptop (localhost:3016) and a phone on the same LAN
(<machine-LAN-IP>:3016) with no hardcoded address to update when the IP changes.
src/
app/
(public)/ # Public site — home, /log/[id], /personal-best
admin/ # Admin area — login, new, edit/[id], import, personal-best
map/ # Full-screen map view
components/ # MapView, ListView, TimelineView, CountryModal, SiteHeader…
admin/ # MediaManager, Combobox
utils/ # apiBase, formatLocation, locationData, postHelpers
| Route | Purpose |
|---|---|
/ | Home — map / list / timeline of all posts |
/log/[id] | Single post with photo and video carousels + lightbox |
/personal-best | Personal-best records |
/map | Full-screen map |
/admin | Post management (JWT auth against the backend) |
/admin/new, /admin/edit/[id] | Create / edit a post, with direct-to-R2 media upload |
/admin/import | Facebook export import review flow |
bun run test # Jest unit tests with coverage
bun run test:watch
bun run test:e2e # Playwright (starts its own dev server on :3006)
bun run test:e2e:ui # Playwright UI mode
bun run lint
A pre-push hook (.githooks/pre-push, installed automatically by the
prepare script) runs eslint --fix, amends any auto-fixes into the last
commit, then runs the unit tests and aborts the push if they fail.
.github/workflows/ci.yml runs on push and PR to develop / master:
lint → unit tests → Playwright E2E → bun run build. Test results are published
as PR checks via dorny/test-reporter.
These are non-obvious and each one was learned the hard way.
Images bypass the Vercel image optimizer (next.config.ts,
images.unoptimized: true). On the Hobby plan the optimizer has a monthly
transformation quota; photo-heavy post pages blew past it and every
next/image request started returning 402, breaking every image. R2 media is
already Cloudflare-served (free egress, CDN-cached) and FB-export sized
(~200 KB median). If per-device srcset/AVIF is wanted back, do it in
Cloudflare or pre-generate variants in the ETL — not Vercel.
Fonts are loaded with a plain <link>, not next/font/google
(src/app/layout.tsx). next/font downloads woff2 files at build time, and
when the Vercel builder could not reach fonts.gstatic.com the build hard-failed
with no fallback. Weights must stay in sync with the font-* utilities in use —
serif 900 in particular, which every font-serif font-black heading depends on.
Video preload is deliberate, not accidental (src/app/(public)/log/[id]/page.tsx).
The main carousel keeps preload="metadata" because the first frame is the
visual there. The thumbnail rail renders an icon tile instead of a <video>
(at 56 px the frame is unreadable, and each one costs a metadata fetch), and the
lightbox uses preload="none" because every slide in the strip is rendered at
once. See docs/VIDEO_PLAN.md.
Bun is the only supported package manager. package.json's
packageManager field pins the exact version and a preinstall hook
(npx only-allow bun) blocks npm/pnpm/yarn install attempts, so a
second lockfile can't reappear. A past package-manager switch dropped the
Vercel build cache and contributed to a build failure — that's the incident
this guards against.