Commit graph

187 commits

Author SHA1 Message Date
Marko Djordjevic
33ed7b7cb7 Fix auth redirect to 0.0.0.0 and credentials sign-in error
- 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>
2026-02-20 23:42:24 +01:00
Marko Djordjevic
852d19e3db Add favicon from RealFaviconGenerator
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>
2026-02-20 23:07:10 +01:00
Marko Djordjevic
ea8c86f425 fix annotation links 2026-02-20 22:30:55 +01:00
Marko Djordjevic
54a45146ed Redirect to home page on sign out instead of login page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 22:30:38 +01:00
Marko Djordjevic
173623a432 Move span-label-types and annotation-types links from sidebar settings menu to settings page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 22:26:39 +01:00
Marko Djordjevic
61ad63ce35 Remove root page.tsx redirect so landing page renders at /
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>
2026-02-20 22:13:54 +01:00
Marko Djordjevic
3e242c3359 Migrate middleware.ts to proxy.ts for Next.js 16 and fix build errors
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>
2026-02-20 21:52:38 +01:00
Marko Djordjevic
fe68eca773 Add settings gear icon to sidebar (task 13.2)
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>
2026-02-20 13:42:19 +01:00
Marko Djordjevic
9884f47d5a feat(13.1): add UserMenu component with avatar, Settings link, and Sign Out
- 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>
2026-02-20 13:41:21 +01:00
Marko Djordjevic
fba0b29d64 Add back navigation link to /app on settings page
- 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>
2026-02-20 13:37:48 +01:00
Marko Djordjevic
0d8d8627a2 Add Danger Zone section to settings page with delete account dialog
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>
2026-02-20 13:37:03 +01:00
Marko Djordjevic
64b3bfd0d4 feat(settings): add Security section with change password form (task 12.2)
- 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>
2026-02-20 13:35:19 +01:00
Marko Djordjevic
9514a987e3 Add settings page profile section (task 12.1)
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>
2026-02-20 13:32:39 +01:00
Marko Djordjevic
77327eeb61 Add Continue with Google button to register page
- 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
2026-02-20 13:31:03 +01:00
Marko Djordjevic
50d8c84367 Add register page (task 11.1): name/email/password form with POST to /api/auth/register and auto sign-in
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:29:36 +01:00
Marko Djordjevic
3b03a87c41 Task 10.3: Add "Forgot password?" link with toast and "Sign up" link to login page
- 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>
2026-02-20 13:28:17 +01:00
Marko Djordjevic
42a76ed41b Add error state display for invalid credentials on login page
- 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>
2026-02-20 13:22:51 +01:00
Marko Djordjevic
954dd27c88 Add login page with credentials and Google sign-in (task 10.1)
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>
2026-02-20 13:21:48 +01:00
Marko Djordjevic
d0ebf677f3 Add auth-aware navbar to landing page
- 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>
2026-02-20 13:19:58 +01:00
Marko Djordjevic
09facbce69 Add landing page at src/app/(public)/page.tsx (task 9.1)
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>
2026-02-20 13:18:31 +01:00
Marko Djordjevic
7cd0acc82f Update hardcoded links in app page from / to /app
- 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>
2026-02-20 13:16:45 +01:00
Marko Djordjevic
07efa32cfb Task 8.3: Create src/app/app/layout.tsx with SessionProvider and nav bar
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>
2026-02-20 13:15:41 +01:00
Marko Djordjevic
985f459bd7 Task 8.2: Move src/app/page.tsx to src/app/app/page.tsx
- 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>
2026-02-20 13:14:15 +01:00
Marko Djordjevic
c9dbd4c813 Task 8.1: Create public layout with minimal structure (shared fonts/theme, no sidebar)
- 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>
2026-02-20 13:13:25 +01:00
Marko Djordjevic
bd668589b6 Add X-User-ID header to all FastAPI ML service proxy routes
- 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>
2026-02-20 13:12:25 +01:00
Marko Djordjevic
685639a0d3 Add getAuthUser() auth guard to all ML proxy API routes (task 7.3)
Adds authentication check at the top of each handler in:
- /api/predict
- /api/predict/batch
- /api/model/info
- /api/model/load
- /api/patterns/detect
- /api/patterns/available
- /api/training/start
- /api/training/runs

