Release: auth system, admin RBAC, and production Supabase setup #70
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
# nuzlocke-tracker-1y09
|
# nuzlocke-tracker-1y09
|
||||||
title: Enforce feature branch workflow for agents
|
title: Enforce feature branch workflow for agents
|
||||||
status: todo
|
status: completed
|
||||||
type: task
|
type: task
|
||||||
priority: high
|
priority: high
|
||||||
created_at: 2026-03-20T20:48:21Z
|
created_at: 2026-03-20T20:48:21Z
|
||||||
updated_at: 2026-03-20T20:59:21Z
|
updated_at: 2026-03-20T21:01:47Z
|
||||||
---
|
---
|
||||||
|
|
||||||
## Problem
|
## Problem
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
---
|
---
|
||||||
# nuzlocke-tracker-he1n
|
# nuzlocke-tracker-he1n
|
||||||
title: Add local GoTrue container for dev auth testing
|
title: Add local GoTrue container for dev auth testing
|
||||||
status: todo
|
status: in-progress
|
||||||
type: feature
|
type: feature
|
||||||
|
priority: normal
|
||||||
created_at: 2026-03-20T20:57:04Z
|
created_at: 2026-03-20T20:57:04Z
|
||||||
updated_at: 2026-03-20T20:57:04Z
|
updated_at: 2026-03-20T21:08:22Z
|
||||||
---
|
---
|
||||||
|
|
||||||
## Problem
|
## Problem
|
||||||
@@ -34,17 +35,17 @@ GoTrue will use the existing `db` PostgreSQL container, creating its own `auth`
|
|||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
- [ ] Research GoTrue Docker image and required env vars (JWT secret, DB connection, SMTP disabled, etc.)
|
- [x] 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
|
- [x] Add `gotrue` service to `docker-compose.yml` using the existing `db` container
|
||||||
- [ ] Configure GoTrue to use the same PostgreSQL with its own `auth` schema
|
- [x] 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
|
- [x] 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)
|
- [x] 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)
|
- [x] 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] Verify backend JWT verification works with GoTrue-issued tokens (same HS256 + shared secret)
|
||||||
- [ ] Test email/password signup and login flow end-to-end locally
|
- [ ] 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)
|
- [x] Verify OAuth buttons gracefully handle missing providers in local dev (show disabled state or helpful message)
|
||||||
- [ ] Update `docker-compose.yml` healthcheck for GoTrue readiness
|
- [x] Update `docker-compose.yml` healthcheck for GoTrue readiness
|
||||||
- [ ] Document the local auth setup in README or contributing guide
|
- [x] Document the local auth setup in README or contributing guide
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,17 @@
|
|||||||
],
|
],
|
||||||
"PreCompact": [
|
"PreCompact": [
|
||||||
{ "hooks": [{ "type": "command", "command": "beans prime" }] }
|
{ "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/<name>\"; exit 2; fi; fi'"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
19
.env.example
19
.env.example
@@ -3,11 +3,20 @@ DEBUG=true
|
|||||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/nuzlocke
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/nuzlocke
|
||||||
|
|
||||||
# Supabase Auth (backend)
|
# Supabase Auth (backend)
|
||||||
SUPABASE_URL=https://your-project.supabase.co
|
# For local dev with GoTrue container:
|
||||||
SUPABASE_ANON_KEY=your-anon-key
|
SUPABASE_URL=http://localhost:9999
|
||||||
SUPABASE_JWT_SECRET=your-jwt-secret
|
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)
|
# Frontend settings (used by Vite)
|
||||||
VITE_API_URL=http://localhost:8000
|
VITE_API_URL=http://localhost:8000
|
||||||
VITE_SUPABASE_URL=https://your-project.supabase.co
|
# For local dev with GoTrue container:
|
||||||
VITE_SUPABASE_ANON_KEY=your-anon-key
|
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
|
||||||
|
|||||||
13
CLAUDE.md
13
CLAUDE.md
@@ -1,12 +1,11 @@
|
|||||||
# Branching Strategy
|
# Branching Strategy
|
||||||
|
|
||||||
- **Never commit directly to `main`.** `main` is always production-ready.
|
- **Never commit directly to `develop` or `main`.** Always create a `feature/*` branch first.
|
||||||
- Day-to-day work happens on `develop`.
|
- When starting an **epic**, create `feature/<epic-title-slug>` off `develop`
|
||||||
- New work is done on `feature/*` branches off `develop`.
|
- When starting a **standalone task/bug** (no parent epic), create `feature/<task-title-slug>` off `develop`
|
||||||
- Merge flow: `feature/*` → `develop` → `main`.
|
- Each task within an epic gets its own commit(s) on the epic's feature branch
|
||||||
- **Squash merge** `feature/*` into `develop` (one clean commit per feature).
|
- Branch naming: use a kebab-case slug of the bean title (e.g., `feature/add-auth-system`)
|
||||||
- **Merge commit** `develop` into `main` (marks deploy points).
|
- When the epic/task is complete, squash merge into `develop`
|
||||||
- Always `git pull` the target branch before merging into it.
|
|
||||||
|
|
||||||
# Pre-commit Hooks
|
# Pre-commit Hooks
|
||||||
|
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -14,15 +14,29 @@ A full-stack Nuzlocke run tracker for Pokemon games.
|
|||||||
docker compose up
|
docker compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
This starts three services:
|
This starts four services:
|
||||||
|
|
||||||
| Service | URL |
|
| Service | URL |
|
||||||
|------------|--------------------------|
|
|------------|---------------------------|
|
||||||
| Frontend | http://localhost:5173 |
|
| Frontend | http://localhost:5173 |
|
||||||
| API | http://localhost:8000 |
|
| API | http://localhost:8080 |
|
||||||
| API Docs | http://localhost:8000/docs|
|
| API Docs | http://localhost:8080/docs|
|
||||||
|
| GoTrue | http://localhost:9999 |
|
||||||
| PostgreSQL | localhost:5432 |
|
| 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
|
### Run Migrations
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -12,9 +12,14 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- DEBUG=true
|
- DEBUG=true
|
||||||
- DATABASE_URL=postgresql://postgres:postgres@db:5432/nuzlocke
|
- 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:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
gotrue:
|
||||||
|
condition: service_healthy
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
@@ -29,8 +34,12 @@ services:
|
|||||||
- ./frontend/index.html:/app/index.html:cached
|
- ./frontend/index.html:/app/index.html:cached
|
||||||
environment:
|
environment:
|
||||||
- VITE_API_URL=http://localhost:8080
|
- 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:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
|
- gotrue
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
db:
|
db:
|
||||||
@@ -43,6 +52,7 @@ services:
|
|||||||
- POSTGRES_DB=nuzlocke
|
- POSTGRES_DB=nuzlocke
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./docker/init:/docker-entrypoint-initdb.d:ro
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
@@ -50,5 +60,43 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
restart: unless-stopped
|
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:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ const supabaseAnonKey = import.meta.env['VITE_SUPABASE_ANON_KEY'] ?? ''
|
|||||||
function createSupabaseClient(): SupabaseClient {
|
function createSupabaseClient(): SupabaseClient {
|
||||||
if (!supabaseUrl || !supabaseAnonKey) {
|
if (!supabaseUrl || !supabaseAnonKey) {
|
||||||
// Return a stub client for tests/dev without Supabase configured
|
// 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)
|
return createClient(supabaseUrl, supabaseAnonKey)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { useState } from 'react'
|
|||||||
import { Link, useNavigate, useLocation } from 'react-router-dom'
|
import { Link, useNavigate, useLocation } from 'react-router-dom'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
|
||||||
|
const isLocalDev = import.meta.env['VITE_SUPABASE_URL']?.includes('localhost') ?? false
|
||||||
|
|
||||||
export function Login() {
|
export function Login() {
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
const [password, setPassword] = useState('')
|
const [password, setPassword] = useState('')
|
||||||
@@ -108,7 +110,9 @@ export function Login() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleGoogleLogin}
|
onClick={handleGoogleLogin}
|
||||||
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors"
|
disabled={isLocalDev}
|
||||||
|
title={isLocalDev ? 'OAuth not available in local dev' : undefined}
|
||||||
|
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-surface-2"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
@@ -133,7 +137,9 @@ export function Login() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleDiscordLogin}
|
onClick={handleDiscordLogin}
|
||||||
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors"
|
disabled={isLocalDev}
|
||||||
|
title={isLocalDev ? 'OAuth not available in local dev' : undefined}
|
||||||
|
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-surface-2"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
||||||
@@ -141,6 +147,11 @@ export function Login() {
|
|||||||
Discord
|
Discord
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{isLocalDev && (
|
||||||
|
<p className="text-center text-xs text-text-tertiary">
|
||||||
|
OAuth providers are not available in local development. Use email/password instead.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
<p className="text-center text-sm text-text-secondary">
|
<p className="text-center text-sm text-text-secondary">
|
||||||
Don't have an account?{' '}
|
Don't have an account?{' '}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { useState } from 'react'
|
|||||||
import { Link, useNavigate } from 'react-router-dom'
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
|
||||||
|
const isLocalDev = import.meta.env['VITE_SUPABASE_URL']?.includes('localhost') ?? false
|
||||||
|
|
||||||
export function Signup() {
|
export function Signup() {
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
const [password, setPassword] = useState('')
|
const [password, setPassword] = useState('')
|
||||||
@@ -172,7 +174,9 @@ export function Signup() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleGoogleSignup}
|
onClick={handleGoogleSignup}
|
||||||
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors"
|
disabled={isLocalDev}
|
||||||
|
title={isLocalDev ? 'OAuth not available in local dev' : undefined}
|
||||||
|
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-surface-2"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
@@ -197,7 +201,9 @@ export function Signup() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleDiscordSignup}
|
onClick={handleDiscordSignup}
|
||||||
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors"
|
disabled={isLocalDev}
|
||||||
|
title={isLocalDev ? 'OAuth not available in local dev' : undefined}
|
||||||
|
className="flex items-center justify-center gap-2 py-2 px-4 bg-surface-2 hover:bg-surface-3 border border-border-default rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-surface-2"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
|
||||||
@@ -205,6 +211,11 @@ export function Signup() {
|
|||||||
Discord
|
Discord
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{isLocalDev && (
|
||||||
|
<p className="text-center text-xs text-text-tertiary">
|
||||||
|
OAuth providers are not available in local development. Use email/password instead.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
<p className="text-center text-sm text-text-secondary">
|
<p className="text-center text-sm text-text-secondary">
|
||||||
Already have an account?{' '}
|
Already have an account?{' '}
|
||||||
|
|||||||
Reference in New Issue
Block a user