candle-annotator/openspec/changes/user-accounts/specs/postgres-data-layer/spec.md
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

5.6 KiB

ADDED Requirements

Requirement: Users table schema

The Drizzle schema SHALL define a users table with columns: id (uuid, primary key, default gen_random_uuid()), name (text, nullable), email (text, unique, not null), email_verified (timestamp, nullable), password_hash (text, nullable), image (text, nullable), provider (text, not null, default 'credentials'), provider_account_id (text, nullable), created_at (timestamp, not null, default now), updated_at (timestamp, not null, default now).

Scenario: Users table created

  • WHEN the schema is loaded and migrations are applied
  • THEN the users table exists with all specified columns and the email unique constraint

Scenario: UUID primary key generation

  • WHEN a new user is inserted without specifying an ID
  • THEN a UUID is automatically generated via gen_random_uuid()

MODIFIED Requirements

Requirement: PostgreSQL schema definitions

The Drizzle schema SHALL define all frontend tables using pgTable from drizzle-orm/pg-core. The following tables SHALL be defined: users, charts, candles, annotation_types, annotations, span_label_types, span_annotations. All tables except users and candles SHALL include a user_id column (uuid, foreign key to users.id, not null). The candles table inherits user scope through its chart_id foreign key to charts.

Scenario: Charts table schema

  • WHEN the schema is loaded
  • THEN the charts table has columns: id (serial, primary key), name (text, not null), user_id (uuid, foreign key to users.id, not null), created_at (timestamp, not null, default now), with a unique index on (user_id, name)

Scenario: Candles table schema

  • WHEN the schema is loaded
  • THEN the candles table has columns: id (serial, primary key), chart_id (integer, foreign key to charts.id, not null), time (timestamp, not null), open (double precision, not null), high (double precision, not null), low (double precision, not null), close (double precision, not null), with a unique index on (chart_id, time)

Scenario: Annotation types table schema

  • WHEN the schema is loaded
  • THEN the annotation_types table has columns: id (serial, primary key), name (text, not null), display_name (text, not null), color (text, not null), category (text, not null), icon (text, nullable), is_active (boolean, not null, default true), user_id (uuid, foreign key to users.id, not null), created_at (timestamp, not null, default now), with a unique index on (user_id, name)

Scenario: Annotations table schema

  • WHEN the schema is loaded
  • THEN the annotations table has columns: id (serial, primary key), chart_id (integer, foreign key to charts.id, not null), timestamp (timestamp, not null), label_type (text, not null), geometry (jsonb, nullable), color (text, default '#3b82f6'), user_id (uuid, foreign key to users.id, not null), created_at (timestamp, not null, default now)

Scenario: Span label types table schema

  • WHEN the schema is loaded
  • THEN the span_label_types table has columns: id (serial, primary key), name (text, not null), display_name (text, not null), color (text, not null), hotkey (text, nullable), is_active (boolean, not null, default true), sort_order (integer, not null, default 0), user_id (uuid, foreign key to users.id, not null), created_at (timestamp, not null, default now), with a unique index on (user_id, name)

Scenario: Span annotations table schema

  • WHEN the schema is loaded
  • THEN the span_annotations table has columns: id (serial, primary key), chart_id (integer, foreign key to charts.id, not null), start_time (timestamp, not null), end_time (timestamp, not null), label (text, not null), confidence (integer, nullable), outcome (text, nullable), notes (text, nullable), sub_spans (jsonb, nullable), color (text, not null, default '#2196F3'), source (text, not null, default 'human'), model_prediction (jsonb, nullable), user_id (uuid, foreign key to users.id, not null), created_at (timestamp, not null, default now)

ADDED Requirements

Requirement: User data migration script

The project SHALL include a migration script that adds user_id to existing tables, creates a default admin user, assigns all existing data to that user, and makes user_id NOT NULL.

Scenario: Migration adds user_id columns

  • WHEN the migration runs on a database without user_id columns
  • THEN user_id (uuid, nullable) columns are added to charts, annotation_types, annotations, span_label_types, span_annotations

Scenario: Default admin user created

  • WHEN the migration runs and no users exist
  • THEN a default admin user is created with email from DEFAULT_ADMIN_EMAIL env var (default: admin@candleannotator.local) and password from DEFAULT_ADMIN_PASSWORD env var (default: changeme123)

Scenario: Existing data assigned to admin

  • WHEN the migration runs and existing rows have NULL user_id
  • THEN all rows are updated to set user_id to the admin user's ID

Scenario: Columns made NOT NULL

  • WHEN all existing rows have been assigned a user_id
  • THEN the user_id columns are altered to NOT NULL with foreign key constraints

Scenario: Unique constraints updated

  • WHEN the migration completes
  • THEN the unique constraint on charts.name is replaced with (user_id, name)
  • AND the unique constraint on annotation_types.name is replaced with (user_id, name)
  • AND the unique constraint on span_label_types.name is replaced with (user_id, name)