From c6224bd6fb5735b578da2d5fdb3e12ac565d2131 Mon Sep 17 00:00:00 2001 From: neon <18559658+neonsolstice@users.noreply.github.com> Date: Thu, 8 Jan 2026 08:00:29 -0600 Subject: [PATCH] feat: add auth schema, consolidate to single baseline migration --- server/package.json | 2 +- server/src/db/migrations/0000_init_schema.sql | 211 ++- .../src/db/migrations/0001_lethal_pixie.sql | 8 - .../db/migrations/0002_powerful_thundra.sql | 23 - .../0003_reading_progress_by_file.sql | 11 - .../src/db/migrations/meta/0000_snapshot.json | 1368 ++++++++++++++--- .../src/db/migrations/meta/0001_snapshot.json | 778 ---------- .../src/db/migrations/meta/0002_snapshot.json | 927 ----------- .../src/db/migrations/meta/0003_snapshot.json | 933 ----------- server/src/db/migrations/meta/_journal.json | 23 +- server/src/db/schema/auth.ts | 145 ++ server/src/db/schema/index.ts | 1 + server/src/db/schema/reader.ts | 105 +- .../annotation/annotation.controller.ts | 3 +- .../modules/annotation/annotation.service.ts | 3 +- server/src/modules/book/book.controller.ts | 6 +- server/src/modules/book/book.repository.ts | 14 +- server/src/modules/book/book.service.ts | 8 +- .../modules/bookmark/bookmark.controller.ts | 3 +- .../modules/bookmark/bookmark.repository.ts | 4 +- .../src/modules/bookmark/bookmark.service.ts | 4 +- 21 files changed, 1571 insertions(+), 3009 deletions(-) delete mode 100644 server/src/db/migrations/0001_lethal_pixie.sql delete mode 100644 server/src/db/migrations/0002_powerful_thundra.sql delete mode 100644 server/src/db/migrations/0003_reading_progress_by_file.sql delete mode 100644 server/src/db/migrations/meta/0001_snapshot.json delete mode 100644 server/src/db/migrations/meta/0002_snapshot.json delete mode 100644 server/src/db/migrations/meta/0003_snapshot.json create mode 100644 server/src/db/schema/auth.ts diff --git a/server/package.json b/server/package.json index e915c5934..49f9d2ade 100644 --- a/server/package.json +++ b/server/package.json @@ -12,7 +12,7 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "db:generate": "drizzle-kit generate", + "db:generate": "drizzle-kit generate --name", "db:migrate": "drizzle-kit migrate", "db:studio": "drizzle-kit studio", "scan:test": "ts-node -r tsconfig-paths/register src/scripts/scan-test.ts", diff --git a/server/src/db/migrations/0000_init_schema.sql b/server/src/db/migrations/0000_init_schema.sql index 9a8ec5e1c..3003b010e 100644 --- a/server/src/db/migrations/0000_init_schema.sql +++ b/server/src/db/migrations/0000_init_schema.sql @@ -1,14 +1,82 @@ -CREATE TABLE "authors" ( +CREATE TYPE "public"."library_access_level" AS ENUM('viewer', 'editor', 'owner');--> statement-breakpoint +CREATE TABLE "app_settings" ( "id" serial PRIMARY KEY NOT NULL, - "name" varchar(500) NOT NULL, - "sort_name" varchar(500) + "key" varchar(100) NOT NULL, + "value" text NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "app_settings_key_unique" UNIQUE("key") ); --> statement-breakpoint -CREATE TABLE "book_authors" ( - "book_id" integer NOT NULL, - "author_id" integer NOT NULL, - "display_order" integer DEFAULT 0 NOT NULL, - CONSTRAINT "book_authors_book_id_author_id_pk" PRIMARY KEY("book_id","author_id") +CREATE TABLE "password_reset_tokens" ( + "id" serial PRIMARY KEY NOT NULL, + "user_id" integer NOT NULL, + "token_hash" varchar(255) NOT NULL, + "expires_at" timestamp NOT NULL, + "used_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "password_reset_tokens_token_hash_unique" UNIQUE("token_hash") +); +--> statement-breakpoint +CREATE TABLE "permissions" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(100) NOT NULL, + "description" text, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "permissions_name_unique" UNIQUE("name") +); +--> statement-breakpoint +CREATE TABLE "refresh_tokens" ( + "id" serial PRIMARY KEY NOT NULL, + "user_id" integer NOT NULL, + "token_hash" varchar(255) NOT NULL, + "expires_at" timestamp NOT NULL, + "revoked_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "refresh_tokens_token_hash_unique" UNIQUE("token_hash") +); +--> statement-breakpoint +CREATE TABLE "role_permissions" ( + "role_id" integer NOT NULL, + "permission_id" integer NOT NULL, + CONSTRAINT "role_permissions_role_id_permission_id_pk" PRIMARY KEY("role_id","permission_id") +); +--> statement-breakpoint +CREATE TABLE "roles" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(100) NOT NULL, + "description" text, + "is_superuser" boolean DEFAULT false NOT NULL, + "is_system" boolean DEFAULT false NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "roles_name_unique" UNIQUE("name") +); +--> statement-breakpoint +CREATE TABLE "user_library_access" ( + "user_id" integer NOT NULL, + "library_id" integer NOT NULL, + "access_level" "library_access_level" DEFAULT 'viewer' NOT NULL, + CONSTRAINT "user_library_access_user_id_library_id_pk" PRIMARY KEY("user_id","library_id") +); +--> statement-breakpoint +CREATE TABLE "user_roles" ( + "user_id" integer NOT NULL, + "role_id" integer NOT NULL, + CONSTRAINT "user_roles_user_id_role_id_pk" PRIMARY KEY("user_id","role_id") +); +--> statement-breakpoint +CREATE TABLE "users" ( + "id" serial PRIMARY KEY NOT NULL, + "username" varchar(100) NOT NULL, + "name" varchar(255) NOT NULL, + "email" varchar(255), + "password_hash" varchar(255) NOT NULL, + "active" boolean DEFAULT true NOT NULL, + "is_default_password" boolean DEFAULT false NOT NULL, + "settings" jsonb DEFAULT '{}'::jsonb NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "users_username_unique" UNIQUE("username"), + CONSTRAINT "users_email_unique" UNIQUE("email") ); --> statement-breakpoint CREATE TABLE "book_files" ( @@ -27,28 +95,6 @@ CREATE TABLE "book_files" ( "updated_at" timestamp DEFAULT now() NOT NULL ); --> statement-breakpoint -CREATE TABLE "book_metadata" ( - "book_id" integer PRIMARY KEY NOT NULL, - "title" varchar(1000), - "subtitle" varchar(1000), - "description" text, - "isbn10" varchar(10), - "isbn13" varchar(13), - "publisher" varchar(500), - "published_year" integer, - "language" varchar(10), - "page_count" integer, - "series_name" varchar(500), - "series_index" real, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "book_tags" ( - "book_id" integer NOT NULL, - "tag_id" integer NOT NULL, - CONSTRAINT "book_tags_book_id_tag_id_pk" PRIMARY KEY("book_id","tag_id") -); ---> statement-breakpoint CREATE TABLE "books" ( "id" serial PRIMARY KEY NOT NULL, "library_id" integer NOT NULL, @@ -76,6 +122,47 @@ CREATE TABLE "library_folders" ( "created_at" timestamp DEFAULT now() NOT NULL ); --> statement-breakpoint +CREATE TABLE "authors" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(500) NOT NULL, + "sort_name" varchar(500) +); +--> statement-breakpoint +CREATE TABLE "book_authors" ( + "book_id" integer NOT NULL, + "author_id" integer NOT NULL, + "display_order" integer DEFAULT 0 NOT NULL, + CONSTRAINT "book_authors_book_id_author_id_pk" PRIMARY KEY("book_id","author_id") +); +--> statement-breakpoint +CREATE TABLE "book_metadata" ( + "book_id" integer PRIMARY KEY NOT NULL, + "title" varchar(1000), + "subtitle" varchar(1000), + "description" text, + "isbn10" varchar(10), + "isbn13" varchar(13), + "publisher" varchar(500), + "published_year" integer, + "language" varchar(10), + "page_count" integer, + "series_name" varchar(500), + "series_index" real, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "book_tags" ( + "book_id" integer NOT NULL, + "tag_id" integer NOT NULL, + CONSTRAINT "book_tags_book_id_tag_id_pk" PRIMARY KEY("book_id","tag_id") +); +--> statement-breakpoint +CREATE TABLE "tags" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(200) NOT NULL, + CONSTRAINT "tags_name_unique" UNIQUE("name") +); +--> statement-breakpoint CREATE TABLE "scan_jobs" ( "id" serial PRIMARY KEY NOT NULL, "library_id" integer NOT NULL, @@ -89,20 +176,66 @@ CREATE TABLE "scan_jobs" ( "completed_at" timestamp ); --> statement-breakpoint -CREATE TABLE "tags" ( +CREATE TABLE "annotations" ( "id" serial PRIMARY KEY NOT NULL, - "name" varchar(200) NOT NULL, - CONSTRAINT "tags_name_unique" UNIQUE("name") + "user_id" integer NOT NULL, + "book_id" integer NOT NULL, + "cfi" varchar(2000) NOT NULL, + "text" text NOT NULL, + "color" varchar(20) DEFAULT 'yellow' NOT NULL, + "style" varchar(20) DEFAULT 'highlight' NOT NULL, + "note" text, + "chapter_title" varchar(500), + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL ); --> statement-breakpoint -ALTER TABLE "book_authors" ADD CONSTRAINT "book_authors_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "book_authors" ADD CONSTRAINT "book_authors_author_id_authors_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."authors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE TABLE "bookmarks" ( + "id" serial PRIMARY KEY NOT NULL, + "user_id" integer NOT NULL, + "book_id" integer NOT NULL, + "cfi" varchar(2000) NOT NULL, + "title" varchar(500) NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "reading_progress" ( + "book_file_id" integer NOT NULL, + "user_id" integer NOT NULL, + "percentage" real DEFAULT 0 NOT NULL, + "cfi" varchar(2000), + "page_number" integer, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "reading_progress_book_file_id_user_id_pk" PRIMARY KEY("book_file_id","user_id") +); +--> statement-breakpoint +ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "refresh_tokens" ADD CONSTRAINT "refresh_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_role_id_roles_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."roles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_permission_id_permissions_id_fk" FOREIGN KEY ("permission_id") REFERENCES "public"."permissions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_library_access" ADD CONSTRAINT "user_library_access_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_library_access" ADD CONSTRAINT "user_library_access_library_id_libraries_id_fk" FOREIGN KEY ("library_id") REFERENCES "public"."libraries"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_roles" ADD CONSTRAINT "user_roles_role_id_roles_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."roles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "book_files" ADD CONSTRAINT "book_files_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "book_files" ADD CONSTRAINT "book_files_library_folder_id_library_folders_id_fk" FOREIGN KEY ("library_folder_id") REFERENCES "public"."library_folders"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "book_metadata" ADD CONSTRAINT "book_metadata_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "book_tags" ADD CONSTRAINT "book_tags_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "book_tags" ADD CONSTRAINT "book_tags_tag_id_tags_id_fk" FOREIGN KEY ("tag_id") REFERENCES "public"."tags"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "books" ADD CONSTRAINT "books_library_id_libraries_id_fk" FOREIGN KEY ("library_id") REFERENCES "public"."libraries"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint ALTER TABLE "books" ADD CONSTRAINT "books_library_folder_id_library_folders_id_fk" FOREIGN KEY ("library_folder_id") REFERENCES "public"."library_folders"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "library_folders" ADD CONSTRAINT "library_folders_library_id_libraries_id_fk" FOREIGN KEY ("library_id") REFERENCES "public"."libraries"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "scan_jobs" ADD CONSTRAINT "scan_jobs_library_id_libraries_id_fk" FOREIGN KEY ("library_id") REFERENCES "public"."libraries"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file +ALTER TABLE "book_authors" ADD CONSTRAINT "book_authors_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "book_authors" ADD CONSTRAINT "book_authors_author_id_authors_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."authors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "book_metadata" ADD CONSTRAINT "book_metadata_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "book_tags" ADD CONSTRAINT "book_tags_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "book_tags" ADD CONSTRAINT "book_tags_tag_id_tags_id_fk" FOREIGN KEY ("tag_id") REFERENCES "public"."tags"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "scan_jobs" ADD CONSTRAINT "scan_jobs_library_id_libraries_id_fk" FOREIGN KEY ("library_id") REFERENCES "public"."libraries"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "annotations" ADD CONSTRAINT "annotations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "annotations" ADD CONSTRAINT "annotations_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bookmarks" ADD CONSTRAINT "bookmarks_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bookmarks" ADD CONSTRAINT "bookmarks_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "reading_progress" ADD CONSTRAINT "reading_progress_book_file_id_book_files_id_fk" FOREIGN KEY ("book_file_id") REFERENCES "public"."book_files"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "reading_progress" ADD CONSTRAINT "reading_progress_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "password_reset_tokens_user_id_idx" ON "password_reset_tokens" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "refresh_tokens_user_id_idx" ON "refresh_tokens" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "annotations_user_id_idx" ON "annotations" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "bookmarks_user_id_idx" ON "bookmarks" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "reading_progress_user_id_idx" ON "reading_progress" USING btree ("user_id"); \ No newline at end of file diff --git a/server/src/db/migrations/0001_lethal_pixie.sql b/server/src/db/migrations/0001_lethal_pixie.sql deleted file mode 100644 index 34c85052b..000000000 --- a/server/src/db/migrations/0001_lethal_pixie.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE "reading_progress" ( - "book_id" integer PRIMARY KEY NOT NULL, - "cfi" varchar(2000), - "percentage" real DEFAULT 0 NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -ALTER TABLE "reading_progress" ADD CONSTRAINT "reading_progress_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/server/src/db/migrations/0002_powerful_thundra.sql b/server/src/db/migrations/0002_powerful_thundra.sql deleted file mode 100644 index 8d2d1bd7d..000000000 --- a/server/src/db/migrations/0002_powerful_thundra.sql +++ /dev/null @@ -1,23 +0,0 @@ -CREATE TABLE "annotations" ( - "id" serial PRIMARY KEY NOT NULL, - "book_id" integer NOT NULL, - "cfi" varchar(2000) NOT NULL, - "text" text NOT NULL, - "color" varchar(20) DEFAULT 'yellow' NOT NULL, - "style" varchar(20) DEFAULT 'highlight' NOT NULL, - "note" text, - "chapter_title" varchar(500), - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "bookmarks" ( - "id" serial PRIMARY KEY NOT NULL, - "book_id" integer NOT NULL, - "cfi" varchar(2000) NOT NULL, - "title" varchar(500) NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -ALTER TABLE "annotations" ADD CONSTRAINT "annotations_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "bookmarks" ADD CONSTRAINT "bookmarks_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/server/src/db/migrations/0003_reading_progress_by_file.sql b/server/src/db/migrations/0003_reading_progress_by_file.sql deleted file mode 100644 index 7b92b6c13..000000000 --- a/server/src/db/migrations/0003_reading_progress_by_file.sql +++ /dev/null @@ -1,11 +0,0 @@ -DROP TABLE "reading_progress"; ---> statement-breakpoint -CREATE TABLE "reading_progress" ( - "book_file_id" integer PRIMARY KEY NOT NULL, - "percentage" real DEFAULT 0 NOT NULL, - "cfi" varchar(2000), - "page_number" integer, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -ALTER TABLE "reading_progress" ADD CONSTRAINT "reading_progress_book_file_id_book_files_id_fk" FOREIGN KEY ("book_file_id") REFERENCES "public"."book_files"("id") ON DELETE cascade ON UPDATE no action; diff --git a/server/src/db/migrations/meta/0000_snapshot.json b/server/src/db/migrations/meta/0000_snapshot.json index 5b942cbb7..ced5f9789 100644 --- a/server/src/db/migrations/meta/0000_snapshot.json +++ b/server/src/db/migrations/meta/0000_snapshot.json @@ -1,11 +1,145 @@ { - "id": "0ef7cbbe-fd22-4de4-8490-422e3ab29b4f", + "id": "f65aa508-f4aa-4dc3-bd34-e2a1d8ac3a61", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", "tables": { - "public.authors": { - "name": "authors", + "public.app_settings": { + "name": "app_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "app_settings_key_unique": { + "name": "app_settings_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "password_reset_tokens_user_id_idx": { + "name": "password_reset_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "password_reset_tokens_token_hash_unique": { + "name": "password_reset_tokens_token_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "token_hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permissions": { + "name": "permissions", "schema": "", "columns": { "id": { @@ -16,57 +150,153 @@ }, "name": { "name": "name", - "type": "varchar(500)", + "type": "varchar(100)", "primaryKey": false, "notNull": true }, - "sort_name": { - "name": "sort_name", - "type": "varchar(500)", + "description": { + "name": "description", + "type": "text", "primaryKey": false, "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "permissions_name_unique": { + "name": "permissions_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false }, - "public.book_authors": { - "name": "book_authors", + "public.refresh_tokens": { + "name": "refresh_tokens", "schema": "", "columns": { - "book_id": { - "name": "book_id", + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", "type": "integer", "primaryKey": false, "notNull": true }, - "author_id": { - "name": "author_id", - "type": "integer", + "token_hash": { + "name": "token_hash", + "type": "varchar(255)", "primaryKey": false, "notNull": true }, - "display_order": { - "name": "display_order", - "type": "integer", + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", "primaryKey": false, "notNull": true, - "default": 0 + "default": "now()" + } + }, + "indexes": { + "refresh_tokens_user_id_idx": { + "name": "refresh_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "refresh_tokens_user_id_users_id_fk": { + "name": "refresh_tokens_user_id_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "refresh_tokens_token_hash_unique": { + "name": "refresh_tokens_token_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "token_hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role_permissions": { + "name": "role_permissions", + "schema": "", + "columns": { + "role_id": { + "name": "role_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "permission_id": { + "name": "permission_id", + "type": "integer", + "primaryKey": false, + "notNull": true } }, "indexes": {}, "foreignKeys": { - "book_authors_book_id_books_id_fk": { - "name": "book_authors_book_id_books_id_fk", - "tableFrom": "book_authors", - "tableTo": "books", + "role_permissions_role_id_roles_id_fk": { + "name": "role_permissions_role_id_roles_id_fk", + "tableFrom": "role_permissions", + "tableTo": "roles", "columnsFrom": [ - "book_id" + "role_id" ], "columnsTo": [ "id" @@ -74,26 +304,26 @@ "onDelete": "cascade", "onUpdate": "no action" }, - "book_authors_author_id_authors_id_fk": { - "name": "book_authors_author_id_authors_id_fk", - "tableFrom": "book_authors", - "tableTo": "authors", + "role_permissions_permission_id_permissions_id_fk": { + "name": "role_permissions_permission_id_permissions_id_fk", + "tableFrom": "role_permissions", + "tableTo": "permissions", "columnsFrom": [ - "author_id" + "permission_id" ], "columnsTo": [ "id" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" } }, "compositePrimaryKeys": { - "book_authors_book_id_author_id_pk": { - "name": "book_authors_book_id_author_id_pk", + "role_permissions_role_id_permission_id_pk": { + "name": "role_permissions_role_id_permission_id_pk", "columns": [ - "book_id", - "author_id" + "role_id", + "permission_id" ] } }, @@ -102,6 +332,287 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.roles": { + "name": "roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_superuser": { + "name": "is_superuser", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_system": { + "name": "is_system", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "roles_name_unique": { + "name": "roles_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_library_access": { + "name": "user_library_access", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "library_id": { + "name": "library_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "access_level": { + "name": "access_level", + "type": "library_access_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'viewer'" + } + }, + "indexes": {}, + "foreignKeys": { + "user_library_access_user_id_users_id_fk": { + "name": "user_library_access_user_id_users_id_fk", + "tableFrom": "user_library_access", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_library_access_library_id_libraries_id_fk": { + "name": "user_library_access_library_id_libraries_id_fk", + "tableFrom": "user_library_access", + "tableTo": "libraries", + "columnsFrom": [ + "library_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_library_access_user_id_library_id_pk": { + "name": "user_library_access_user_id_library_id_pk", + "columns": [ + "user_id", + "library_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_roles": { + "name": "user_roles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_roles_user_id_users_id_fk": { + "name": "user_roles_user_id_users_id_fk", + "tableFrom": "user_roles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_roles_role_id_roles_id_fk": { + "name": "user_roles_role_id_roles_id_fk", + "tableFrom": "user_roles", + "tableTo": "roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_roles_user_id_role_id_pk": { + "name": "user_roles_user_id_role_id_pk", + "columns": [ + "user_id", + "role_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "password_hash": { + "name": "password_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_default_password": { + "name": "is_default_password", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_username_unique": { + "name": "users_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.book_files": { "name": "book_files", "schema": "", @@ -223,172 +734,6 @@ "checkConstraints": {}, "isRLSEnabled": false }, - "public.book_metadata": { - "name": "book_metadata", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": true, - "notNull": true - }, - "title": { - "name": "title", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "subtitle": { - "name": "subtitle", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "isbn10": { - "name": "isbn10", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "isbn13": { - "name": "isbn13", - "type": "varchar(13)", - "primaryKey": false, - "notNull": false - }, - "publisher": { - "name": "publisher", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "published_year": { - "name": "published_year", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "language": { - "name": "language", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "page_count": { - "name": "page_count", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "series_name": { - "name": "series_name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "series_index": { - "name": "series_index", - "type": "real", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "book_metadata_book_id_books_id_fk": { - "name": "book_metadata_book_id_books_id_fk", - "tableFrom": "book_metadata", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_tags": { - "name": "book_tags", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "tag_id": { - "name": "tag_id", - "type": "integer", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "book_tags_book_id_books_id_fk": { - "name": "book_tags_book_id_books_id_fk", - "tableFrom": "book_tags", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_tags_tag_id_tags_id_fk": { - "name": "book_tags_tag_id_tags_id_fk", - "tableFrom": "book_tags", - "tableTo": "tags", - "columnsFrom": [ - "tag_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "book_tags_book_id_tag_id_pk": { - "name": "book_tags_book_id_tag_id_pk", - "columns": [ - "book_id", - "tag_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, "public.books": { "name": "books", "schema": "", @@ -585,6 +930,303 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.authors": { + "name": "authors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "sort_name": { + "name": "sort_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_authors": { + "name": "book_authors", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "book_authors_book_id_books_id_fk": { + "name": "book_authors_book_id_books_id_fk", + "tableFrom": "book_authors", + "tableTo": "books", + "columnsFrom": [ + "book_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_authors_author_id_authors_id_fk": { + "name": "book_authors_author_id_authors_id_fk", + "tableFrom": "book_authors", + "tableTo": "authors", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_authors_book_id_author_id_pk": { + "name": "book_authors_book_id_author_id_pk", + "columns": [ + "book_id", + "author_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_metadata": { + "name": "book_metadata", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isbn10": { + "name": "isbn10", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "isbn13": { + "name": "isbn13", + "type": "varchar(13)", + "primaryKey": false, + "notNull": false + }, + "publisher": { + "name": "publisher", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "published_year": { + "name": "published_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "language": { + "name": "language", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "page_count": { + "name": "page_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "series_name": { + "name": "series_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "series_index": { + "name": "series_index", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "book_metadata_book_id_books_id_fk": { + "name": "book_metadata_book_id_books_id_fk", + "tableFrom": "book_metadata", + "tableTo": "books", + "columnsFrom": [ + "book_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_tags": { + "name": "book_tags", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag_id": { + "name": "tag_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "book_tags_book_id_books_id_fk": { + "name": "book_tags_book_id_books_id_fk", + "tableFrom": "book_tags", + "tableTo": "books", + "columnsFrom": [ + "book_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_tags_tag_id_tags_id_fk": { + "name": "book_tags_tag_id_tags_id_fk", + "tableFrom": "book_tags", + "tableTo": "tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_tags_book_id_tag_id_pk": { + "name": "book_tags_book_id_tag_id_pk", + "columns": [ + "book_id", + "tag_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tags": { + "name": "tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tags_name_unique": { + "name": "tags_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.scan_jobs": { "name": "scan_jobs", "schema": "", @@ -677,8 +1319,8 @@ "checkConstraints": {}, "isRLSEnabled": false }, - "public.tags": { - "name": "tags", + "public.annotations": { + "name": "annotations", "schema": "", "columns": { "id": { @@ -687,31 +1329,329 @@ "primaryKey": true, "notNull": true }, - "name": { - "name": "name", - "type": "varchar(200)", + "user_id": { + "name": "user_id", + "type": "integer", "primaryKey": false, "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cfi": { + "name": "cfi", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'yellow'" + }, + "style": { + "name": "style", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'highlight'" + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "chapter_title": { + "name": "chapter_title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" } }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "tags_name_unique": { - "name": "tags_name_unique", - "nullsNotDistinct": false, + "indexes": { + "annotations_user_id_idx": { + "name": "annotations_user_id_idx", "columns": [ - "name" + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotations_user_id_users_id_fk": { + "name": "annotations_user_id_users_id_fk", + "tableFrom": "annotations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "annotations_book_id_books_id_fk": { + "name": "annotations_book_id_books_id_fk", + "tableFrom": "annotations", + "tableTo": "books", + "columnsFrom": [ + "book_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bookmarks": { + "name": "bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cfi": { + "name": "cfi", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "bookmarks_user_id_idx": { + "name": "bookmarks_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "bookmarks_user_id_users_id_fk": { + "name": "bookmarks_user_id_users_id_fk", + "tableFrom": "bookmarks", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "bookmarks_book_id_books_id_fk": { + "name": "bookmarks_book_id_books_id_fk", + "tableFrom": "bookmarks", + "tableTo": "books", + "columnsFrom": [ + "book_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reading_progress": { + "name": "reading_progress", + "schema": "", + "columns": { + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cfi": { + "name": "cfi", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": false + }, + "page_number": { + "name": "page_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reading_progress_user_id_idx": { + "name": "reading_progress_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reading_progress_book_file_id_book_files_id_fk": { + "name": "reading_progress_book_file_id_book_files_id_fk", + "tableFrom": "reading_progress", + "tableTo": "book_files", + "columnsFrom": [ + "book_file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reading_progress_user_id_users_id_fk": { + "name": "reading_progress_user_id_users_id_fk", + "tableFrom": "reading_progress", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "reading_progress_book_file_id_user_id_pk": { + "name": "reading_progress_book_file_id_user_id_pk", + "columns": [ + "book_file_id", + "user_id" ] } }, + "uniqueConstraints": {}, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false } }, - "enums": {}, + "enums": { + "public.library_access_level": { + "name": "library_access_level", + "schema": "public", + "values": [ + "viewer", + "editor", + "owner" + ] + } + }, "schemas": {}, "sequences": {}, "roles": {}, diff --git a/server/src/db/migrations/meta/0001_snapshot.json b/server/src/db/migrations/meta/0001_snapshot.json deleted file mode 100644 index 6e77f93ba..000000000 --- a/server/src/db/migrations/meta/0001_snapshot.json +++ /dev/null @@ -1,778 +0,0 @@ -{ - "id": "dec293e0-a32e-4ed0-b6c4-165016502eeb", - "prevId": "0ef7cbbe-fd22-4de4-8490-422e3ab29b4f", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.book_files": { - "name": "book_files", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "library_folder_id": { - "name": "library_folder_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "absolute_path": { - "name": "absolute_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "rel_path": { - "name": "rel_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": false - }, - "ino": { - "name": "ino", - "type": "bigint", - "primaryKey": false, - "notNull": true - }, - "size_bytes": { - "name": "size_bytes", - "type": "bigint", - "primaryKey": false, - "notNull": false - }, - "mtime": { - "name": "mtime", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "hash": { - "name": "hash", - "type": "varchar(64)", - "primaryKey": false, - "notNull": false - }, - "format": { - "name": "format", - "type": "varchar(20)", - "primaryKey": false, - "notNull": false - }, - "role": { - "name": "role", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'primary'" - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "book_files_book_id_books_id_fk": { - "name": "book_files_book_id_books_id_fk", - "tableFrom": "book_files", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_files_library_folder_id_library_folders_id_fk": { - "name": "book_files_library_folder_id_library_folders_id_fk", - "tableFrom": "book_files", - "tableTo": "library_folders", - "columnsFrom": [ - "library_folder_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.books": { - "name": "books", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "library_folder_id": { - "name": "library_folder_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "folder_path": { - "name": "folder_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'present'" - }, - "added_at": { - "name": "added_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "books_library_id_libraries_id_fk": { - "name": "books_library_id_libraries_id_fk", - "tableFrom": "books", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "books_library_folder_id_library_folders_id_fk": { - "name": "books_library_folder_id_library_folders_id_fk", - "tableFrom": "books", - "tableTo": "library_folders", - "columnsFrom": [ - "library_folder_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.libraries": { - "name": "libraries", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "icon": { - "name": "icon", - "type": "varchar(100)", - "primaryKey": false, - "notNull": false - }, - "scan_mode": { - "name": "scan_mode", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'auto'" - }, - "poll_interval_seconds": { - "name": "poll_interval_seconds", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 300 - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.library_folders": { - "name": "library_folders", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "path": { - "name": "path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "library_folders_library_id_libraries_id_fk": { - "name": "library_folders_library_id_libraries_id_fk", - "tableFrom": "library_folders", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.authors": { - "name": "authors", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": true - }, - "sort_name": { - "name": "sort_name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_authors": { - "name": "book_authors", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "author_id": { - "name": "author_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "display_order": { - "name": "display_order", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - } - }, - "indexes": {}, - "foreignKeys": { - "book_authors_book_id_books_id_fk": { - "name": "book_authors_book_id_books_id_fk", - "tableFrom": "book_authors", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_authors_author_id_authors_id_fk": { - "name": "book_authors_author_id_authors_id_fk", - "tableFrom": "book_authors", - "tableTo": "authors", - "columnsFrom": [ - "author_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "book_authors_book_id_author_id_pk": { - "name": "book_authors_book_id_author_id_pk", - "columns": [ - "book_id", - "author_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_metadata": { - "name": "book_metadata", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": true, - "notNull": true - }, - "title": { - "name": "title", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "subtitle": { - "name": "subtitle", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "isbn10": { - "name": "isbn10", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "isbn13": { - "name": "isbn13", - "type": "varchar(13)", - "primaryKey": false, - "notNull": false - }, - "publisher": { - "name": "publisher", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "published_year": { - "name": "published_year", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "language": { - "name": "language", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "page_count": { - "name": "page_count", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "series_name": { - "name": "series_name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "series_index": { - "name": "series_index", - "type": "real", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "book_metadata_book_id_books_id_fk": { - "name": "book_metadata_book_id_books_id_fk", - "tableFrom": "book_metadata", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_tags": { - "name": "book_tags", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "tag_id": { - "name": "tag_id", - "type": "integer", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "book_tags_book_id_books_id_fk": { - "name": "book_tags_book_id_books_id_fk", - "tableFrom": "book_tags", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_tags_tag_id_tags_id_fk": { - "name": "book_tags_tag_id_tags_id_fk", - "tableFrom": "book_tags", - "tableTo": "tags", - "columnsFrom": [ - "tag_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "book_tags_book_id_tag_id_pk": { - "name": "book_tags_book_id_tag_id_pk", - "columns": [ - "book_id", - "tag_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.tags": { - "name": "tags", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(200)", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "tags_name_unique": { - "name": "tags_name_unique", - "nullsNotDistinct": false, - "columns": [ - "name" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.scan_jobs": { - "name": "scan_jobs", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'running'" - }, - "triggered_by": { - "name": "triggered_by", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true - }, - "added_count": { - "name": "added_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "updated_count": { - "name": "updated_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "missing_count": { - "name": "missing_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "error_message": { - "name": "error_message", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "started_at": { - "name": "started_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "completed_at": { - "name": "completed_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "scan_jobs_library_id_libraries_id_fk": { - "name": "scan_jobs_library_id_libraries_id_fk", - "tableFrom": "scan_jobs", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.reading_progress": { - "name": "reading_progress", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": true, - "notNull": true - }, - "cfi": { - "name": "cfi", - "type": "varchar(2000)", - "primaryKey": false, - "notNull": false - }, - "percentage": { - "name": "percentage", - "type": "real", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "reading_progress_book_id_books_id_fk": { - "name": "reading_progress_book_id_books_id_fk", - "tableFrom": "reading_progress", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/server/src/db/migrations/meta/0002_snapshot.json b/server/src/db/migrations/meta/0002_snapshot.json deleted file mode 100644 index 2bb55108e..000000000 --- a/server/src/db/migrations/meta/0002_snapshot.json +++ /dev/null @@ -1,927 +0,0 @@ -{ - "id": "302f263f-9118-49fa-bb8e-9d1238b67a76", - "prevId": "dec293e0-a32e-4ed0-b6c4-165016502eeb", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.book_files": { - "name": "book_files", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "library_folder_id": { - "name": "library_folder_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "absolute_path": { - "name": "absolute_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "rel_path": { - "name": "rel_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": false - }, - "ino": { - "name": "ino", - "type": "bigint", - "primaryKey": false, - "notNull": true - }, - "size_bytes": { - "name": "size_bytes", - "type": "bigint", - "primaryKey": false, - "notNull": false - }, - "mtime": { - "name": "mtime", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "hash": { - "name": "hash", - "type": "varchar(64)", - "primaryKey": false, - "notNull": false - }, - "format": { - "name": "format", - "type": "varchar(20)", - "primaryKey": false, - "notNull": false - }, - "role": { - "name": "role", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'primary'" - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "book_files_book_id_books_id_fk": { - "name": "book_files_book_id_books_id_fk", - "tableFrom": "book_files", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_files_library_folder_id_library_folders_id_fk": { - "name": "book_files_library_folder_id_library_folders_id_fk", - "tableFrom": "book_files", - "tableTo": "library_folders", - "columnsFrom": [ - "library_folder_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.books": { - "name": "books", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "library_folder_id": { - "name": "library_folder_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "folder_path": { - "name": "folder_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'present'" - }, - "added_at": { - "name": "added_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "books_library_id_libraries_id_fk": { - "name": "books_library_id_libraries_id_fk", - "tableFrom": "books", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "books_library_folder_id_library_folders_id_fk": { - "name": "books_library_folder_id_library_folders_id_fk", - "tableFrom": "books", - "tableTo": "library_folders", - "columnsFrom": [ - "library_folder_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.libraries": { - "name": "libraries", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "icon": { - "name": "icon", - "type": "varchar(100)", - "primaryKey": false, - "notNull": false - }, - "scan_mode": { - "name": "scan_mode", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'auto'" - }, - "poll_interval_seconds": { - "name": "poll_interval_seconds", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 300 - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.library_folders": { - "name": "library_folders", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "path": { - "name": "path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "library_folders_library_id_libraries_id_fk": { - "name": "library_folders_library_id_libraries_id_fk", - "tableFrom": "library_folders", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.authors": { - "name": "authors", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": true - }, - "sort_name": { - "name": "sort_name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_authors": { - "name": "book_authors", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "author_id": { - "name": "author_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "display_order": { - "name": "display_order", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - } - }, - "indexes": {}, - "foreignKeys": { - "book_authors_book_id_books_id_fk": { - "name": "book_authors_book_id_books_id_fk", - "tableFrom": "book_authors", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_authors_author_id_authors_id_fk": { - "name": "book_authors_author_id_authors_id_fk", - "tableFrom": "book_authors", - "tableTo": "authors", - "columnsFrom": [ - "author_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "book_authors_book_id_author_id_pk": { - "name": "book_authors_book_id_author_id_pk", - "columns": [ - "book_id", - "author_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_metadata": { - "name": "book_metadata", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": true, - "notNull": true - }, - "title": { - "name": "title", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "subtitle": { - "name": "subtitle", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "isbn10": { - "name": "isbn10", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "isbn13": { - "name": "isbn13", - "type": "varchar(13)", - "primaryKey": false, - "notNull": false - }, - "publisher": { - "name": "publisher", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "published_year": { - "name": "published_year", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "language": { - "name": "language", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "page_count": { - "name": "page_count", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "series_name": { - "name": "series_name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "series_index": { - "name": "series_index", - "type": "real", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "book_metadata_book_id_books_id_fk": { - "name": "book_metadata_book_id_books_id_fk", - "tableFrom": "book_metadata", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_tags": { - "name": "book_tags", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "tag_id": { - "name": "tag_id", - "type": "integer", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "book_tags_book_id_books_id_fk": { - "name": "book_tags_book_id_books_id_fk", - "tableFrom": "book_tags", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_tags_tag_id_tags_id_fk": { - "name": "book_tags_tag_id_tags_id_fk", - "tableFrom": "book_tags", - "tableTo": "tags", - "columnsFrom": [ - "tag_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "book_tags_book_id_tag_id_pk": { - "name": "book_tags_book_id_tag_id_pk", - "columns": [ - "book_id", - "tag_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.tags": { - "name": "tags", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(200)", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "tags_name_unique": { - "name": "tags_name_unique", - "nullsNotDistinct": false, - "columns": [ - "name" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.scan_jobs": { - "name": "scan_jobs", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'running'" - }, - "triggered_by": { - "name": "triggered_by", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true - }, - "added_count": { - "name": "added_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "updated_count": { - "name": "updated_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "missing_count": { - "name": "missing_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "error_message": { - "name": "error_message", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "started_at": { - "name": "started_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "completed_at": { - "name": "completed_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "scan_jobs_library_id_libraries_id_fk": { - "name": "scan_jobs_library_id_libraries_id_fk", - "tableFrom": "scan_jobs", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.annotations": { - "name": "annotations", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "cfi": { - "name": "cfi", - "type": "varchar(2000)", - "primaryKey": false, - "notNull": true - }, - "text": { - "name": "text", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "color": { - "name": "color", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'yellow'" - }, - "style": { - "name": "style", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'highlight'" - }, - "note": { - "name": "note", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "chapter_title": { - "name": "chapter_title", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "annotations_book_id_books_id_fk": { - "name": "annotations_book_id_books_id_fk", - "tableFrom": "annotations", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.bookmarks": { - "name": "bookmarks", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "cfi": { - "name": "cfi", - "type": "varchar(2000)", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "varchar(500)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "bookmarks_book_id_books_id_fk": { - "name": "bookmarks_book_id_books_id_fk", - "tableFrom": "bookmarks", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.reading_progress": { - "name": "reading_progress", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": true, - "notNull": true - }, - "cfi": { - "name": "cfi", - "type": "varchar(2000)", - "primaryKey": false, - "notNull": false - }, - "percentage": { - "name": "percentage", - "type": "real", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "reading_progress_book_id_books_id_fk": { - "name": "reading_progress_book_id_books_id_fk", - "tableFrom": "reading_progress", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/server/src/db/migrations/meta/0003_snapshot.json b/server/src/db/migrations/meta/0003_snapshot.json deleted file mode 100644 index 40c847ff3..000000000 --- a/server/src/db/migrations/meta/0003_snapshot.json +++ /dev/null @@ -1,933 +0,0 @@ -{ - "id": "0496ecaa-687d-42db-982e-096c3f3605f7", - "prevId": "302f263f-9118-49fa-bb8e-9d1238b67a76", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.book_files": { - "name": "book_files", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "library_folder_id": { - "name": "library_folder_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "absolute_path": { - "name": "absolute_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "rel_path": { - "name": "rel_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": false - }, - "ino": { - "name": "ino", - "type": "bigint", - "primaryKey": false, - "notNull": true - }, - "size_bytes": { - "name": "size_bytes", - "type": "bigint", - "primaryKey": false, - "notNull": false - }, - "mtime": { - "name": "mtime", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "hash": { - "name": "hash", - "type": "varchar(64)", - "primaryKey": false, - "notNull": false - }, - "format": { - "name": "format", - "type": "varchar(20)", - "primaryKey": false, - "notNull": false - }, - "role": { - "name": "role", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'primary'" - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "book_files_book_id_books_id_fk": { - "name": "book_files_book_id_books_id_fk", - "tableFrom": "book_files", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_files_library_folder_id_library_folders_id_fk": { - "name": "book_files_library_folder_id_library_folders_id_fk", - "tableFrom": "book_files", - "tableTo": "library_folders", - "columnsFrom": [ - "library_folder_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.books": { - "name": "books", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "library_folder_id": { - "name": "library_folder_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "folder_path": { - "name": "folder_path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'present'" - }, - "added_at": { - "name": "added_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "books_library_id_libraries_id_fk": { - "name": "books_library_id_libraries_id_fk", - "tableFrom": "books", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "books_library_folder_id_library_folders_id_fk": { - "name": "books_library_folder_id_library_folders_id_fk", - "tableFrom": "books", - "tableTo": "library_folders", - "columnsFrom": [ - "library_folder_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.libraries": { - "name": "libraries", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "icon": { - "name": "icon", - "type": "varchar(100)", - "primaryKey": false, - "notNull": false - }, - "scan_mode": { - "name": "scan_mode", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'auto'" - }, - "poll_interval_seconds": { - "name": "poll_interval_seconds", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 300 - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.library_folders": { - "name": "library_folders", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "path": { - "name": "path", - "type": "varchar(4096)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "library_folders_library_id_libraries_id_fk": { - "name": "library_folders_library_id_libraries_id_fk", - "tableFrom": "library_folders", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.authors": { - "name": "authors", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": true - }, - "sort_name": { - "name": "sort_name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_authors": { - "name": "book_authors", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "author_id": { - "name": "author_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "display_order": { - "name": "display_order", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - } - }, - "indexes": {}, - "foreignKeys": { - "book_authors_book_id_books_id_fk": { - "name": "book_authors_book_id_books_id_fk", - "tableFrom": "book_authors", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_authors_author_id_authors_id_fk": { - "name": "book_authors_author_id_authors_id_fk", - "tableFrom": "book_authors", - "tableTo": "authors", - "columnsFrom": [ - "author_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "book_authors_book_id_author_id_pk": { - "name": "book_authors_book_id_author_id_pk", - "columns": [ - "book_id", - "author_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_metadata": { - "name": "book_metadata", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": true, - "notNull": true - }, - "title": { - "name": "title", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "subtitle": { - "name": "subtitle", - "type": "varchar(1000)", - "primaryKey": false, - "notNull": false - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "isbn10": { - "name": "isbn10", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "isbn13": { - "name": "isbn13", - "type": "varchar(13)", - "primaryKey": false, - "notNull": false - }, - "publisher": { - "name": "publisher", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "published_year": { - "name": "published_year", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "language": { - "name": "language", - "type": "varchar(10)", - "primaryKey": false, - "notNull": false - }, - "page_count": { - "name": "page_count", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "series_name": { - "name": "series_name", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "series_index": { - "name": "series_index", - "type": "real", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "book_metadata_book_id_books_id_fk": { - "name": "book_metadata_book_id_books_id_fk", - "tableFrom": "book_metadata", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.book_tags": { - "name": "book_tags", - "schema": "", - "columns": { - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "tag_id": { - "name": "tag_id", - "type": "integer", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "book_tags_book_id_books_id_fk": { - "name": "book_tags_book_id_books_id_fk", - "tableFrom": "book_tags", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "book_tags_tag_id_tags_id_fk": { - "name": "book_tags_tag_id_tags_id_fk", - "tableFrom": "book_tags", - "tableTo": "tags", - "columnsFrom": [ - "tag_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "book_tags_book_id_tag_id_pk": { - "name": "book_tags_book_id_tag_id_pk", - "columns": [ - "book_id", - "tag_id" - ] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.tags": { - "name": "tags", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "varchar(200)", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "tags_name_unique": { - "name": "tags_name_unique", - "nullsNotDistinct": false, - "columns": [ - "name" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.scan_jobs": { - "name": "scan_jobs", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "library_id": { - "name": "library_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'running'" - }, - "triggered_by": { - "name": "triggered_by", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true - }, - "added_count": { - "name": "added_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "updated_count": { - "name": "updated_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "missing_count": { - "name": "missing_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "error_message": { - "name": "error_message", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "started_at": { - "name": "started_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "completed_at": { - "name": "completed_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "scan_jobs_library_id_libraries_id_fk": { - "name": "scan_jobs_library_id_libraries_id_fk", - "tableFrom": "scan_jobs", - "tableTo": "libraries", - "columnsFrom": [ - "library_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.annotations": { - "name": "annotations", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "cfi": { - "name": "cfi", - "type": "varchar(2000)", - "primaryKey": false, - "notNull": true - }, - "text": { - "name": "text", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "color": { - "name": "color", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'yellow'" - }, - "style": { - "name": "style", - "type": "varchar(20)", - "primaryKey": false, - "notNull": true, - "default": "'highlight'" - }, - "note": { - "name": "note", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "chapter_title": { - "name": "chapter_title", - "type": "varchar(500)", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "annotations_book_id_books_id_fk": { - "name": "annotations_book_id_books_id_fk", - "tableFrom": "annotations", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.bookmarks": { - "name": "bookmarks", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "book_id": { - "name": "book_id", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "cfi": { - "name": "cfi", - "type": "varchar(2000)", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "varchar(500)", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "bookmarks_book_id_books_id_fk": { - "name": "bookmarks_book_id_books_id_fk", - "tableFrom": "bookmarks", - "tableTo": "books", - "columnsFrom": [ - "book_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.reading_progress": { - "name": "reading_progress", - "schema": "", - "columns": { - "book_file_id": { - "name": "book_file_id", - "type": "integer", - "primaryKey": true, - "notNull": true - }, - "percentage": { - "name": "percentage", - "type": "real", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "cfi": { - "name": "cfi", - "type": "varchar(2000)", - "primaryKey": false, - "notNull": false - }, - "page_number": { - "name": "page_number", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": { - "reading_progress_book_file_id_book_files_id_fk": { - "name": "reading_progress_book_file_id_book_files_id_fk", - "tableFrom": "reading_progress", - "tableTo": "book_files", - "columnsFrom": [ - "book_file_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/server/src/db/migrations/meta/_journal.json b/server/src/db/migrations/meta/_journal.json index 6b27a8382..fefeffca3 100644 --- a/server/src/db/migrations/meta/_journal.json +++ b/server/src/db/migrations/meta/_journal.json @@ -5,30 +5,9 @@ { "idx": 0, "version": "7", - "when": 1767889071823, + "when": 1767927442636, "tag": "0000_init_schema", "breakpoints": true - }, - { - "idx": 1, - "version": "7", - "when": 1767889071823, - "tag": "0001_lethal_pixie", - "breakpoints": true - }, - { - "idx": 2, - "version": "7", - "when": 1767889071823, - "tag": "0002_powerful_thundra", - "breakpoints": true - }, - { - "idx": 3, - "version": "7", - "when": 1767889071823, - "tag": "0003_reading_progress_by_file", - "breakpoints": true } ] } diff --git a/server/src/db/schema/auth.ts b/server/src/db/schema/auth.ts new file mode 100644 index 000000000..00cde8c21 --- /dev/null +++ b/server/src/db/schema/auth.ts @@ -0,0 +1,145 @@ +import { + boolean, + index, + integer, + jsonb, + pgEnum, + pgTable, + primaryKey, + serial, + text, + timestamp, + varchar, +} from 'drizzle-orm/pg-core'; + +import { libraries } from './libraries'; + +export const users = pgTable('users', { + id: serial('id').primaryKey(), + username: varchar('username', { length: 100 }).notNull().unique(), + name: varchar('name', { length: 255 }).notNull(), + email: varchar('email', { length: 255 }).unique(), + passwordHash: varchar('password_hash', { length: 255 }).notNull(), + active: boolean('active').notNull().default(true), + isDefaultPassword: boolean('is_default_password').notNull().default(false), + settings: jsonb('settings').notNull().default({}), + createdAt: timestamp('created_at').notNull().defaultNow(), + updatedAt: timestamp('updated_at') + .notNull() + .defaultNow() + .$onUpdateFn(() => new Date()), +}); + +export const permissions = pgTable('permissions', { + id: serial('id').primaryKey(), + name: varchar('name', { length: 100 }).notNull().unique(), + description: text('description'), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const roles = pgTable('roles', { + id: serial('id').primaryKey(), + name: varchar('name', { length: 100 }).notNull().unique(), + description: text('description'), + isSuperuser: boolean('is_superuser').notNull().default(false), + isSystem: boolean('is_system').notNull().default(false), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export const rolePermissions = pgTable( + 'role_permissions', + { + roleId: integer('role_id') + .notNull() + .references(() => roles.id, { onDelete: 'cascade' }), + permissionId: integer('permission_id') + .notNull() + .references(() => permissions.id, { onDelete: 'cascade' }), + }, + (t) => [primaryKey({ columns: [t.roleId, t.permissionId] })], +); + +export const userRoles = pgTable( + 'user_roles', + { + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + roleId: integer('role_id') + .notNull() + .references(() => roles.id, { onDelete: 'cascade' }), + }, + (t) => [primaryKey({ columns: [t.userId, t.roleId] })], +); + +export const libraryAccessLevelEnum = pgEnum('library_access_level', [ + 'viewer', + 'editor', + 'owner', +]); + +export const userLibraryAccess = pgTable( + 'user_library_access', + { + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + libraryId: integer('library_id') + .notNull() + .references(() => libraries.id, { onDelete: 'cascade' }), + accessLevel: libraryAccessLevelEnum('access_level').notNull().default('viewer'), + }, + (t) => [primaryKey({ columns: [t.userId, t.libraryId] })], +); + +export const refreshTokens = pgTable( + 'refresh_tokens', + { + id: serial('id').primaryKey(), + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + tokenHash: varchar('token_hash', { length: 255 }).notNull().unique(), + expiresAt: timestamp('expires_at').notNull(), + revokedAt: timestamp('revoked_at'), + createdAt: timestamp('created_at').notNull().defaultNow(), + }, + (t) => [index('refresh_tokens_user_id_idx').on(t.userId)], +); + +export const passwordResetTokens = pgTable( + 'password_reset_tokens', + { + id: serial('id').primaryKey(), + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + tokenHash: varchar('token_hash', { length: 255 }).notNull().unique(), + expiresAt: timestamp('expires_at').notNull(), + usedAt: timestamp('used_at'), + createdAt: timestamp('created_at').notNull().defaultNow(), + }, + (t) => [index('password_reset_tokens_user_id_idx').on(t.userId)], +); + +export const appSettings = pgTable('app_settings', { + id: serial('id').primaryKey(), + key: varchar('key', { length: 100 }).notNull().unique(), + value: text('value').notNull(), + updatedAt: timestamp('updated_at') + .notNull() + .defaultNow() + .$onUpdateFn(() => new Date()), +}); + +export type User = typeof users.$inferSelect; +export type NewUser = typeof users.$inferInsert; + +export type Permission = typeof permissions.$inferSelect; +export type NewPermission = typeof permissions.$inferInsert; + +export type Role = typeof roles.$inferSelect; +export type NewRole = typeof roles.$inferInsert; + +export type RefreshToken = typeof refreshTokens.$inferSelect; +export type AppSetting = typeof appSettings.$inferSelect; diff --git a/server/src/db/schema/index.ts b/server/src/db/schema/index.ts index 757acbe37..1cb8bbb28 100644 --- a/server/src/db/schema/index.ts +++ b/server/src/db/schema/index.ts @@ -1,3 +1,4 @@ +export * from './auth'; export * from './libraries'; export * from './books'; export * from './metadata'; diff --git a/server/src/db/schema/reader.ts b/server/src/db/schema/reader.ts index 0212013e7..0cbad2961 100644 --- a/server/src/db/schema/reader.ts +++ b/server/src/db/schema/reader.ts @@ -1,49 +1,84 @@ -import { integer, pgTable, real, serial, text, timestamp, varchar } from 'drizzle-orm/pg-core'; +import { + index, + integer, + pgTable, + primaryKey, + real, + serial, + text, + timestamp, + varchar, +} from 'drizzle-orm/pg-core'; import { bookFiles, books } from './books'; +import { users } from './auth'; -export const readingProgress = pgTable('reading_progress', { - bookFileId: integer('book_file_id') - .primaryKey() - .references(() => bookFiles.id, { onDelete: 'cascade' }), - percentage: real('percentage').notNull().default(0), - // EPUB: CFI string pinpoints exact location - cfi: varchar('cfi', { length: 2000 }), - // PDF / CBX / CBR: zero-based page index - pageNumber: integer('page_number'), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); +export const readingProgress = pgTable( + 'reading_progress', + { + bookFileId: integer('book_file_id') + .notNull() + .references(() => bookFiles.id, { onDelete: 'cascade' }), + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + percentage: real('percentage').notNull().default(0), + // EPUB: CFI string pinpoints exact location + cfi: varchar('cfi', { length: 2000 }), + // PDF / CBX / CBR: zero-based page index + pageNumber: integer('page_number'), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + }, + (t) => [ + primaryKey({ columns: [t.bookFileId, t.userId] }), + index('reading_progress_user_id_idx').on(t.userId), + ], +); export type ReadingProgress = typeof readingProgress.$inferSelect; export type NewReadingProgress = typeof readingProgress.$inferInsert; -export const bookmarks = pgTable('bookmarks', { - id: serial('id').primaryKey(), - bookId: integer('book_id') - .notNull() - .references(() => books.id, { onDelete: 'cascade' }), - cfi: varchar('cfi', { length: 2000 }).notNull(), - title: varchar('title', { length: 500 }).notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), -}); +export const bookmarks = pgTable( + 'bookmarks', + { + id: serial('id').primaryKey(), + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + bookId: integer('book_id') + .notNull() + .references(() => books.id, { onDelete: 'cascade' }), + cfi: varchar('cfi', { length: 2000 }).notNull(), + title: varchar('title', { length: 500 }).notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), + }, + (t) => [index('bookmarks_user_id_idx').on(t.userId)], +); export type BookmarkRow = typeof bookmarks.$inferSelect; export type NewBookmark = typeof bookmarks.$inferInsert; -export const annotations = pgTable('annotations', { - id: serial('id').primaryKey(), - bookId: integer('book_id') - .notNull() - .references(() => books.id, { onDelete: 'cascade' }), - cfi: varchar('cfi', { length: 2000 }).notNull(), - text: text('text').notNull(), - color: varchar('color', { length: 20 }).notNull().default('yellow'), - style: varchar('style', { length: 20 }).notNull().default('highlight'), - note: text('note'), - chapterTitle: varchar('chapter_title', { length: 500 }), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); +export const annotations = pgTable( + 'annotations', + { + id: serial('id').primaryKey(), + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + bookId: integer('book_id') + .notNull() + .references(() => books.id, { onDelete: 'cascade' }), + cfi: varchar('cfi', { length: 2000 }).notNull(), + text: text('text').notNull(), + color: varchar('color', { length: 20 }).notNull().default('yellow'), + style: varchar('style', { length: 20 }).notNull().default('highlight'), + note: text('note'), + chapterTitle: varchar('chapter_title', { length: 500 }), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + }, + (t) => [index('annotations_user_id_idx').on(t.userId)], +); export type AnnotationRow = typeof annotations.$inferSelect; export type NewAnnotation = typeof annotations.$inferInsert; diff --git a/server/src/modules/annotation/annotation.controller.ts b/server/src/modules/annotation/annotation.controller.ts index 1967af2b5..670b1e908 100644 --- a/server/src/modules/annotation/annotation.controller.ts +++ b/server/src/modules/annotation/annotation.controller.ts @@ -15,7 +15,8 @@ export class AnnotationController { @Post() createAnnotation(@Param('bookId', ParseIntPipe) bookId: number, @Body() dto: CreateAnnotationDto) { - return this.annotationService.createAnnotation(bookId, dto); + // TODO: replace with real userId from auth guard once auth is wired up + return this.annotationService.createAnnotation(1, bookId, dto); } @Patch(':annotationId') diff --git a/server/src/modules/annotation/annotation.service.ts b/server/src/modules/annotation/annotation.service.ts index 900a73f42..a452e5c9e 100644 --- a/server/src/modules/annotation/annotation.service.ts +++ b/server/src/modules/annotation/annotation.service.ts @@ -12,8 +12,9 @@ export class AnnotationService { return this.annotationRepo.findByBookId(bookId); } - async createAnnotation(bookId: number, dto: CreateAnnotationDto) { + async createAnnotation(userId: number, bookId: number, dto: CreateAnnotationDto) { return this.annotationRepo.create({ + userId, bookId, cfi: dto.cfi, text: dto.text, diff --git a/server/src/modules/book/book.controller.ts b/server/src/modules/book/book.controller.ts index 1c73b2bc5..1f2d9484c 100644 --- a/server/src/modules/book/book.controller.ts +++ b/server/src/modules/book/book.controller.ts @@ -68,12 +68,14 @@ export class BookController { @Get('files/:fileId/progress') async getFileProgress(@Param('fileId', ParseIntPipe) fileId: number) { - return (await this.bookService.getProgress(fileId)) ?? { cfi: null, pageNumber: null, percentage: 0 }; + // TODO: replace with real userId from auth guard once auth is wired up + return (await this.bookService.getProgress(1, fileId)) ?? { cfi: null, pageNumber: null, percentage: 0 }; } @Post('files/:fileId/progress') async saveFileProgress(@Param('fileId', ParseIntPipe) fileId: number, @Body() dto: SaveProgressDto) { - await this.bookService.saveProgress(fileId, dto.cfi, dto.pageNumber, dto.percentage); + // TODO: replace with real userId from auth guard once auth is wired up + await this.bookService.saveProgress(1, fileId, dto.cfi, dto.pageNumber, dto.percentage); } @Get(':id') diff --git a/server/src/modules/book/book.repository.ts b/server/src/modules/book/book.repository.ts index 553d5d8ae..998477dc3 100644 --- a/server/src/modules/book/book.repository.ts +++ b/server/src/modules/book/book.repository.ts @@ -98,17 +98,21 @@ export class BookRepository { return file ?? null; } - async findProgress(fileId: number) { - const [row] = await this.db.select().from(readingProgress).where(eq(readingProgress.bookFileId, fileId)).limit(1); + async findProgress(userId: number, fileId: number) { + const [row] = await this.db + .select() + .from(readingProgress) + .where(and(eq(readingProgress.bookFileId, fileId), eq(readingProgress.userId, userId))) + .limit(1); return row ?? null; } - async upsertProgress(fileId: number, cfi: string | null, pageNumber: number | null, percentage: number) { + async upsertProgress(userId: number, fileId: number, cfi: string | null, pageNumber: number | null, percentage: number) { await this.db .insert(readingProgress) - .values({ bookFileId: fileId, cfi, pageNumber, percentage }) + .values({ userId, bookFileId: fileId, cfi, pageNumber, percentage }) .onConflictDoUpdate({ - target: readingProgress.bookFileId, + target: [readingProgress.bookFileId, readingProgress.userId], set: { cfi, pageNumber, percentage, updatedAt: sql`now()` }, }); } diff --git a/server/src/modules/book/book.service.ts b/server/src/modules/book/book.service.ts index 3b4398539..87d48dfad 100644 --- a/server/src/modules/book/book.service.ts +++ b/server/src/modules/book/book.service.ts @@ -74,12 +74,12 @@ export class BookService { return { path: file.absolutePath, size, format: file.format ?? 'unknown' }; } - async getProgress(fileId: number) { - return this.bookRepo.findProgress(fileId); + async getProgress(userId: number, fileId: number) { + return this.bookRepo.findProgress(userId, fileId); } - async saveProgress(fileId: number, cfi: string | null | undefined, pageNumber: number | null | undefined, percentage: number) { - await this.bookRepo.upsertProgress(fileId, cfi ?? null, pageNumber ?? null, percentage); + async saveProgress(userId: number, fileId: number, cfi: string | null | undefined, pageNumber: number | null | undefined, percentage: number) { + await this.bookRepo.upsertProgress(userId, fileId, cfi ?? null, pageNumber ?? null, percentage); } async getDetail(id: number): Promise { diff --git a/server/src/modules/bookmark/bookmark.controller.ts b/server/src/modules/bookmark/bookmark.controller.ts index be2ca60b7..1271a1856 100644 --- a/server/src/modules/bookmark/bookmark.controller.ts +++ b/server/src/modules/bookmark/bookmark.controller.ts @@ -14,7 +14,8 @@ export class BookmarkController { @Post() createBookmark(@Param('bookId', ParseIntPipe) bookId: number, @Body() dto: CreateBookmarkDto) { - return this.bookmarkService.createBookmark(bookId, dto); + // TODO: replace with real userId from auth guard once auth is wired up + return this.bookmarkService.createBookmark(1, bookId, dto); } @Delete(':bookmarkId') diff --git a/server/src/modules/bookmark/bookmark.repository.ts b/server/src/modules/bookmark/bookmark.repository.ts index 0f2313c87..425e0cc6f 100644 --- a/server/src/modules/bookmark/bookmark.repository.ts +++ b/server/src/modules/bookmark/bookmark.repository.ts @@ -16,8 +16,8 @@ export class BookmarkRepository { return this.db.select().from(bookmarks).where(eq(bookmarks.bookId, bookId)); } - async create(bookId: number, cfi: string, title: string) { - const [row] = await this.db.insert(bookmarks).values({ bookId, cfi, title }).returning(); + async create(userId: number, bookId: number, cfi: string, title: string) { + const [row] = await this.db.insert(bookmarks).values({ userId, bookId, cfi, title }).returning(); return row; } diff --git a/server/src/modules/bookmark/bookmark.service.ts b/server/src/modules/bookmark/bookmark.service.ts index 58cd32375..dde3119ff 100644 --- a/server/src/modules/bookmark/bookmark.service.ts +++ b/server/src/modules/bookmark/bookmark.service.ts @@ -11,8 +11,8 @@ export class BookmarkService { return this.bookmarkRepo.findByBookId(bookId); } - async createBookmark(bookId: number, dto: CreateBookmarkDto) { - return this.bookmarkRepo.create(bookId, dto.cfi, dto.title); + async createBookmark(userId: number, bookId: number, dto: CreateBookmarkDto) { + return this.bookmarkRepo.create(userId, bookId, dto.cfi, dto.title); } async deleteBookmark(bookId: number, bookmarkId: number) {