OpenTrailPaper
LilyGO T5S3 4.7" E-Paper PRO용 오픈소스 자전거 컴퓨터 펌웨어, 오프라인 지도·GPX 경로·FIT 기록·블루투스 센서 지원
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Tracking your commutes from SF to G/A/PA/MTV/FB/ETC
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A competitive cycling commute tracker for the SF2G community. Connect your Strava account, sync your rides, and see how you stack up across seven Bay Area corridors.
Live at sf2ging.com
| Layer | Technology |
|---|---|
| Framework | TanStack Start (full-stack React) |
| UI | React 19, TanStack Table, TanStack Virtual |
| Data | TanStack Query |
| Backend | Supabase (PostgreSQL + REST) |
| Auth | Strava OAuth 2.0 |
| Charts | Recharts |
| Maps | Leaflet |
| Analytics | PostHog |
| Styling | Vanilla CSS (custom properties, glassmorphism) |
| Testing | Vitest |
| Bundler | Vite |
| Deploy | Cloudflare Workers (Free plan) |
| Package Manager | pnpm |
packageManager)brew install colima docker && colima start)brew install supabase/tap/supabase)localhost in its Authorization Callback Domain (only needed to log in locally)git clone https://github.com/pmaier983/sf2g.git
cd sf2g
pnpm install
# Local database (never point local dev at production)
pnpm db:start # starts Postgres + PostgREST in Docker
pnpm db:reset # applies supabase/migrations/* and supabase/seed/*
cp .env.example .env.local # local Supabase defaults are pre-filled
pnpm dev # http://localhost:5173
| Command | Description |
|---|---|
pnpm dev | Start Vite dev server (localhost:5173) |
pnpm build | Production build for Cloudflare |
pnpm preview | Preview production build locally |
pnpm deploy | Build and deploy to Cloudflare Workers |
pnpm typecheck | Run TypeScript type checking |
pnpm test | Run tests once |
pnpm test:watch | Run tests in watch mode |
pnpm test:db | DB tests against the local fixture DB (resets it when migrations or fixtures differ) |
pnpm e2e | Playwright smoke tests (reuses a running pnpm dev) |
pnpm db:start / db:stop | Start / stop the local Supabase stack |
pnpm db:reset | Re-apply all migrations and seed data locally |
pnpm db:types | Regenerate app/lib/database.types.ts from the local DB |
app/
├── routes/ # File-based routing
│ ├── __root.tsx # Root layout (navbar, theme, toasts)
│ ├── index.tsx # Landing page
│ ├── leaderboard.tsx # Full leaderboard with filters
│ ├── routes.tsx # Route corridor documentation
│ ├── auth/ # OAuth login & callback
│ └── profile/ # Rider profiles ($userId)
├── components/ # Shared components
│ ├── LeaderboardTable.tsx # Virtualized leaderboard
│ ├── LeaderboardColumns.tsx # Column definitions & sub-tables
│ ├── LeaderboardFilters.tsx # Route/time filter chips
│ ├── InteractiveMap.tsx # Leaflet route map
│ ├── GatewayMap.tsx # Gateway checkpoint map
│ ├── ProfileRideStats.tsx # Rider stat cards
│ ├── ProfileRidesTable.tsx # Per-rider ride history
│ ├── CommunityPieCharts.tsx # Route distribution charts
│ ├── GrowthChart.tsx # Cumulative ride growth
│ ├── RideFrequencyChart.tsx # Ride frequency over time
│ ├── RouteSpeedTable.tsx # Speed stats by route
│ ├── WindIndicator.tsx # Tailwind/headwind display
│ ├── FilterChips.tsx # Chip-based filter UI
│ ├── UnitToggle.tsx # Metric/imperial toggle
│ ├── ThemeToggle.tsx # Dark/light mode toggle
│ ├── NavBar.tsx # Navigation bar
│ ├── Toast.tsx # Sonner toast wrapper
│ └── DevToolsPanel.tsx # Admin/dev tools panel
├── lib/ # Utilities, types, classifiers
│ ├── route-classifier.ts # GPS gateway route classification
│ ├── destination-classifier.ts # Office/destination detection
│ ├── office-locations.ts # Known office coordinates
│ ├── constants.ts # Gateways, colors, labels
│ ├── database.types.ts # Auto-generated Supabase types
│ ├── session.ts # Cookie session management
│ ├── strava-oauth.ts # Strava OAuth flow
│ ├── strava.ts # Strava API helpers
│ ├── wind.ts # Wind calculation utilities
│ ├── rate-limiter.ts # API rate limiting
│ ├── analytics.ts # PostHog analytics
│ ├── supabase.ts # Supabase client factory
│ └── useUnit.ts # Unit preference hook
├── queries/ # TanStack Query option factories
│ ├── leaderboard.ts
│ ├── rides.ts
│ └── user.ts
├── server/ # Server-side logic (createServerFn)
│ ├── auth.ts # OAuth login/logout/callback
│ ├── sync.ts # Strava ride sync
│ ├── leaderboard.ts # Leaderboard queries
│ ├── rides.ts # Ride CRUD operations
│ ├── reclassify.ts # Bulk route reclassification
│ ├── wind-enrichment.ts # Wind data enrichment
│ ├── weather.ts # Weather API integration
│ ├── cron.ts # Maintenance + admin "run one tick"
│ ├── sync-drain.ts # Sync job queue drain (Cron Trigger)
│ └── users.ts # User management
└── styles/ # CSS files
├── global.css # Theme variables, base styles
├── components.css # Component-specific styles
├── leaderboard.css # Leaderboard-specific styles
├── maps.css # Map component styles
├── routes.css # Routes page styles
└── wind.css # Wind indicator styles
Routes are classified using GPS gateway checkpoints — specific lat/lng coordinates that a ride's decoded polyline must pass through. This avoids dependency on Strava segment IDs and works reliably across different GPS devices.
| Route | Description | Gateway North | Gateway South |
|---|---|---|---|
| Bayway | Bay shore / Foster City | (37.684, -122.390) | (37.579, -122.311) |
| Skyline | Mountain ridge / Skyline Blvd | (37.668, -122.485) | (37.489, -122.320) |
| HMBW | Coastal / Devil's Slide | (37.578, -122.512) | (37.449, -122.429) |
| Royale | El Camino Real corridor | (37.611, -122.403) | (37.521, -122.277) |
| Fleaway | Flat El Camino / Bayshore | (37.624, -122.408) | (37.492, -122.266) |
| MEBW | Middle East Bay | (37.683, -122.178) | (37.509, -122.114) |
| FEBW | Far East Bay | (37.814, -122.144) | (37.669, -122.001) |
Start point: Peet's Coffee, PPR (37.773, -122.439) Intercept: JD/Skyline (37.698, -122.495) Gateway radius: 500 meters
Browser ──► Cloudflare Worker (TanStack Start, Free plan: 10 ms CPU, 50 subrequests)
│ every page = one SQL function call (get_leaderboard, get_rides, …)
▼
Supabase Postgres ◄── sync_jobs queue ◄── Strava webhook (records only)
▲ │
└── Cron Trigger */5 ────┘ drains the queue in budget-sized steps
app/lib/table-query.ts) and every table renders through
app/components/table/DataTable.tsx.wrangler.jsonc)
drains sync_jobs every 5 minutes; a 12-hour sweep catches anything a
webhook missed. See docs/ops/sync.md.app/lib/dates.ts).Copy .env.example to .env.local (local values) — production values are
Cloudflare secrets/vars.
| Variable | Description |
|---|---|
SUPABASE_URL, SUPABASE_ANON_KEY | Supabase project (local: pnpm db:start) |
SUPABASE_SERVICE_ROLE_KEY | Server only — sync, queue, admin |
STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET | Strava API app |
STRAVA_WEBHOOK_VERIFY_TOKEN | Echoed back when Strava verifies the webhook |
STRAVA_WEBHOOK_SUBSCRIPTION_ID | Optional — ignore events for other subscriptions |
SESSION_SECRET | ≥ 32 chars, signs session cookies |
ADMIN_USER_IDS | Comma-separated user ids allowed to use admin actions |
CRON_SECRET | Bearer token for POST /api/cron |
APP_URL | Public app URL |
VITE_MAP_TILE_URL, VITE_MAP_TILE_URL_DARK, VITE_MAP_TILE_ATTRIBUTION | Optional map tile provider (default: OpenStreetMap) |
Private — not yet licensed for public use.