OpenAthlete
AGPLv3 기반 지구력 훈련 플랫폼—EU 호스팅, 투명한 로직, 자체 호스팅·전체 내보내기 지원
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Running-club PWA for the Madregot club. Coaches parse training plans from text or
images with AI and push structured workouts to athletes' Garmin watches; athletes
get a Hebrew-first mobile app with their program, a Strava-style activity feed,
attendance, academy reports and push notifications.README 전문 보기 ↗Running-club PWA for the Madregot club. Coaches parse training plans from text or images with AI and push structured workouts to athletes' Garmin watches; athletes get a Hebrew-first mobile app with their program, a Strava-style activity feed, attendance, academy reports and push notifications.
next-intl (he/en, RTL)garmin-connect packagemaxDuration = 300)Requires Node >= 22.12.
npm install
Create a project at supabase.com, then run the SQL in
this order in the SQL Editor. Both steps are required — schema.sql creates
the base tables (coaches, groups, athletes, weekly_plans,
workout_deliveries, athlete_activities) and the migrations build everything
else on top of them. Running only one of the two leaves the app unable to boot.
supabase/schema.sql — the base tables.supabase/migrations/ in numeric order, from
000_extensions.sql through the highest-numbered file (currently
076_notification_image.sql, 72 files).Migrations are applied by hand in the SQL Editor; there is no supabase db push
wired up in this repo. To concatenate them in order for a single paste:
for f in supabase/migrations/*.sql; do echo "-- $f"; cat "$f"; echo; done > /tmp/all-migrations.sql
Then copy your project URL and API keys from Settings → API.
cp .env.local.example .env.local
.env.local.example documents every variable the app reads, grouped by the
feature it unlocks and annotated with what breaks when it's missing. Only the
first block ("Core") plus ENCRYPTION_KEY is needed to boot; the rest are
per-feature and degrade gracefully when blank.
Two that catch people out:
ENCRYPTION_KEY must be 64 hex characters, and is not rotatable in
place — a new key cannot decrypt existing Garmin/Strava connections. Generate
a fresh one only for an empty database.NEXT_PUBLIC_VAPID_PUBLIC_KEY /
VAPID_PRIVATE_KEY pair (npx web-push generate-vapid-keys). With either
missing, sending silently no-ops rather than erroring.npm run dev
Open http://localhost:3000.
npx vercel
Add the same environment variables in Vercel → Settings → Environment
Variables. The cron schedules in vercel.json are registered automatically on
deploy.
When pulling Vercel env vars locally, always target a temp path —
vercel env pull /tmp/mc-vercel.env --environment=production. The default destination is.env.local, which overwrites your own file.
npm run check # typecheck + tests + lint + build (run before pushing)
npm run typecheck
npm run test # vitest
npm run lint
Bump src/lib/version.ts and package.json together for each change (patch
bumps by default).
This trips up every change that touches a timestamp, so it's documented in
src/lib/utils.ts as well:
athlete_activities.start_time) store Garmin's
startTimeLocal wall-clock in a TIMESTAMPTZ column that Postgres reads as
UTC. The athlete's real local time therefore is the timestamp's UTC
wall-clock — accessors must read UTC parts, and comparing these to Date.now()
silently loses Israel's offset.israelNow() / israelToday() / israelDateAnchor().Do not try to fix this by setting a global TZ — that was tried and
reverted; TZ is deliberately unset outside of individual tests.
Further design notes live in docs/ (academy-feature.md, feed-plan.md,
run-chat-architecture.md, competitor-research.md).