Web apps

Pottery course site with lifetime video and PDF access

Beginner-friendly course site where students pay once to permanently unlock pottery videos and PDF handouts, behind login and private file storage.

nextjstypescriptsupabasestripepostgresvitest

Built 1 time, downloaded 0 times.

Install from your terminal

Run this in your project folder. It signs you in, shows what changes and asks before it overwrites a file.

npx claude-lab add pottery-course-site-with-lifetime-video-and-pdf-access-65fd19
CLAUDE.md
# Pottery course site

## Purpose

This is a paid, one-time-purchase course site for pottery classes. Students pay once through Stripe and keep lifetime access to the course videos and PDF handouts. The owner is the only admin. Read SPEC.md for what to build and PLAN.md for the current phase.

## Stack

- Next.js with the App Router and TypeScript
- Supabase for login, Postgres and private file storage
- Stripe Checkout for the one-time payment
- Mux or Bunny Stream for video

These are defaults. The owner may change them in SPEC.md before Phase 1 starts. If SPEC.md names a different tool, follow SPEC.md and tell the owner.

## Commands

- `npm run dev` starts the local site at http://localhost:3000
- `npm run lint` checks code style
- `npm run typecheck` checks TypeScript types
- `npm run build` makes a production build
- `npm test` runs the Vitest tests
- `npm run verify` runs all of the above

Run `npm run verify` before saying any task is done. If it fails, fix the first error and run it again.

## Secrets and keys

- Never commit `.env` files or any real key.
- Use obvious placeholders in examples, such as YOUR_STRIPE_KEY or YOUR_SUPABASE_URL.
- The Supabase service-role key stays on the server. Never import it into client components or send it to the browser.
- Real values belong in `.env.local`, which the owner creates. Do not ask the owner to paste keys into chat.

## Access rules

- A lesson video or PDF may only be served after checking for a matching row in `purchases` for the signed-in user.
- Never expose raw storage URLs, storage paths or video IDs to people who have not bought the course.
- Access checks run on the server, on every lesson and PDF route.
- Denied requests return 403.

## Stripe rules

- Grant access only from the verified webhook event `checkout.session.completed`.
- Never grant access from the success redirect page, because the URL can be visited without paying.
- Verify the webhook signature before doing anything with the event.
- Handle repeated webhook events safely. The same event may arrive more than once.
- Keep Stripe in test mode until Phase 5 is finished.

## Code conventions

- Use server components by default. Add client components only for interactive parts.
- Keep functions small and named for what they do.
- Put each feature in its own folder under `src/features/`.
- Use a database row-level security policy on every new table.
- Store PDFs only in the private storage bucket, never in a public one.

## Working style

- The owner is new to coding. After each change, explain what you did in two or three plain sentences.
- Ask before adding any new dependency. Name the package and say why it is needed.
- Ask before changing `.claude/settings.json` or any hooks.
- Mark finished checklist items in PLAN.md as you complete them.

## Review

@REVIEW.md

Use the same checklist for code review and before merging any change. Anything that fails `npm run verify` is a blocker.

## Gotchas

- Supabase row-level security blocks reads by default. If a query returns nothing, check the policy before changing code.
- Stripe test and live keys look alike. Confirm which mode `.env.local` uses before running a purchase test.
- Video IDs are not secret, but they must never reach a visitor who has not paid. Serve playback through a signed, short-lived URL.
Use for my project