- Add AUTH_URL env var to docker-compose.yml and .env/.env.example so
NextAuth builds correct redirect URLs instead of falling back to the
Docker bind address (0.0.0.0:3000)
- Normalize email to lowercase in authorize() to match how the register
route stores emails, preventing case-sensitive lookup mismatches
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Downloads and integrates favicon assets (SVG, PNG, ICO, Apple touch icon,
web app manifest icons) and wires them up via Next.js metadata API in the
root layout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The root src/app/page.tsx was redirecting all visitors to /login,
preventing the (public)/page.tsx landing page from rendering at /.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Next.js 16 renamed middleware to proxy. Merged session-based auth and
API key auth into a single proxy.ts. Also fixed: auth route handler
exports, missing card component, Button asChild type errors, signIn
return type, Drizzle eq() type narrowing, and useSearchParams suspense
boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add python-dotenv loading in main.py so DATABASE_URL is read from .env
before db.py module initializes
- Add MLFLOW_TRACKING_URI to .env.example pointing to PostgreSQL
- Add python-dotenv>=1.0.0 to pyproject.toml dependencies
- Initialize MLflow schema in candle_annotator PostgreSQL database
MLflow server now starts without filesystem deprecation warnings and
with full job execution support.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Change AUTH_SECRET generation command from 'openssl rand -hex 32' to 'openssl rand -base64 32' for consistency with documentation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Document Auth.js v5 multi-user authentication with Credentials and Google OAuth
- Add new routing structure: (public) for /, /login, /register and /app/* protected routes
- Document middleware-based route protection in middleware.ts
- Add new API auth endpoints: /api/auth/register, /profile, /password, /account
- Document user_id foreign keys on all data tables with composite unique constraints
- Add settings page at /app/settings for user profile management
- Update API endpoints section to show auth endpoints and protected data endpoints
- Update file structure to reflect new auth files and route groups
- Update constraints to note authentication requirement and JWT session management
- Update version history to reflect v3.2.0 changes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add new 'User Accounts & Authentication' section describing:
- Multi-user support with per-user data isolation
- Auth.js v5 with credentials and Google OAuth sign-in
- Registration with email validation and password requirements
- Settings page for profile management and account deletion
- Default admin account seeding
Remove outdated single-user limitation from Known Limitations section.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add user_id column to TrainingRun model in db.py
- Store user_id on TrainingRun insert in /training/start
- Filter GET /training/runs by user_id (returns empty list if no user context)
- Enforce user ownership on GET /training/runs/{run_id} (404 on mismatch)
- Enforce user ownership on DELETE /training/runs/{run_id} (404 on mismatch)
- Add migration 002 to add user_id column and index to training_runs table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Updated FastAPI /training/start endpoint to extract X-User-ID header via get_user_id() dependency
- Modified _run_training_background to accept and use user_id parameter
- Added MLflow experiment setup with user scoping: experiments are named user_{user_id}_training when user_id is provided, falling back to default experiment name otherwise
- Updated database record insertion to store scoped experiment name
- Updated training/train.py train() function to accept user_id parameter and use it for experiment naming
- Mark task 14.2 as complete in tasks.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Create a get_user_id() dependency that extracts the X-User-ID header
from incoming requests, making it available to route handlers.
The dependency is optional (not enforced) — callers decide whether
to use it or require it on specific routes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The signOut() function in src/components/user-menu.tsx is already correctly
configured with callbackUrl: '/login' to destroy the session and redirect
to the login page. Verified implementation is complete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added a Settings icon button next to the theme toggle in the sidebar header
that links to /app/settings. This provides quick access to user settings
from the main workspace.
- Import Link from next/link in app/page.tsx
- Add Settings icon with link to /app/settings in sidebar header
- Mark task 13.2 as complete in tasks.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Install @radix-ui/react-dropdown-menu and add shadcn/ui dropdown-menu component
- Create src/components/user-menu.tsx: avatar button showing user initial,
DropdownMenu with name/email label, Settings link (/app/settings), Sign Out
(calls signOut with redirect to /login)
- Replace placeholder div in src/app/app/layout.tsx with <UserMenu />
- Remove now-redundant standalone Settings link from nav bar (Settings is in dropdown)
- Mark task 13.1 done in openspec/changes/user-accounts/tasks.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added ChevronLeft icon from lucide-react
- Added Link import from next/link
- Created back navigation component at top of settings page with hover state
- Links back to /app with "← Back" text
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a destructively-styled Danger Zone card to src/app/app/settings/page.tsx.
Clicking "Delete Account" opens a shadcn/ui Dialog that warns the user the
action is irreversible, requires typing "DELETE" to enable the confirm button,
calls DELETE /api/auth/account on confirmation, then signs the user out and
redirects to "/". Marks task 12.3 complete in tasks.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add GET /api/auth/profile endpoint to expose user provider info
- Settings page fetches provider on load to detect credentials vs OAuth
- Credentials users: change password form (current/new/confirm) calling PUT /api/auth/password
- OAuth (Google) users: "Signed in via Google — password cannot be changed" message
- Client-side validation: min 8 chars, passwords-must-match before API call
- Success and error feedback displayed inline in the Security card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Create src/app/app/settings/page.tsx as a client component with a
Profile section: display name input pre-filled from session with a
Save button (PUT /api/auth/profile), read-only email field, and
success/error feedback. Mark task 12.1 done in tasks.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Google OAuth button matching login page style
- Add divider with 'or' text between form and OAuth button
- Button calls signIn('google') with redirect to /app
- Matches task 11.3 requirements
Verified that the register page at src/app/(public)/register/page.tsx already has proper error state handling:
- Duplicate email error (409): "An account with this email already exists"
- Short password error (400): "Password must be at least 8 characters"
- Errors displayed in a clear alert box with icon and destructive styling
- API endpoint returns correct error messages with proper HTTP status codes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Install sonner for toast notifications
- Add handleForgotPassword function that shows "Not yet available" toast
- Add "Forgot password?" link next to password label
- Verify "Sign up" link to /register exists
- Add Toaster component to public layout
- Mark task 10.3 as [x] in tasks.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Capture error from signIn() response (checks for ok: false)
- Check for ?error= in URL search params (Auth.js redirects with error on failure)
- Display red alert box with error message below the form when auth fails
- Clear error on new login attempt
Completes task 10.2 in user-accounts change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Create src/app/(public)/login/page.tsx as a client component matching
the Lovable design: email/password form calling signIn("credentials"),
"Continue with Google" button calling signIn("google"), and a link to
the register page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract navbar into separate client component (navbar.tsx) with useSession hook
- When authenticated: show "Go to App" button linking to /app
- When unauthenticated: show "Log in" and "Get Started" buttons
- Add SessionProvider to public layout to enable auth hooks
- Create session-provider wrapper component to separate concerns (metadata exports still work)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the public landing page matching the Lovable design mockup:
- Sticky navbar with CandleAnnotator logo, Log in / Get Started links
- Hero section with gradient background, headline, and CTA buttons
- Features grid with 6 cards (Precision Annotation, ML Training Pipeline,
Real-Time Predictions, Multi-Chart Workspace, Keyboard-First Workflow,
Export & Persist) using lucide-react icons
- Stats bar showing 50ms render latency, 6 shortcut keys, JSON export
- Footer CTA section with Create Free Account button
- Minimal footer with logo and copyright
Uses Tailwind CSS and shadcn/ui Button component, consistent with project stack.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Changed href="/annotation-types" to href="/app/annotation-types"
- Changed href="/span-label-types" to href="/app/span-label-types"
These links are in the settings menu of the main app workspace
and should navigate within the protected /app route group.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Protected app layout wrapping all /app/* pages with SessionProvider from
next-auth/react. Includes a minimal fixed nav bar with an app title link,
a settings link to /app/settings, and a placeholder slot for the user menu
dropdown (to be implemented in task 13.1).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create src/app/app/ directory
- Move the main app component (dashboard) to src/app/app/page.tsx
- Replace src/app/page.tsx with a redirect to /login
- Update tasks.md to mark 8.2 as complete
This establishes the /app workspace route for the authenticated app while the root / will redirect to login.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create src/app/(public)/layout.tsx as route group for public pages
- Inherits font variables and theme from root layout via middleware
- No sidebar, nav bar, or protected components in public layout
- Marked task 8.1 complete in tasks.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add X-User-ID header containing user.id to all fetch calls from proxy routes
- Updated routes: /api/predict, /api/predict/batch, /api/model/info, /api/model/load, /api/patterns/detect, /api/patterns/available, /api/training/start, /api/training/runs
- Enables user scoping on the FastAPI ML service side
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Every data API route now filters SELECT, INSERT, UPDATE, and DELETE
queries by the authenticated user's ID, ensuring full multi-tenant
data isolation. Candle queries are scoped via chart_id ownership.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>