OpenTrailPaper
LilyGO T5S3 4.7" E-Paper PRO용 오픈소스 자전거 컴퓨터 펌웨어, 오프라인 지도·GPX 경로·FIT 기록·블루투스 센서 지원
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
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.