OpenTrailPaper
Open-source bike computer firmware for the LilyGO T5S3 4.7" E-Paper PRO, with offline maps, GPX routes, FIT recording and Bluetooth sensors.
Log your golf round hole by hole, see what cost you strokes, and take a practice plan to the range.
Sports & fitnessTypeScript · CSSGitHub @apham799OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Log a round hole by hole at a real Chicago course, see what cost you strokes, and get a practice plan that knows the weather.
Design, Build, Ship (MPCS 51238), Assignment 4. This extends my Assignment 3 tool from a one-device prototype (localStorage) into a multi-user app: classmates create an account, their rounds and practice are saved to a database, and each person sees only their own.
PROGRESS.md · Agent context: AGENTS.md · Flows (from A3): flows.mdflowchart LR
B["Browser<br/>(Next.js app)"]
C["Clerk<br/>sign-in, session token"]
S[("Supabase Postgres<br/>row-level security")]
W["/api/weather<br/>(our server, cached 15 min)"]
O["Open-Meteo<br/>forecast API"]
B -- "sign in / getToken()" --> C
B -- "query + Clerk token" --> S
S -. "verifies the token (third-party auth)<br/>RLS: user_id = token's sub" .-> S
B -- "lat, lon" --> W
W -- "cached per location" --> O
Tracing a save (e.g. finishing a hole):
update() in src/lib/storage.ts).supabase-js. getToken() asks Clerk for the signed-in user's session token, and it goes with the request.user_id must equal the token's sub. The database fills in user_id itself, so the app never sends it.Where the app knows who you are, and where it decides what you see: Clerk knows who you are (the session token, read by src/proxy.ts and useAuth()). Supabase RLS decides what you see. The app never filters by user itself, and there's no secret or service-role key anywhere, so every query runs as you.
Where the API is called, and why: Open-Meteo is called on the server, in src/app/api/weather/route.ts → src/lib/weather-server.ts, never from the browser. It needs no key, so the reasons are:
"use cache" helper keeps each location for 15 minutes, so everyone looking at the same course shares one call. The free tier allows about 10,000 a day. The first call took about 0.74 s; cached ones about 0.03 s.?weather=down to a course page to see that last one.Four tables, each with user_id text default (auth.jwt() ->> 'sub') and row-level security on every table: four policies (select, insert, update, delete) built on one rule, (select auth.jwt() ->> 'sub') = user_id. The anon role has no access.
| Table | Holds |
|---|---|
rounds | One per round: course, tee, holes (JSON), and a weather snapshot at tee time |
practice_sessions | One per session: focus, minutes, feel, drills, challenge score, weather |
custom_courses | Courses you add yourself |
user_settings | One row per person: current focus, weekly goal, hole notes, badges |
The built-in courses, practice facilities and drill videos are shared, read-only reference data, so they ship in the code (src/data/) instead of the database. Schema changes are migrations in supabase/migrations/.
.env.local (git-ignored) and in Vercel's environment variables, never in the code or the repo. .claude/settings.json has a deny rule so the agent can't read .env files.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY). CLERK_SECRET_KEY stays on the server. There is no Supabase secret key at all.Seven Playwright tests (npm run test:e2e) in tests/e2e/. The two test accounts are Clerk +clerk_test users that sign in with one-time tickets, so no passwords are stored anywhere.
| Test | Checks |
|---|---|
two-users.spec.ts | Two golfers each save a course; neither can see the other's |
guest-trial.spec.ts | A guest's trial is saved to the account on sign-in; the banner can be closed |
weather.spec.ts | Weather is saved with a round, and a round still starts when weather is down |
summary-advice.spec.ts | Skipped tee-shot taps aren't counted as missed fairways |
course-search.spec.ts | Typos, word order and nicknames find the right course |
drill-video.spec.ts | Nothing loads from YouTube until you tap "Watch how" |
Watched it fail first. I broke the custom_courses select policy on purpose (using (true)), and the two-user test failed: Expected: 0, Received: 1, because Golfer A's course list showed Golfer B's course. Restoring the real rule made it pass. Both migrations are in the history, and the write-up, with the failing output, is in docs/step7/.
/security-review on the feature branch (51 files): no high- or medium-severity findings. One low-impact note: a guest trial left open in a shared browser tab would be added to whoever signs in next in that same tab.
A UI/UX review of the running app at desktop and phone sizes found, among other things:
All four are fixed, each with a commit.
(Draft: rewrite in my own words before class.)
What I stopped or redirected:
What I caught along the way:
.env.local itself (the brief says to write it by hand). The agent filtered the CLI's output so it never saw a key.npm install
# .env.local (create it yourself):
# NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
# CLERK_SECRET_KEY=sk_test_...
# NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
# NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...
npm run dev # http://localhost:3000
npm run test:e2e # Playwright (uses the dev server)
Everything runs on free plans: Vercel Hobby, Supabase Free, Clerk development instance, Open-Meteo, and no payment methods. Supabase pauses a free project after a week without use, so open the app now and then until grades are back.