Commit graph

348 commits

Author SHA1 Message Date
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
162dfb0882 Mark task 8.4 complete: Update hardcoded links to /app
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:16:49 +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
40afd4111c Mark task 3.2 done: JWT and session callbacks already implemented in src/auth.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:56:48 +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
73f44bf447 Add data migration script for user-accounts (task 2.5)
Create scripts/migrate-users.ts that:
- Creates a default admin user from DEFAULT_ADMIN_EMAIL/DEFAULT_ADMIN_PASSWORD env vars
- Backfills user_id on all existing rows in charts, annotations, annotation_types,
  span_annotations, span_label_types
- Is idempotent (safe to run multiple times)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:51:23 +01:00
Marko Djordjevic
877ae032a1 Mark task 2.4 as complete: Generate Drizzle migration 2026-02-20 09:50:14 +01:00
Marko Djordjevic
f4225d0334 Generate Drizzle migration with user accounts schema changes 2026-02-20 09:50:08 +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
0cd74ebedd Mark task 2.2 complete in user-accounts tasks.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:47:41 +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
d05ed21cd6 Task 1.3: Update docker-compose.yml to pass new auth environment variables
Add AUTH_SECRET, AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_TRUST_HOST,
DEFAULT_ADMIN_EMAIL, and DEFAULT_ADMIN_PASSWORD environment variables to
the candle-annotator service in docker-compose.yml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:45:12 +01:00
Marko Djordjevic
7ba1380cc7 Add authentication environment variables to .env.example
Added AUTH_SECRET, AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_TRUST_HOST, DEFAULT_ADMIN_EMAIL, and DEFAULT_ADMIN_PASSWORD environment variables with helpful comments and placeholder values for Auth.js v5 and Google OAuth setup.

Marked task 1.2 as complete in tasks.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:44:25 +01:00
Marko Djordjevic
973662739a Install authentication dependencies: next-auth@5, bcryptjs, @types/bcryptjs
Add required npm packages for user authentication implementation:
- next-auth@5.0.0-beta.30: Next.js authentication library
- bcryptjs@3.0.3: Password hashing library
- @types/bcryptjs@2.4.6: TypeScript types for bcryptjs

Mark task 1.1 as complete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:43:45 +01:00
Marko Djordjevic
3c39690f4a Assign per-task model tags in user-accounts tasks.md
Replace section-level model tags with individual task-level tags.
Haiku for mechanical/config/boilerplate, sonnet for standard feature
work, opus for 2.5 (data migration backfill) and 7.2 (user_id query
permeation across all routes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 08:59:17 +01:00
Marko Djordjevic
925e7284e3 Archive code-review-fix change and sync specs to main
- Synced 14 capability delta specs to main specs
- Created 6 new main specs: api-authentication, error-boundary, input-validation, security-headers, shared-types
- Updated 8 existing specs with security, validation, and performance requirements
- Archived change to openspec/changes/archive/2026-02-20-code-review-fix/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 08:54:59 +01:00
Marko Djordjevic
adb93a2d2e update gitingore for models dir 2026-02-19 00:01:22 +01:00
Marko Djordjevic
26f2c44509 Fix XGBoost label encoding and single-class guard 2026-02-18 23:58:24 +01:00
Marko Djordjevic
73c10a4156 Fix inference feature mismatch with training metadata 2026-02-18 23:53:38 +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
d6b980a3ca add API KEY var to docker compose 2026-02-18 22:55:46 +01:00
Marko Djordjevic
e446e3e563 Fix ml-service volume permissions 2026-02-18 22:50:36 +01:00
Marko Djordjevic
7af1c75c56 fix deployment folder permisions 2026-02-18 22:50:36 +01:00
Marko Djordjevic
86a8cc66b6 fix deployment folder permisions 2026-02-18 22:34:23 +01:00
Marko Djordjevic
385e7944b7 update github action for deploy 2026-02-18 22:15:00 +01:00
Marko Djordjevic
9ba4736dce fix: install prod node_modules in Docker final stage for migration script 2026-02-18 22:11:51 +01:00
Marko Djordjevic
996caf342f deployment script 2026-02-18 21:49:14 +01:00
Marko Djordjevic
11cf414489 test deployment 2 2026-02-18 21:44:05 +01:00
Marko Djordjevic
16123d385a test deployment 2026-02-18 21:42:42 +01:00