Returns 401 Unauthorized for unauthenticated requests. Proxy/fetch logic unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:10:23 +01:00
Marko Djordjevic
5f727d84c6 Scope all Drizzle queries by user_id from authenticated session
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>
2026-02-20 13:08:09 +01:00
Marko Djordjevic
9901d0f3f1 Add getAuthUser() auth guard to all data API routes (task 7.1)
Add 401 Unauthorized check at the top of every handler in:
- /api/upload (POST)
- /api/candles (GET)
- /api/charts (GET) and /api/charts/[id] (GET, DELETE)
- /api/annotations (GET, POST, DELETE) and /api/annotations/[id] (PATCH, DELETE)
- /api/annotation-types (GET, POST, DELETE) and /api/annotation-types/[id] (PATCH)
- /api/span-annotations (GET, POST, DELETE), /[id] (PATCH, DELETE), /export (GET)
- /api/span-label-types (GET, POST) and /[id] (PATCH, DELETE)
- /api/export (GET) and /api/export/spans (GET)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:26:09 +01:00
Marko Djordjevic
aa2c5fdb69 Add DELETE /api/auth/account endpoint for full account deletion
Implements task 6.3: deletes all user data in correct FK order
(span_annotations, annotations, candles, charts, span_label_types,
annotation_types) then deletes the user record. Returns 401 if not
authenticated, 200 on success.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:22:43 +01:00
Marko Djordjevic
93f7d20382 Add PUT /api/auth/password endpoint for credential users
Implements task 6.2: verifies current password with bcryptjs, rejects
OAuth users (no password_hash), validates new password (8+ chars),
hashes and persists the new password.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:21:27 +01:00
Marko Djordjevic
c36ab7c146 Implement task 6.1: Create PUT /api/auth/profile endpoint for updating user display name
- Create src/app/api/auth/profile/route.ts with PUT handler
- Validates user is authenticated (returns 401 if not)
- Validates request body has a non-empty name field
- Updates user's name in the database
- Returns 200 with updated user data

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:20:20 +01:00
Marko Djordjevic
d4e92cf88f Add src/lib/auth.ts helper for extracting user from Auth.js session
Implement task 5.2: Create getAuthUser() helper that calls auth() from
src/auth.ts and returns the authenticated user object or null if not
authenticated. Mark task 5.2 complete in tasks.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:19:12 +01:00
Marko Djordjevic
55fd91ff52 Add Next.js middleware for route protection (task 5.1)
- Create src/middleware.ts using Auth.js v5 auth() wrapper
- Protect /app/* routes: redirect unauthenticated users to /login
- Protect /api/* routes (except /api/auth/* and /api/health): return 401 JSON for unauthenticated requests
- Redirect authenticated users away from /login and /register to /app
- Mark task 5.1 as complete in tasks.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:18:15 +01:00
Marko Djordjevic
10e4ec8648 Task 4.2: Add default data seeding on new user creation
Create seedUserDefaults() helper in src/lib/db/seed-user-defaults.ts
that inserts default annotation_types (break_up, break_down, line) and
default span_label_types (bull_flag, bear_flag, etc.) scoped to a
given user_id. Call it from POST /api/auth/register after user insert
and from src/auth.ts Google signIn callback after new Google user creation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:16:55 +01:00
Marko Djordjevic
9a5e325632 feat(auth): add POST /api/auth/register endpoint (task 4.1)
Validates email presence and password length (8+ chars), checks email
uniqueness with 409 on conflict, hashes password with bcryptjs (cost 12),
inserts user into the users table and returns 201 with id/email/name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:14:55 +01:00
Marko Djordjevic
45b6366861 Create nextauth route handler exporting GET/POST from auth.ts (task 3.4) 2026-02-20 10:13:32 +01:00
Marko Djordjevic
a8c88f3ca2 Task 3.3: Handle Google OAuth sign-in callback in src/auth.ts
- Add signIn callback: on Google sign-in, check users table by email;
  create new user (provider='google', provider_account_id, name/image
  from profile) if not found, or allow sign-in for returning users
- Update jwt callback to look up DB uuid by email for Google sign-ins,
  so token.id is always the DB uuid rather than the Google sub ID
- Mark task 3.3 as complete in tasks.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:58:18 +01:00
Marko Djordjevic
50e85e499a Add src/auth.ts with Auth.js v5 config (task 3.1)
JWT session strategy, Credentials provider with bcryptjs password
verification, and Google OAuth provider using AUTH_GOOGLE_ID/SECRET.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:55:04 +01:00
Marko Djordjevic
b633a3f52a Task 2.3: Replace single-column unique constraints with composite (user_id, name) unique indexes
- charts.name → uniqueIndex('charts_user_id_name_unique').on(user_id, name)
- annotation_types.name → uniqueIndex('annotation_types_user_id_name_unique').on(user_id, name)
- span_label_types.name → uniqueIndex('span_label_types_user_id_name_unique').on(user_id, name)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:48:50 +01:00
Marko Djordjevic
633a76876f Add user_id FK column to charts, annotations, annotation_types, span_annotations, span_label_types
Adds nullable uuid user_id column referencing users.id to each of the 5
data tables. Column is nullable for now to allow the data migration
script (task 2.5) to backfill existing rows before enforcing NOT NULL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:47:30 +01:00
Marko Djordjevic
9101cfb7af Add users table to Drizzle schema (task 2.1)
Adds the `users` table to `src/lib/db/schema.ts` with:
- UUID primary key (defaultRandom)
- email (unique, not null)
- password_hash (nullable, for OAuth-only users)
- name, image (nullable)
- provider (default 'credentials'), provider_account_id (nullable)
- email_verified (timestamp, nullable)
- created_at, updated_at

Marks task 2.1 as complete in tasks.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:46:20 +01:00
Marko Djordjevic
328476a581 Fix predict proxy schema and error messages 2026-02-18 23:38:17 +01:00
Marko Djordjevic
508d267078 Handle no-model 503 as online 2026-02-18 23:34:00 +01:00
Marko Djordjevic
07064fbf40 fix(training): use selected chart and include TA-Lib span sources 2026-02-18 23:21:23 +01:00
Marko Djordjevic
3448c6febd fix(auth): allow same-origin browser requests through API middleware 2026-02-18 23:07:42 +01:00
Marko Djordjevic
685b8e9733 fix: rename exported function from middleware to proxy in proxy.ts 2026-02-18 21:16:09 +01:00
Marko Djordjevic
522c2f269d fix: rename middleware.ts to proxy.ts (deprecated convention) 2026-02-18 21:14:42 +01:00
Marko Djordjevic
9d3cf2aeb2 code-review-fix task 12.12: remove dead no-op filter code from page.tsx 2026-02-18 20:44:43 +01:00
Marko Djordjevic
843c867034 code-review-fix task 12.11: remove eslint-disable in TrainingPanel and fix dependency array 2026-02-18 20:43:57 +01:00
Marko Djordjevic
b309956e79 code-review-fix task 12.10: remove unused no-op Tooltip component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 20:43:11 +01:00