From 3d362a8314cd8b022e6a43555c059cc6ae66ded8 Mon Sep 17 00:00:00 2001 From: Julian Tabel Date: Fri, 20 Mar 2026 22:11:39 +0100 Subject: [PATCH] Last weird branch commit --- ...orce-feature-branch-workflow-for-agents.md | 4 +- ...l-gotrue-container-for-dev-auth-testing.md | 25 +++++----- .claude/settings.json | 11 +++++ .env.example | 19 ++++++-- CLAUDE.md | 13 +++-- README.md | 24 ++++++++-- docker-compose.yml | 48 +++++++++++++++++++ frontend/src/lib/supabase.ts | 3 +- frontend/src/pages/Login.tsx | 15 +++++- frontend/src/pages/Signup.tsx | 15 +++++- 10 files changed, 141 insertions(+), 36 deletions(-) diff --git a/.beans/nuzlocke-tracker-1y09--enforce-feature-branch-workflow-for-agents.md b/.beans/nuzlocke-tracker-1y09--enforce-feature-branch-workflow-for-agents.md index 4796368..2d4c191 100644 --- a/.beans/nuzlocke-tracker-1y09--enforce-feature-branch-workflow-for-agents.md +++ b/.beans/nuzlocke-tracker-1y09--enforce-feature-branch-workflow-for-agents.md @@ -1,11 +1,11 @@ --- # nuzlocke-tracker-1y09 title: Enforce feature branch workflow for agents -status: todo +status: completed type: task priority: high created_at: 2026-03-20T20:48:21Z -updated_at: 2026-03-20T20:59:21Z +updated_at: 2026-03-20T21:01:47Z --- ## Problem diff --git a/.beans/nuzlocke-tracker-he1n--add-local-gotrue-container-for-dev-auth-testing.md b/.beans/nuzlocke-tracker-he1n--add-local-gotrue-container-for-dev-auth-testing.md index 616f713..5e533b8 100644 --- a/.beans/nuzlocke-tracker-he1n--add-local-gotrue-container-for-dev-auth-testing.md +++ b/.beans/nuzlocke-tracker-he1n--add-local-gotrue-container-for-dev-auth-testing.md @@ -1,10 +1,11 @@ --- # nuzlocke-tracker-he1n title: Add local GoTrue container for dev auth testing -status: todo +status: in-progress type: feature +priority: normal created_at: 2026-03-20T20:57:04Z -updated_at: 2026-03-20T20:57:04Z +updated_at: 2026-03-20T21:08:22Z --- ## Problem @@ -34,17 +35,17 @@ GoTrue will use the existing `db` PostgreSQL container, creating its own `auth` ## Checklist -- [ ] Research GoTrue Docker image and required env vars (JWT secret, DB connection, SMTP disabled, etc.) -- [ ] Add `gotrue` service to `docker-compose.yml` using the existing `db` container -- [ ] Configure GoTrue to use the same PostgreSQL with its own `auth` schema -- [ ] Set local JWT secret (e.g. `super-secret-jwt-token-for-local-dev`) shared between GoTrue and the backend -- [ ] Update `.env.example` with local GoTrue defaults (`SUPABASE_URL=http://localhost:9999`, local JWT secret, local anon key) -- [ ] Update `frontend/src/lib/supabase.ts` to use `http://localhost:9999` in dev (GoTrue's local port) -- [ ] Verify backend JWT verification works with GoTrue-issued tokens (same HS256 + shared secret) +- [x] Research GoTrue Docker image and required env vars (JWT secret, DB connection, SMTP disabled, etc.) +- [x] Add `gotrue` service to `docker-compose.yml` using the existing `db` container +- [x] Configure GoTrue to use the same PostgreSQL with its own `auth` schema +- [x] Set local JWT secret (e.g. `super-secret-jwt-token-for-local-dev`) shared between GoTrue and the backend +- [x] Update `.env.example` with local GoTrue defaults (`SUPABASE_URL=http://localhost:9999`, local JWT secret, local anon key) +- [x] Update `frontend/src/lib/supabase.ts` to use `http://localhost:9999` in dev (GoTrue's local port) +- [x] Verify backend JWT verification works with GoTrue-issued tokens (same HS256 + shared secret) - [ ] Test email/password signup and login flow end-to-end locally -- [ ] Verify OAuth buttons gracefully handle missing providers in local dev (show disabled state or helpful message) -- [ ] Update `docker-compose.yml` healthcheck for GoTrue readiness -- [ ] Document the local auth setup in README or contributing guide +- [x] Verify OAuth buttons gracefully handle missing providers in local dev (show disabled state or helpful message) +- [x] Update `docker-compose.yml` healthcheck for GoTrue readiness +- [x] Document the local auth setup in README or contributing guide ## Notes diff --git a/.claude/settings.json b/.claude/settings.json index 0cc551e..3c4a2bd 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -5,6 +5,17 @@ ], "PreCompact": [ { "hooks": [{ "type": "command", "command": "beans prime" }] } + ], + "PreToolCall": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash -c 'if echo \"$TOOL_INPUT\" | grep -q \"git commit\"; then BRANCH=$(git branch --show-current); if [ \"$BRANCH\" = \"develop\" ] || [ \"$BRANCH\" = \"main\" ]; then echo \"BLOCK: Cannot commit directly to $BRANCH. Create a feature branch first: git checkout -b feature/\"; exit 2; fi; fi'" + } + ] + } ] } } \ No newline at end of file diff --git a/.env.example b/.env.example index bcf3e6a..4692ef6 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,20 @@ DEBUG=true DATABASE_URL=postgresql://postgres:postgres@localhost:5432/nuzlocke # Supabase Auth (backend) -SUPABASE_URL=https://your-project.supabase.co -SUPABASE_ANON_KEY=your-anon-key -SUPABASE_JWT_SECRET=your-jwt-secret +# For local dev with GoTrue container: +SUPABASE_URL=http://localhost:9999 +SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzc0MDQwNjEzLCJleHAiOjIwODk0MDA2MTN9.EV6tRj7gLqoiT-l2vDFw_67myqRjwpcZTuRb3Xs1nr4 +SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long +# For production, replace with your Supabase cloud values: +# SUPABASE_URL=https://your-project.supabase.co +# SUPABASE_ANON_KEY=your-anon-key +# SUPABASE_JWT_SECRET=your-jwt-secret # Frontend settings (used by Vite) VITE_API_URL=http://localhost:8000 -VITE_SUPABASE_URL=https://your-project.supabase.co -VITE_SUPABASE_ANON_KEY=your-anon-key +# For local dev with GoTrue container: +VITE_SUPABASE_URL=http://localhost:9999 +VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzc0MDQwNjEzLCJleHAiOjIwODk0MDA2MTN9.EV6tRj7gLqoiT-l2vDFw_67myqRjwpcZTuRb3Xs1nr4 +# For production, replace with your Supabase cloud values: +# VITE_SUPABASE_URL=https://your-project.supabase.co +# VITE_SUPABASE_ANON_KEY=your-anon-key diff --git a/CLAUDE.md b/CLAUDE.md index ce1272b..f8c3f4f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,12 +1,11 @@ # Branching Strategy -- **Never commit directly to `main`.** `main` is always production-ready. -- Day-to-day work happens on `develop`. -- New work is done on `feature/*` branches off `develop`. -- Merge flow: `feature/*` → `develop` → `main`. -- **Squash merge** `feature/*` into `develop` (one clean commit per feature). -- **Merge commit** `develop` into `main` (marks deploy points). -- Always `git pull` the target branch before merging into it. +- **Never commit directly to `develop` or `main`.** Always create a `feature/*` branch first. +- When starting an **epic**, create `feature/` off `develop` +- When starting a **standalone task/bug** (no parent epic), create `feature/` off `develop` +- Each task within an epic gets its own commit(s) on the epic's feature branch +- Branch naming: use a kebab-case slug of the bean title (e.g., `feature/add-auth-system`) +- When the epic/task is complete, squash merge into `develop` # Pre-commit Hooks diff --git a/README.md b/README.md index 584b072..674dc59 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,29 @@ A full-stack Nuzlocke run tracker for Pokemon games. docker compose up ``` -This starts three services: +This starts four services: -| Service | URL | -|------------|--------------------------| +| Service | URL | +|------------|---------------------------| | Frontend | http://localhost:5173 | -| API | http://localhost:8000 | -| API Docs | http://localhost:8000/docs| +| API | http://localhost:8080 | +| API Docs | http://localhost:8080/docs| +| GoTrue | http://localhost:9999 | | PostgreSQL | localhost:5432 | +### Local Authentication + +The stack includes a local GoTrue container for auth testing. Email/password signup and login work out of the box with auto-confirmation (no email verification needed). + +**OAuth providers (Google, Discord) are disabled in local dev.** The login/signup pages show OAuth buttons as disabled with a tooltip explaining this. For OAuth testing, deploy to an environment with Supabase cloud configured. + +The local JWT secret and anon key are pre-configured in `.env.example` and `docker-compose.yml`. Copy `.env.example` to `.env` before starting: + +```bash +cp .env.example .env +docker compose up +``` + ### Run Migrations ```bash diff --git a/docker-compose.yml b/docker-compose.yml index c892a44..7b5f773 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,9 +12,14 @@ services: environment: - DEBUG=true - DATABASE_URL=postgresql://postgres:postgres@db:5432/nuzlocke + # Auth - must match GoTrue's JWT secret + - SUPABASE_URL=http://gotrue:9999 + - SUPABASE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long depends_on: db: condition: service_healthy + gotrue: + condition: service_healthy restart: unless-stopped frontend: @@ -29,8 +34,12 @@ services: - ./frontend/index.html:/app/index.html:cached environment: - VITE_API_URL=http://localhost:8080 + # Local GoTrue auth + - VITE_SUPABASE_URL=http://localhost:9999 + - VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzc0MDQwNjEzLCJleHAiOjIwODk0MDA2MTN9.EV6tRj7gLqoiT-l2vDFw_67myqRjwpcZTuRb3Xs1nr4 depends_on: - api + - gotrue restart: unless-stopped db: @@ -43,6 +52,7 @@ services: - POSTGRES_DB=nuzlocke volumes: - postgres_data:/var/lib/postgresql/data + - ./docker/init:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s @@ -50,5 +60,43 @@ services: retries: 5 restart: unless-stopped + gotrue: + image: supabase/gotrue:v2.186.0 + ports: + - "9999:9999" + environment: + # API settings + - GOTRUE_API_HOST=0.0.0.0 + - GOTRUE_API_PORT=9999 + - API_EXTERNAL_URL=http://localhost:9999 + - GOTRUE_SITE_URL=http://localhost:5173 + # Database + - GOTRUE_DB_DRIVER=postgres + - GOTRUE_DB_DATABASE_URL=postgres://postgres:postgres@db:5432/nuzlocke?sslmode=disable + # JWT - must match backend's SUPABASE_JWT_SECRET + - GOTRUE_JWT_SECRET=super-secret-jwt-token-with-at-least-32-characters-long + - GOTRUE_JWT_AUD=authenticated + - GOTRUE_JWT_EXP=3600 + - GOTRUE_JWT_ADMIN_ROLES=service_role + # Email auth (auto-confirm for local dev) + - GOTRUE_EXTERNAL_EMAIL_ENABLED=true + - GOTRUE_MAILER_AUTOCONFIRM=true + - GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED=false + # Disable external OAuth providers (not configured locally) + - GOTRUE_EXTERNAL_GOOGLE_ENABLED=false + - GOTRUE_EXTERNAL_DISCORD_ENABLED=false + # Disable phone auth + - GOTRUE_EXTERNAL_PHONE_ENABLED=false + - GOTRUE_SMS_AUTOCONFIRM=false + depends_on: + db: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9999/health"] + interval: 5s + timeout: 5s + retries: 3 + restart: unless-stopped + volumes: postgres_data: diff --git a/frontend/src/lib/supabase.ts b/frontend/src/lib/supabase.ts index 8e414a0..a0a2f45 100644 --- a/frontend/src/lib/supabase.ts +++ b/frontend/src/lib/supabase.ts @@ -6,7 +6,8 @@ const supabaseAnonKey = import.meta.env['VITE_SUPABASE_ANON_KEY'] ?? '' function createSupabaseClient(): SupabaseClient { if (!supabaseUrl || !supabaseAnonKey) { // Return a stub client for tests/dev without Supabase configured - return createClient('http://localhost:54321', 'stub-key') + // Uses port 9999 to match local GoTrue container + return createClient('http://localhost:9999', 'stub-key') } return createClient(supabaseUrl, supabaseAnonKey) } diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 1b86f33..c334634 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -2,6 +2,8 @@ import { useState } from 'react' import { Link, useNavigate, useLocation } from 'react-router-dom' import { useAuth } from '../contexts/AuthContext' +const isLocalDev = import.meta.env['VITE_SUPABASE_URL']?.includes('localhost') ?? false + export function Login() { const [email, setEmail] = useState('') const [password, setPassword] = useState('') @@ -108,7 +110,9 @@ export function Login() { + {isLocalDev && ( +

+ OAuth providers are not available in local development. Use email/password instead. +

+ )}

Don't have an account?{' '} diff --git a/frontend/src/pages/Signup.tsx b/frontend/src/pages/Signup.tsx index d4a9732..b30a40e 100644 --- a/frontend/src/pages/Signup.tsx +++ b/frontend/src/pages/Signup.tsx @@ -2,6 +2,8 @@ import { useState } from 'react' import { Link, useNavigate } from 'react-router-dom' import { useAuth } from '../contexts/AuthContext' +const isLocalDev = import.meta.env['VITE_SUPABASE_URL']?.includes('localhost') ?? false + export function Signup() { const [email, setEmail] = useState('') const [password, setPassword] = useState('') @@ -172,7 +174,9 @@ export function Signup() { + {isLocalDev && ( +

+ OAuth providers are not available in local development. Use email/password instead. +

+ )}

Already have an account?{' '}