Merge branch 'worktree-agent-a9a8b0dc' into Develop

# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	drizzle-pg/meta/0000_snapshot.json
#	drizzle-pg/meta/_journal.json
#	src/db/schema.ts
#	src/db/seed.ts
#	src/server/middleware/auth.ts
#	src/server/services/auth.service.ts
#	src/server/services/category.service.ts
#	src/server/services/oauth.service.ts
#	tests/helpers/db.ts
This commit is contained in:
2026-04-05 10:38:29 +02:00
16 changed files with 770 additions and 259 deletions

View File

@@ -0,0 +1,140 @@
CREATE TABLE "api_keys" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"key_hash" text NOT NULL,
"key_prefix" text NOT NULL,
"user_id" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "categories" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"icon" text DEFAULT 'package' NOT NULL,
"user_id" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "categories_user_id_name_unique" UNIQUE("user_id","name")
);
--> statement-breakpoint
CREATE TABLE "items" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"weight_grams" double precision,
"price_cents" integer,
"category_id" integer NOT NULL,
"user_id" integer NOT NULL,
"notes" text,
"product_url" text,
"image_filename" text,
"image_source_url" text,
"quantity" integer DEFAULT 1 NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "oauth_clients" (
"id" serial PRIMARY KEY NOT NULL,
"client_id" text NOT NULL,
"client_name" text,
"redirect_uris" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "oauth_clients_client_id_unique" UNIQUE("client_id")
);
--> statement-breakpoint
CREATE TABLE "oauth_codes" (
"id" serial PRIMARY KEY NOT NULL,
"code" text NOT NULL,
"client_id" text NOT NULL,
"code_challenge" text NOT NULL,
"code_challenge_method" text DEFAULT 'S256' NOT NULL,
"redirect_uri" text NOT NULL,
"expires_at" timestamp NOT NULL,
"used" integer DEFAULT 0 NOT NULL,
CONSTRAINT "oauth_codes_code_unique" UNIQUE("code")
);
--> statement-breakpoint
CREATE TABLE "oauth_tokens" (
"id" serial PRIMARY KEY NOT NULL,
"access_token_hash" text NOT NULL,
"refresh_token_hash" text NOT NULL,
"client_id" text NOT NULL,
"user_id" integer NOT NULL,
"expires_at" timestamp NOT NULL,
"refresh_expires_at" timestamp NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "oauth_tokens_access_token_hash_unique" UNIQUE("access_token_hash"),
CONSTRAINT "oauth_tokens_refresh_token_hash_unique" UNIQUE("refresh_token_hash")
);
--> statement-breakpoint
CREATE TABLE "settings" (
"user_id" integer NOT NULL,
"key" text NOT NULL,
"value" text NOT NULL,
CONSTRAINT "settings_user_id_key_pk" PRIMARY KEY("user_id","key")
);
--> statement-breakpoint
CREATE TABLE "setup_items" (
"id" serial PRIMARY KEY NOT NULL,
"setup_id" integer NOT NULL,
"item_id" integer NOT NULL,
"classification" text DEFAULT 'base' NOT NULL
);
--> statement-breakpoint
CREATE TABLE "setups" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"user_id" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "thread_candidates" (
"id" serial PRIMARY KEY NOT NULL,
"thread_id" integer NOT NULL,
"name" text NOT NULL,
"weight_grams" double precision,
"price_cents" integer,
"category_id" integer NOT NULL,
"notes" text,
"product_url" text,
"image_filename" text,
"image_source_url" text,
"status" text DEFAULT 'researching' NOT NULL,
"pros" text,
"cons" text,
"sort_order" double precision DEFAULT 0 NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "threads" (
"id" serial PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"status" text DEFAULT 'active' NOT NULL,
"resolved_candidate_id" integer,
"category_id" integer NOT NULL,
"user_id" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "users" (
"id" serial PRIMARY KEY NOT NULL,
"logto_sub" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "users_logto_sub_unique" UNIQUE("logto_sub")
);
--> statement-breakpoint
ALTER TABLE "api_keys" ADD CONSTRAINT "api_keys_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "categories" ADD CONSTRAINT "categories_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "items" ADD CONSTRAINT "items_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "items" ADD CONSTRAINT "items_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "oauth_tokens" ADD CONSTRAINT "oauth_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "settings" ADD CONSTRAINT "settings_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "setup_items" ADD CONSTRAINT "setup_items_setup_id_setups_id_fk" FOREIGN KEY ("setup_id") REFERENCES "public"."setups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "setup_items" ADD CONSTRAINT "setup_items_item_id_items_id_fk" FOREIGN KEY ("item_id") REFERENCES "public"."items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "setups" ADD CONSTRAINT "setups_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "thread_candidates" ADD CONSTRAINT "thread_candidates_thread_id_threads_id_fk" FOREIGN KEY ("thread_id") REFERENCES "public"."threads"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "thread_candidates" ADD CONSTRAINT "thread_candidates_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "threads" ADD CONSTRAINT "threads_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "threads" ADD CONSTRAINT "threads_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;

View File

@@ -1,5 +1,5 @@
{
"id": "2b9c2a13-0ec1-4011-b34a-13c710cf6c34",
"id": "2f3f44c0-0fd3-4ac5-b1fb-51bc709342df",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
@@ -32,6 +32,12 @@
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
@@ -41,7 +47,21 @@
}
},
"indexes": {},
"foreignKeys": {},
"foreignKeys": {
"api_keys_user_id_users_id_fk": {
"name": "api_keys_user_id_users_id_fk",
"tableFrom": "api_keys",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
@@ -71,6 +91,12 @@
"notNull": true,
"default": "'package'"
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
@@ -80,13 +106,28 @@
}
},
"indexes": {},
"foreignKeys": {},
"foreignKeys": {
"categories_user_id_users_id_fk": {
"name": "categories_user_id_users_id_fk",
"tableFrom": "categories",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"categories_name_unique": {
"name": "categories_name_unique",
"categories_user_id_name_unique": {
"name": "categories_user_id_name_unique",
"nullsNotDistinct": false,
"columns": [
"user_id",
"name"
]
}
@@ -129,6 +170,12 @@
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"notes": {
"name": "notes",
"type": "text",
@@ -189,6 +236,19 @@
],
"onDelete": "no action",
"onUpdate": "no action"
},
"items_user_id_users_id_fk": {
"name": "items_user_id_users_id_fk",
"tableFrom": "items",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
@@ -298,10 +358,10 @@
},
"used": {
"name": "used",
"type": "boolean",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": false
"default": 0
}
},
"indexes": {},
@@ -348,6 +408,12 @@
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
@@ -369,7 +435,21 @@
}
},
"indexes": {},
"foreignKeys": {},
"foreignKeys": {
"oauth_tokens_user_id_users_id_fk": {
"name": "oauth_tokens_user_id_users_id_fk",
"tableFrom": "oauth_tokens",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"oauth_tokens_access_token_hash_unique": {
@@ -391,59 +471,20 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.sessions": {
"name": "sessions",
"public.settings": {
"name": "settings",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.settings": {
"name": "settings",
"schema": "",
"columns": {
"key": {
"name": "key",
"type": "text",
"primaryKey": true,
"primaryKey": false,
"notNull": true
},
"value": {
@@ -454,8 +495,30 @@
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"foreignKeys": {
"settings_user_id_users_id_fk": {
"name": "settings_user_id_users_id_fk",
"tableFrom": "settings",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"settings_user_id_key_pk": {
"name": "settings_user_id_key_pk",
"columns": [
"user_id",
"key"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
@@ -542,6 +605,12 @@
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
@@ -558,7 +627,21 @@
}
},
"indexes": {},
"foreignKeys": {},
"foreignKeys": {
"setups_user_id_users_id_fk": {
"name": "setups_user_id_users_id_fk",
"tableFrom": "setups",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
@@ -740,6 +823,12 @@
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
@@ -769,6 +858,19 @@
],
"onDelete": "no action",
"onUpdate": "no action"
},
"threads_user_id_users_id_fk": {
"name": "threads_user_id_users_id_fk",
"tableFrom": "threads",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
@@ -787,14 +889,8 @@
"primaryKey": true,
"notNull": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true
},
"password_hash": {
"name": "password_hash",
"logto_sub": {
"name": "logto_sub",
"type": "text",
"primaryKey": false,
"notNull": true
@@ -811,11 +907,11 @@
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"users_username_unique": {
"name": "users_username_unique",
"users_logto_sub_unique": {
"name": "users_logto_sub_unique",
"nullsNotDistinct": false,
"columns": [
"username"
"logto_sub"
]
}
},

View File

@@ -5,8 +5,8 @@
{
"idx": 0,
"version": "7",
"when": 1775297839520,
"tag": "0000_fuzzy_shiva",
"when": 1775377947759,
"tag": "0000_thankful_loners",
"breakpoints": true
}
]