diff --git a/server/src/db/migrations/0118_add_kobo_bookmark_links.sql b/server/src/db/migrations/0118_add_kobo_bookmark_links.sql new file mode 100644 index 000000000..2ac726779 --- /dev/null +++ b/server/src/db/migrations/0118_add_kobo_bookmark_links.sql @@ -0,0 +1,21 @@ +CREATE TABLE "kobo_bookmark_links" ( + "id" serial PRIMARY KEY NOT NULL, + "bookmark_id" integer NOT NULL, + "user_id" integer NOT NULL, + "device_id" integer NOT NULL, + "book_id" integer NOT NULL, + "external_key" varchar(64) NOT NULL, + "location" jsonb NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "bookmarks" DROP CONSTRAINT "bookmarks_origin_chk";--> statement-breakpoint +ALTER TABLE "kobo_bookmark_links" ADD CONSTRAINT "kobo_bookmark_links_bookmark_id_bookmarks_id_fk" FOREIGN KEY ("bookmark_id") REFERENCES "public"."bookmarks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "kobo_bookmark_links" ADD CONSTRAINT "kobo_bookmark_links_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "kobo_bookmark_links" ADD CONSTRAINT "kobo_bookmark_links_book_id_books_id_fk" FOREIGN KEY ("book_id") REFERENCES "public"."books"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "kobo_bookmark_links" ADD CONSTRAINT "kobo_bookmark_links_owner_fk" FOREIGN KEY ("device_id","user_id") REFERENCES "public"."kobo_devices"("id","user_id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "kobo_bookmark_links_bookmark_device_uidx" ON "kobo_bookmark_links" USING btree ("bookmark_id","device_id");--> statement-breakpoint +CREATE UNIQUE INDEX "kobo_bookmark_links_device_key_uidx" ON "kobo_bookmark_links" USING btree ("device_id","external_key");--> statement-breakpoint +CREATE INDEX "kobo_bookmark_links_user_device_book_idx" ON "kobo_bookmark_links" USING btree ("user_id","device_id","book_id");--> statement-breakpoint +ALTER TABLE "bookmarks" ADD CONSTRAINT "bookmarks_origin_chk" CHECK ("bookmarks"."origin" in ('web', 'koreader', 'kobo')); \ No newline at end of file diff --git a/server/src/db/migrations/meta/0118_snapshot.json b/server/src/db/migrations/meta/0118_snapshot.json new file mode 100644 index 000000000..77ac55c15 --- /dev/null +++ b/server/src/db/migrations/meta/0118_snapshot.json @@ -0,0 +1,24031 @@ +{ + "id": "630474b7-03ba-44de-a26b-98f1dbc1f744", + "prevId": "aa4f307a-a064-41cc-890c-a1c1b71b786b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.audit_log": { + "name": "audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "actor_username": { + "name": "actor_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "resource": { + "name": "resource", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "resource_id": { + "name": "resource_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "resource_label": { + "name": "resource_label", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_audit_user_id": { + "name": "idx_audit_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_actor_username": { + "name": "idx_audit_actor_username", + "columns": [ + { + "expression": "actor_username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_actor_username_trgm": { + "name": "idx_audit_actor_username_trgm", + "columns": [ + { + "expression": "actor_username", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_audit_resource": { + "name": "idx_audit_resource", + "columns": [ + { + "expression": "resource", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_resource_id": { + "name": "idx_audit_resource_id", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_resource_trgm": { + "name": "idx_audit_resource_trgm", + "columns": [ + { + "expression": "resource", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_audit_action": { + "name": "idx_audit_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_action_trgm": { + "name": "idx_audit_action_trgm", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_audit_description_trgm": { + "name": "idx_audit_description_trgm", + "columns": [ + { + "expression": "description", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_audit_resource_label_trgm": { + "name": "idx_audit_resource_label_trgm", + "columns": [ + { + "expression": "resource_label", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_audit_meta_text_trgm": { + "name": "idx_audit_meta_text_trgm", + "columns": [ + { + "expression": "(\"meta\"::text) gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_audit_ip": { + "name": "idx_audit_ip", + "columns": [ + { + "expression": "ip", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_created_at": { + "name": "idx_audit_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_audit_reading_insights_access": { + "name": "idx_audit_reading_insights_access", + "columns": [ + { + "expression": "resource", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_log_user_id_users_id_fk": { + "name": "audit_log_user_id_users_id_fk", + "tableFrom": "audit_log", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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 with time zone", + "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.auth_sessions": { + "name": "auth_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_version": { + "name": "token_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "authentication_method": { + "name": "authentication_method", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "client_kind": { + "name": "client_kind", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'web'" + }, + "device_label": { + "name": "device_label", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_sessions_user_id_idx": { + "name": "auth_sessions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_sessions_expires_at_idx": { + "name": "auth_sessions_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_sessions_user_id_users_id_fk": { + "name": "auth_sessions_user_id_users_id_fk", + "tableFrom": "auth_sessions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "auth_sessions_client_kind_chk": { + "name": "auth_sessions_client_kind_chk", + "value": "\"auth_sessions\".\"client_kind\" in ('web', 'native')" + }, + "auth_sessions_authentication_method_chk": { + "name": "auth_sessions_authentication_method_chk", + "value": "\"auth_sessions\".\"authentication_method\" in ('password', 'oidc', 'magic_link', 'setup', 'legacy')" + } + }, + "isRLSEnabled": false + }, + "public.magic_access_tokens": { + "name": "magic_access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "label": { + "name": "label", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "raw_token": { + "name": "raw_token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "use_count": { + "name": "use_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "magic_access_tokens_user_id_idx": { + "name": "magic_access_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "magic_access_tokens_user_id_users_id_fk": { + "name": "magic_access_tokens_user_id_users_id_fk", + "tableFrom": "magic_access_tokens", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "magic_access_tokens_created_by_users_id_fk": { + "name": "magic_access_tokens_created_by_users_id_fk", + "tableFrom": "magic_access_tokens", + "tableTo": "users", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "magic_access_tokens_token_hash_unique": { + "name": "magic_access_tokens_token_hash_unique", + "nullsNotDistinct": false, + "columns": ["token_hash"] + } + }, + "policies": {}, + "checkConstraints": { + "magic_access_tokens_use_count_nonneg_chk": { + "name": "magic_access_tokens_use_count_nonneg_chk", + "value": "\"magic_access_tokens\".\"use_count\" >= 0" + } + }, + "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 with time zone", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "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": {} + }, + "password_reset_tokens_expires_at_idx": { + "name": "password_reset_tokens_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "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": { + "password_reset_tokens_expires_after_created_chk": { + "name": "password_reset_tokens_expires_after_created_chk", + "value": "\"password_reset_tokens\".\"expires_at\" > \"password_reset_tokens\".\"created_at\"" + } + }, + "isRLSEnabled": false + }, + "public.refresh_tokens": { + "name": "refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_hash": { + "name": "token_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "authentication_method": { + "name": "authentication_method", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'legacy'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "replaced_by_token_hash": { + "name": "replaced_by_token_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "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": {} + }, + "refresh_tokens_session_id_idx": { + "name": "refresh_tokens_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "refresh_tokens_expires_at_idx": { + "name": "refresh_tokens_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "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" + }, + "refresh_tokens_session_id_auth_sessions_id_fk": { + "name": "refresh_tokens_session_id_auth_sessions_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "auth_sessions", + "columnsFrom": ["session_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": { + "refresh_tokens_expires_after_created_chk": { + "name": "refresh_tokens_expires_after_created_chk", + "value": "\"refresh_tokens\".\"expires_at\" > \"refresh_tokens\".\"created_at\"" + }, + "refresh_tokens_authentication_method_chk": { + "name": "refresh_tokens_authentication_method_chk", + "value": "\"refresh_tokens\".\"authentication_method\" in ('password', 'oidc', 'magic_link', 'setup', 'legacy')" + } + }, + "isRLSEnabled": false + }, + "public.user_content_filter_genres": { + "name": "user_content_filter_genres", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "filter_type": { + "name": "filter_type", + "type": "content_filter_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "genre_id": { + "name": "genre_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_content_filter_genres_genre_id_idx": { + "name": "user_content_filter_genres_genre_id_idx", + "columns": [ + { + "expression": "genre_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_content_filter_genres_user_id_users_id_fk": { + "name": "user_content_filter_genres_user_id_users_id_fk", + "tableFrom": "user_content_filter_genres", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_content_filter_genres_genre_id_genres_id_fk": { + "name": "user_content_filter_genres_genre_id_genres_id_fk", + "tableFrom": "user_content_filter_genres", + "tableTo": "genres", + "columnsFrom": ["genre_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_content_filter_genres_user_id_filter_type_genre_id_pk": { + "name": "user_content_filter_genres_user_id_filter_type_genre_id_pk", + "columns": ["user_id", "filter_type", "genre_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_content_filter_tags": { + "name": "user_content_filter_tags", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "filter_type": { + "name": "filter_type", + "type": "content_filter_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "tag_id": { + "name": "tag_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_content_filter_tags_tag_id_idx": { + "name": "user_content_filter_tags_tag_id_idx", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_content_filter_tags_user_id_users_id_fk": { + "name": "user_content_filter_tags_user_id_users_id_fk", + "tableFrom": "user_content_filter_tags", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_content_filter_tags_tag_id_tags_id_fk": { + "name": "user_content_filter_tags_tag_id_tags_id_fk", + "tableFrom": "user_content_filter_tags", + "tableTo": "tags", + "columnsFrom": ["tag_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_content_filter_tags_user_id_filter_type_tag_id_pk": { + "name": "user_content_filter_tags_user_id_filter_type_tag_id_pk", + "columns": ["user_id", "filter_type", "tag_id"] + } + }, + "uniqueConstraints": {}, + "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": { + "user_library_access_library_user_idx": { + "name": "user_library_access_library_user_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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_permissions": { + "name": "user_permissions", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "permission_name": { + "name": "permission_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_permissions_user_id_users_id_fk": { + "name": "user_permissions_user_id_users_id_fk", + "tableFrom": "user_permissions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_permissions_user_id_permission_name_pk": { + "name": "user_permissions_user_id_permission_name_pk", + "columns": ["user_id", "permission_name"] + } + }, + "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_superuser": { + "name": "is_superuser", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default_password": { + "name": "is_default_password", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "token_version": { + "name": "token_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "failed_login_attempts": { + "name": "failed_login_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "oidc_subject": { + "name": "oidc_subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oidc_issuer": { + "name": "oidc_issuer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_source": { + "name": "avatar_source", + "type": "user_avatar_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "avatar_version": { + "name": "avatar_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "provisioning_method": { + "name": "provisioning_method", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'local'" + }, + "last_login_at": { + "name": "last_login_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_authenticated_at": { + "name": "last_authenticated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "see_own_requested_books": { + "name": "see_own_requested_books", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "reading_insights_sharing_level": { + "name": "reading_insights_sharing_level", + "type": "reading_insights_sharing_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "reading_insights_consented_at": { + "name": "reading_insights_consented_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_username_lower_uidx": { + "name": "users_username_lower_uidx", + "columns": [ + { + "expression": "lower(\"username\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_email_lower_uidx": { + "name": "users_email_lower_uidx", + "columns": [ + { + "expression": "lower(\"email\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"users\".\"email\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_oidc_subject_issuer_uidx": { + "name": "users_oidc_subject_issuer_uidx", + "columns": [ + { + "expression": "oidc_subject", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "oidc_issuer", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"users\".\"oidc_subject\" is not null and \"users\".\"oidc_issuer\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_last_authenticated_at_idx": { + "name": "users_last_authenticated_at_idx", + "columns": [ + { + "expression": "last_authenticated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": { + "users_provisioning_method_chk": { + "name": "users_provisioning_method_chk", + "value": "\"users\".\"provisioning_method\" in ('local', 'manual', 'oidc', 'shared')" + }, + "users_token_version_nonnegative_chk": { + "name": "users_token_version_nonnegative_chk", + "value": "\"users\".\"token_version\" >= 0" + }, + "users_failed_login_attempts_nonnegative_chk": { + "name": "users_failed_login_attempts_nonnegative_chk", + "value": "\"users\".\"failed_login_attempts\" >= 0" + }, + "users_avatar_version_nonnegative_chk": { + "name": "users_avatar_version_nonnegative_chk", + "value": "\"users\".\"avatar_version\" >= 0" + }, + "users_reading_insights_consent_chk": { + "name": "users_reading_insights_consent_chk", + "value": "(\"users\".\"reading_insights_sharing_level\" = 'private' and \"users\".\"reading_insights_consented_at\" is null) or (\"users\".\"reading_insights_sharing_level\" <> 'private' and \"users\".\"reading_insights_consented_at\" is not null)" + } + }, + "isRLSEnabled": false + }, + "public.comic_metadata": { + "name": "comic_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "issue_number": { + "name": "issue_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volume_name": { + "name": "volume_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pencillers": { + "name": "pencillers", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "inkers": { + "name": "inkers", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "colorists": { + "name": "colorists", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "letterers": { + "name": "letterers", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "cover_artists": { + "name": "cover_artists", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "characters": { + "name": "characters", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "teams": { + "name": "teams", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "locations": { + "name": "locations", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "story_arcs": { + "name": "story_arcs", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "comic_metadata_book_id_books_id_fk": { + "name": "comic_metadata_book_id_books_id_fk", + "tableFrom": "comic_metadata", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "comic_metadata_book_id_unique": { + "name": "comic_metadata_book_id_unique", + "nullsNotDistinct": false, + "columns": ["book_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_custom_metadata_values": { + "name": "book_custom_metadata_values", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "field_id": { + "name": "field_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "value_text": { + "name": "value_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "value_number": { + "name": "value_number", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "value_date": { + "name": "value_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "value_boolean": { + "name": "value_boolean", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_custom_metadata_values_field_idx": { + "name": "book_custom_metadata_values_field_idx", + "columns": [ + { + "expression": "field_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_custom_metadata_values_book_id_books_id_fk": { + "name": "book_custom_metadata_values_book_id_books_id_fk", + "tableFrom": "book_custom_metadata_values", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_custom_metadata_values_field_id_custom_metadata_fields_id_fk": { + "name": "book_custom_metadata_values_field_id_custom_metadata_fields_id_fk", + "tableFrom": "book_custom_metadata_values", + "tableTo": "custom_metadata_fields", + "columnsFrom": ["field_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_custom_metadata_values_book_id_field_id_pk": { + "name": "book_custom_metadata_values_book_id_field_id_pk", + "columns": ["book_id", "field_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_custom_metadata_values_single_value_chk": { + "name": "book_custom_metadata_values_single_value_chk", + "value": "(\n (\"book_custom_metadata_values\".\"value_text\" is not null)::int +\n (\"book_custom_metadata_values\".\"value_number\" is not null)::int +\n (\"book_custom_metadata_values\".\"value_date\" is not null)::int +\n (\"book_custom_metadata_values\".\"value_boolean\" is not null)::int\n ) <= 1" + } + }, + "isRLSEnabled": false + }, + "public.custom_metadata_fields": { + "name": "custom_metadata_fields", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_metadata_fields_key_uidx": { + "name": "custom_metadata_fields_key_uidx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_metadata_fields_active_order_idx": { + "name": "custom_metadata_fields_active_order_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "label", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "custom_metadata_fields_type_chk": { + "name": "custom_metadata_fields_type_chk", + "value": "\"custom_metadata_fields\".\"type\" in ('text', 'url', 'number', 'date', 'boolean')" + }, + "custom_metadata_fields_key_format_chk": { + "name": "custom_metadata_fields_key_format_chk", + "value": "\"custom_metadata_fields\".\"key\" ~ '^[a-z0-9][a-z0-9_]{0,99}$'" + }, + "custom_metadata_fields_label_not_blank_chk": { + "name": "custom_metadata_fields_label_not_blank_chk", + "value": "length(btrim(\"custom_metadata_fields\".\"label\")) > 0" + }, + "custom_metadata_fields_display_order_nonnegative_chk": { + "name": "custom_metadata_fields_display_order_nonnegative_chk", + "value": "\"custom_metadata_fields\".\"display_order\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.custom_metadata_library_fields": { + "name": "custom_metadata_library_fields", + "schema": "", + "columns": { + "field_id": { + "name": "field_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "library_id": { + "name": "library_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_metadata_library_fields_field_idx": { + "name": "custom_metadata_library_fields_field_idx", + "columns": [ + { + "expression": "field_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_metadata_library_fields_library_order_idx": { + "name": "custom_metadata_library_fields_library_order_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "field_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "custom_metadata_library_fields_field_id_custom_metadata_fields_id_fk": { + "name": "custom_metadata_library_fields_field_id_custom_metadata_fields_id_fk", + "tableFrom": "custom_metadata_library_fields", + "tableTo": "custom_metadata_fields", + "columnsFrom": ["field_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "custom_metadata_library_fields_library_id_libraries_id_fk": { + "name": "custom_metadata_library_fields_library_id_libraries_id_fk", + "tableFrom": "custom_metadata_library_fields", + "tableTo": "libraries", + "columnsFrom": ["library_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "custom_metadata_library_fields_library_id_field_id_pk": { + "name": "custom_metadata_library_fields_library_id_field_id_pk", + "columns": ["library_id", "field_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "custom_metadata_library_fields_display_order_nonnegative_chk": { + "name": "custom_metadata_library_fields_display_order_nonnegative_chk", + "value": "\"custom_metadata_library_fields\".\"display_order\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.custom_icons": { + "name": "custom_icons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "stored_file_name": { + "name": "stored_file_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "file_hash": { + "name": "file_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_icons_slug_uidx": { + "name": "custom_icons_slug_uidx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "custom_icons_slug_chk": { + "name": "custom_icons_slug_chk", + "value": "\"custom_icons\".\"slug\" ~ '^[a-z0-9][a-z0-9-]*[a-z0-9]$' or \"custom_icons\".\"slug\" ~ '^[a-z0-9]$'" + }, + "custom_icons_file_size_positive_chk": { + "name": "custom_icons_file_size_positive_chk", + "value": "\"custom_icons\".\"file_size\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.migration_plan_artifacts": { + "name": "migration_plan_artifacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "profile_id": { + "name": "profile_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_snapshot_hash": { + "name": "source_snapshot_hash", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "profile_hash": { + "name": "profile_hash", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "plan_hash": { + "name": "plan_hash", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "source_data": { + "name": "source_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "migration_plan_artifacts_plan_hash_uidx": { + "name": "migration_plan_artifacts_plan_hash_uidx", + "columns": [ + { + "expression": "plan_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "migration_plan_artifacts_source_id_idx": { + "name": "migration_plan_artifacts_source_id_idx", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "migration_plan_artifacts_profile_id_idx": { + "name": "migration_plan_artifacts_profile_id_idx", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "migration_plan_artifacts_source_id_migration_sources_id_fk": { + "name": "migration_plan_artifacts_source_id_migration_sources_id_fk", + "tableFrom": "migration_plan_artifacts", + "tableTo": "migration_sources", + "columnsFrom": ["source_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_plan_artifacts_profile_id_migration_profiles_id_fk": { + "name": "migration_plan_artifacts_profile_id_migration_profiles_id_fk", + "tableFrom": "migration_plan_artifacts", + "tableTo": "migration_profiles", + "columnsFrom": ["profile_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_plan_artifacts_created_by_user_id_users_id_fk": { + "name": "migration_plan_artifacts_created_by_user_id_users_id_fk", + "tableFrom": "migration_plan_artifacts", + "tableTo": "users", + "columnsFrom": ["created_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "migration_plan_artifacts_profile_source_fk": { + "name": "migration_plan_artifacts_profile_source_fk", + "tableFrom": "migration_plan_artifacts", + "tableTo": "migration_profiles", + "columnsFrom": ["profile_id", "source_id"], + "columnsTo": ["id", "source_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "migration_plan_artifacts_id_source_profile_unique": { + "name": "migration_plan_artifacts_id_source_profile_unique", + "nullsNotDistinct": false, + "columns": ["id", "source_id", "profile_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_profiles": { + "name": "migration_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "user_mappings": { + "name": "user_mappings", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "path_mappings": { + "name": "path_mappings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "scope": { + "name": "scope", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "migration_profiles_source_name_version_uidx": { + "name": "migration_profiles_source_name_version_uidx", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "migration_profiles_source_id_idx": { + "name": "migration_profiles_source_id_idx", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "migration_profiles_source_id_migration_sources_id_fk": { + "name": "migration_profiles_source_id_migration_sources_id_fk", + "tableFrom": "migration_profiles", + "tableTo": "migration_sources", + "columnsFrom": ["source_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_profiles_created_by_user_id_users_id_fk": { + "name": "migration_profiles_created_by_user_id_users_id_fk", + "tableFrom": "migration_profiles", + "tableTo": "users", + "columnsFrom": ["created_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "migration_profiles_id_source_id_unique": { + "name": "migration_profiles_id_source_id_unique", + "nullsNotDistinct": false, + "columns": ["id", "source_id"] + } + }, + "policies": {}, + "checkConstraints": { + "migration_profiles_version_positive_chk": { + "name": "migration_profiles_version_positive_chk", + "value": "\"migration_profiles\".\"version\" >= 1" + } + }, + "isRLSEnabled": false + }, + "public.migration_run_metrics": { + "name": "migration_run_metrics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "stage": { + "name": "stage", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "processed": { + "name": "processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "imported": { + "name": "imported", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped": { + "name": "skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unresolved": { + "name": "unresolved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed": { + "name": "failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "migration_run_metrics_run_stage_entity_uidx": { + "name": "migration_run_metrics_run_stage_entity_uidx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stage", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "migration_run_metrics_run_id_idx": { + "name": "migration_run_metrics_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "migration_run_metrics_run_id_migration_runs_id_fk": { + "name": "migration_run_metrics_run_id_migration_runs_id_fk", + "tableFrom": "migration_run_metrics", + "tableTo": "migration_runs", + "columnsFrom": ["run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "migration_run_metrics_processed_nonnegative_chk": { + "name": "migration_run_metrics_processed_nonnegative_chk", + "value": "\"migration_run_metrics\".\"processed\" >= 0" + }, + "migration_run_metrics_imported_nonnegative_chk": { + "name": "migration_run_metrics_imported_nonnegative_chk", + "value": "\"migration_run_metrics\".\"imported\" >= 0" + }, + "migration_run_metrics_skipped_nonnegative_chk": { + "name": "migration_run_metrics_skipped_nonnegative_chk", + "value": "\"migration_run_metrics\".\"skipped\" >= 0" + }, + "migration_run_metrics_unresolved_nonnegative_chk": { + "name": "migration_run_metrics_unresolved_nonnegative_chk", + "value": "\"migration_run_metrics\".\"unresolved\" >= 0" + }, + "migration_run_metrics_failed_nonnegative_chk": { + "name": "migration_run_metrics_failed_nonnegative_chk", + "value": "\"migration_run_metrics\".\"failed\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.migration_runs": { + "name": "migration_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "profile_id": { + "name": "profile_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "plan_artifact_id": { + "name": "plan_artifact_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "target_key": { + "name": "target_key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "default": "'bookorbit'" + }, + "state": { + "name": "state", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "current_stage": { + "name": "current_stage", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "triggered_by_user_id": { + "name": "triggered_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "migration_runs_source_target_state_idx": { + "name": "migration_runs_source_target_state_idx", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "migration_runs_state_idx": { + "name": "migration_runs_state_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "migration_runs_source_id_migration_sources_id_fk": { + "name": "migration_runs_source_id_migration_sources_id_fk", + "tableFrom": "migration_runs", + "tableTo": "migration_sources", + "columnsFrom": ["source_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_runs_profile_id_migration_profiles_id_fk": { + "name": "migration_runs_profile_id_migration_profiles_id_fk", + "tableFrom": "migration_runs", + "tableTo": "migration_profiles", + "columnsFrom": ["profile_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_runs_plan_artifact_id_migration_plan_artifacts_id_fk": { + "name": "migration_runs_plan_artifact_id_migration_plan_artifacts_id_fk", + "tableFrom": "migration_runs", + "tableTo": "migration_plan_artifacts", + "columnsFrom": ["plan_artifact_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "migration_runs_triggered_by_user_id_users_id_fk": { + "name": "migration_runs_triggered_by_user_id_users_id_fk", + "tableFrom": "migration_runs", + "tableTo": "users", + "columnsFrom": ["triggered_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "migration_runs_profile_source_fk": { + "name": "migration_runs_profile_source_fk", + "tableFrom": "migration_runs", + "tableTo": "migration_profiles", + "columnsFrom": ["profile_id", "source_id"], + "columnsTo": ["id", "source_id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "migration_runs_plan_artifact_source_profile_fk": { + "name": "migration_runs_plan_artifact_source_profile_fk", + "tableFrom": "migration_runs", + "tableTo": "migration_plan_artifacts", + "columnsFrom": ["plan_artifact_id", "source_id", "profile_id"], + "columnsTo": ["id", "source_id", "profile_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "migration_runs_state_chk": { + "name": "migration_runs_state_chk", + "value": "\"state\" IN ('draft', 'preflight_failed', 'dry_run_ready', 'running', 'failed', 'completed')" + }, + "migration_runs_started_before_ended_chk": { + "name": "migration_runs_started_before_ended_chk", + "value": "\"migration_runs\".\"ended_at\" is null or \"migration_runs\".\"started_at\" is null or \"migration_runs\".\"ended_at\" >= \"migration_runs\".\"started_at\"" + } + }, + "isRLSEnabled": false + }, + "public.migration_sources": { + "name": "migration_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "connection_config": { + "name": "connection_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "capabilities": { + "name": "capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_validated_at": { + "name": "last_validated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "migration_sources_type_name_uidx": { + "name": "migration_sources_type_name_uidx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "migration_sources_created_by_user_id_idx": { + "name": "migration_sources_created_by_user_id_idx", + "columns": [ + { + "expression": "created_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "migration_sources_created_by_user_id_users_id_fk": { + "name": "migration_sources_created_by_user_id_users_id_fk", + "tableFrom": "migration_sources", + "tableTo": "users", + "columnsFrom": ["created_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.libraries": { + "name": "libraries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'books'" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cover_aspect_ratio": { + "name": "cover_aspect_ratio", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'2/3'" + }, + "watch": { + "name": "watch", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "watch_local_folders": { + "name": "watch_local_folders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_scan_cron_expression": { + "name": "auto_scan_cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata_precedence": { + "name": "metadata_precedence", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"folderStructure\",\"embedded\",\"nfoFile\",\"opfFile\",\"sidecar\"]'::jsonb" + }, + "regex_metadata": { + "name": "regex_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "format_priority": { + "name": "format_priority", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"epub\",\"pdf\",\"cbz\",\"cbr\",\"cb7\",\"mobi\",\"azw3\",\"azw\",\"fb2\",\"m4b\",\"mp3\",\"m4a\",\"opus\",\"ogg\",\"flac\"]'::jsonb" + }, + "allowed_formats": { + "name": "allowed_formats", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "organization_mode": { + "name": "organization_mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'book_per_folder'" + }, + "added_at_source": { + "name": "added_at_source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'imported'" + }, + "exclude_patterns": { + "name": "exclude_patterns", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "reading_threshold": { + "name": "reading_threshold", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0.25 + }, + "mark_as_finished_percent_complete": { + "name": "mark_as_finished_percent_complete", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 98 + }, + "file_write_enabled": { + "name": "file_write_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_write_write_cover": { + "name": "file_write_write_cover", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "file_write_epub_enabled": { + "name": "file_write_epub_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "file_write_epub_max_file_size_mb": { + "name": "file_write_epub_max_file_size_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "file_write_fb2_enabled": { + "name": "file_write_fb2_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_write_fb2_max_file_size_mb": { + "name": "file_write_fb2_max_file_size_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "file_write_pdf_enabled": { + "name": "file_write_pdf_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "file_write_pdf_max_file_size_mb": { + "name": "file_write_pdf_max_file_size_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "file_write_cbx_enabled": { + "name": "file_write_cbx_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_write_cbx_max_file_size_mb": { + "name": "file_write_cbx_max_file_size_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "file_write_kindle_enabled": { + "name": "file_write_kindle_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_write_kindle_max_file_size_mb": { + "name": "file_write_kindle_max_file_size_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "file_write_audio_enabled": { + "name": "file_write_audio_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_write_audio_max_file_size_mb": { + "name": "file_write_audio_max_file_size_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "file_write_all_files": { + "name": "file_write_all_files", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_write_read_along_enabled": { + "name": "file_write_read_along_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_write_read_along_max_file_size_mb": { + "name": "file_write_read_along_max_file_size_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + }, + "file_rename_enabled": { + "name": "file_rename_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_naming_pattern": { + "name": "file_naming_pattern", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "metadata_fetch_preferences": { + "name": "metadata_fetch_preferences", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "book_metadata_fetch_config": { + "name": "book_metadata_fetch_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "book_metadata_fetch_last_run_at": { + "name": "book_metadata_fetch_last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "book_metadata_fetch_last_queued_count": { + "name": "book_metadata_fetch_last_queued_count", + "type": "integer", + "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 + }, + "koreader_hash_revision": { + "name": "koreader_hash_revision", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "libraries_name_lower_uidx": { + "name": "libraries_name_lower_uidx", + "columns": [ + { + "expression": "lower(\"name\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "libraries_display_order_nonnegative_chk": { + "name": "libraries_display_order_nonnegative_chk", + "value": "\"libraries\".\"display_order\" >= 0" + }, + "libraries_type_chk": { + "name": "libraries_type_chk", + "value": "\"libraries\".\"type\" in ('books', 'podcasts')" + }, + "libraries_organization_mode_chk": { + "name": "libraries_organization_mode_chk", + "value": "\"libraries\".\"organization_mode\" in ('book_per_folder', 'book_per_file')" + }, + "libraries_added_at_source_chk": { + "name": "libraries_added_at_source_chk", + "value": "\"libraries\".\"added_at_source\" in ('imported', 'file_modified', 'file_created')" + }, + "libraries_reading_threshold_range_chk": { + "name": "libraries_reading_threshold_range_chk", + "value": "\"libraries\".\"reading_threshold\" >= 0 and \"libraries\".\"reading_threshold\" <= 100" + }, + "libraries_mark_finished_percent_range_chk": { + "name": "libraries_mark_finished_percent_range_chk", + "value": "\"libraries\".\"mark_as_finished_percent_complete\" >= 0 and \"libraries\".\"mark_as_finished_percent_complete\" <= 100" + }, + "libraries_scan_mode_chk": { + "name": "libraries_scan_mode_chk", + "value": "\"libraries\".\"scan_mode\" in ('auto', 'manual')" + }, + "libraries_poll_interval_nonnegative_chk": { + "name": "libraries_poll_interval_nonnegative_chk", + "value": "\"libraries\".\"poll_interval_seconds\" is null or \"libraries\".\"poll_interval_seconds\" >= 0" + }, + "libraries_file_write_epub_max_size_chk": { + "name": "libraries_file_write_epub_max_size_chk", + "value": "\"libraries\".\"file_write_epub_max_file_size_mb\" >= 1" + }, + "libraries_file_write_pdf_max_size_chk": { + "name": "libraries_file_write_pdf_max_size_chk", + "value": "\"libraries\".\"file_write_pdf_max_file_size_mb\" >= 1" + }, + "libraries_file_write_cbx_max_size_chk": { + "name": "libraries_file_write_cbx_max_size_chk", + "value": "\"libraries\".\"file_write_cbx_max_file_size_mb\" >= 1" + }, + "libraries_file_write_kindle_max_size_chk": { + "name": "libraries_file_write_kindle_max_size_chk", + "value": "\"libraries\".\"file_write_kindle_max_file_size_mb\" >= 1" + }, + "libraries_file_write_audio_max_size_chk": { + "name": "libraries_file_write_audio_max_size_chk", + "value": "\"libraries\".\"file_write_audio_max_file_size_mb\" >= 1" + } + }, + "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 + }, + "scan_state_version": { + "name": "scan_state_version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "role": { + "name": "role", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'downloads'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "library_folders_library_id_idx": { + "name": "library_folders_library_id_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "library_folders_library_path_uidx": { + "name": "library_folders_library_path_uidx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": { + "library_folders_id_library_id_unique": { + "name": "library_folders_id_library_id_unique", + "nullsNotDistinct": false, + "columns": ["id", "library_id"] + } + }, + "policies": {}, + "checkConstraints": { + "library_folders_scan_state_version_nonnegative_chk": { + "name": "library_folders_scan_state_version_nonnegative_chk", + "value": "\"library_folders\".\"scan_state_version\" >= 0" + }, + "library_folders_role_chk": { + "name": "library_folders_role_chk", + "value": "\"library_folders\".\"role\" in ('downloads', 'local')" + } + }, + "isRLSEnabled": false + }, + "public.book_files": { + "name": "book_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "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": "numeric(20, 0)", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "mtime": { + "name": "mtime", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "file_hash": { + "name": "file_hash", + "type": "varchar(32)", + "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": "'content'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration_seconds": { + "name": "duration_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_fixed_layout": { + "name": "is_fixed_layout", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "media_overlay_available": { + "name": "media_overlay_available", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "media_overlay_duration_seconds": { + "name": "media_overlay_duration_seconds", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "media_overlay_checked_at": { + "name": "media_overlay_checked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_files_public_id_uidx": { + "name": "book_files_public_id_uidx", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_absolute_path_uidx": { + "name": "book_files_absolute_path_uidx", + "columns": [ + { + "expression": "absolute_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_book_id_idx": { + "name": "book_files_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_change_timestamp_idx": { + "name": "book_files_change_timestamp_idx", + "columns": [ + { + "expression": "greatest(\"created_at\", \"updated_at\") desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_library_folder_id_idx": { + "name": "book_files_library_folder_id_idx", + "columns": [ + { + "expression": "library_folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_file_hash_idx": { + "name": "book_files_file_hash_idx", + "columns": [ + { + "expression": "file_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_ino_idx": { + "name": "book_files_ino_idx", + "columns": [ + { + "expression": "ino", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_format_idx": { + "name": "book_files_format_idx", + "columns": [ + { + "expression": "format", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_media_overlay_available_idx": { + "name": "book_files_media_overlay_available_idx", + "columns": [ + { + "expression": "media_overlay_available", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"book_files\".\"media_overlay_available\" = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_library_folder_file_hash_idx": { + "name": "book_files_library_folder_file_hash_idx", + "columns": [ + { + "expression": "library_folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_files_library_folder_ino_idx": { + "name": "book_files_library_folder_ino_idx", + "columns": [ + { + "expression": "library_folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ino", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": "cascade", + "onUpdate": "no action" + }, + "book_files_book_folder_consistency_fk": { + "name": "book_files_book_folder_consistency_fk", + "tableFrom": "book_files", + "tableTo": "books", + "columnsFrom": ["book_id", "library_folder_id"], + "columnsTo": ["id", "library_folder_id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_files_role_chk": { + "name": "book_files_role_chk", + "value": "\"book_files\".\"role\" in ('content', 'cover', 'metadata', 'supplement')" + }, + "book_files_size_bytes_nonnegative_chk": { + "name": "book_files_size_bytes_nonnegative_chk", + "value": "\"book_files\".\"size_bytes\" is null or \"book_files\".\"size_bytes\" >= 0" + }, + "book_files_duration_seconds_nonnegative_chk": { + "name": "book_files_duration_seconds_nonnegative_chk", + "value": "\"book_files\".\"duration_seconds\" is null or \"book_files\".\"duration_seconds\" >= 0" + }, + "book_files_media_overlay_duration_seconds_nonnegative_chk": { + "name": "book_files_media_overlay_duration_seconds_nonnegative_chk", + "value": "\"book_files\".\"media_overlay_duration_seconds\" is null or \"book_files\".\"media_overlay_duration_seconds\" >= 0" + } + }, + "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 + }, + "primary_file_id": { + "name": "primary_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "primary_author_sort_name": { + "name": "primary_author_sort_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "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 with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "books_library_id_folder_path_idx": { + "name": "books_library_id_folder_path_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "folder_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "books_primary_file_id_idx": { + "name": "books_primary_file_id_idx", + "columns": [ + { + "expression": "primary_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "books_library_status_id_idx": { + "name": "books_library_status_id_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "books_library_added_at_idx": { + "name": "books_library_added_at_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"added_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "books_library_visible_added_id_idx": { + "name": "books_library_visible_added_id_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"added_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"books\".\"status\" <> 'processing'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "books_library_author_sort_id_idx": { + "name": "books_library_author_sort_id_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"primary_author_sort_name\" asc nulls last", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"books\".\"status\" <> 'processing'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "books_library_author_sort_desc_id_idx": { + "name": "books_library_author_sort_desc_id_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"primary_author_sort_name\" desc nulls last", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"books\".\"status\" <> 'processing'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": "cascade", + "onUpdate": "no action" + }, + "books_primary_file_id_book_files_id_fk": { + "name": "books_primary_file_id_book_files_id_fk", + "tableFrom": "books", + "tableTo": "book_files", + "columnsFrom": ["primary_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "books_library_folder_library_fk": { + "name": "books_library_folder_library_fk", + "tableFrom": "books", + "tableTo": "library_folders", + "columnsFrom": ["library_folder_id", "library_id"], + "columnsTo": ["id", "library_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "books_id_library_folder_id_unique": { + "name": "books_id_library_folder_id_unique", + "nullsNotDistinct": false, + "columns": ["id", "library_folder_id"] + } + }, + "policies": {}, + "checkConstraints": { + "books_status_chk": { + "name": "books_status_chk", + "value": "\"books\".\"status\" in ('present', 'missing', 'processing')" + } + }, + "isRLSEnabled": false + }, + "public.book_covers": { + "name": "book_covers", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "medium": { + "name": "medium", + "type": "varchar(5)", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(9)", + "primaryKey": false, + "notNull": true + }, + "origin": { + "name": "origin", + "type": "varchar(12)", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "dormant_since": { + "name": "dormant_since", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "book_covers_book_id_books_id_fk": { + "name": "book_covers_book_id_books_id_fk", + "tableFrom": "book_covers", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_covers_book_id_medium_pk": { + "name": "book_covers_book_id_medium_pk", + "columns": ["book_id", "medium"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_covers_medium_chk": { + "name": "book_covers_medium_chk", + "value": "\"book_covers\".\"medium\" in ('ebook', 'audio')" + }, + "book_covers_source_chk": { + "name": "book_covers_source_chk", + "value": "\"book_covers\".\"source\" in ('extracted', 'custom')" + }, + "book_covers_origin_chk": { + "name": "book_covers_origin_chk", + "value": "\"book_covers\".\"origin\" in ('embedded', 'folder_image', 'opf', 'provider', 'dock', 'upload', 'legacy')" + }, + "book_covers_width_positive_chk": { + "name": "book_covers_width_positive_chk", + "value": "\"book_covers\".\"width\" is null or \"book_covers\".\"width\" > 0" + }, + "book_covers_height_positive_chk": { + "name": "book_covers_height_positive_chk", + "value": "\"book_covers\".\"height\" is null or \"book_covers\".\"height\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.book_duplicate_dismissals": { + "name": "book_duplicate_dismissals", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id_a": { + "name": "book_id_a", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id_b": { + "name": "book_id_b", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_duplicate_dismissals_user_created_idx": { + "name": "book_duplicate_dismissals_user_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"created_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_duplicate_dismissals_user_id_users_id_fk": { + "name": "book_duplicate_dismissals_user_id_users_id_fk", + "tableFrom": "book_duplicate_dismissals", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_dismissals_book_id_a_books_id_fk": { + "name": "book_duplicate_dismissals_book_id_a_books_id_fk", + "tableFrom": "book_duplicate_dismissals", + "tableTo": "books", + "columnsFrom": ["book_id_a"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_dismissals_book_id_b_books_id_fk": { + "name": "book_duplicate_dismissals_book_id_b_books_id_fk", + "tableFrom": "book_duplicate_dismissals", + "tableTo": "books", + "columnsFrom": ["book_id_b"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_duplicate_dismissals_user_id_book_id_a_book_id_b_pk": { + "name": "book_duplicate_dismissals_user_id_book_id_a_book_id_b_pk", + "columns": ["user_id", "book_id_a", "book_id_b"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_duplicate_dismissals_order_chk": { + "name": "book_duplicate_dismissals_order_chk", + "value": "\"book_duplicate_dismissals\".\"book_id_a\" < \"book_duplicate_dismissals\".\"book_id_b\"" + } + }, + "isRLSEnabled": false + }, + "public.book_duplicate_group_members": { + "name": "book_duplicate_group_members", + "schema": "", + "columns": { + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scan_id": { + "name": "scan_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "book_duplicate_group_members_group_idx": { + "name": "book_duplicate_group_members_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_duplicate_group_members_group_id_book_duplicate_groups_id_fk": { + "name": "book_duplicate_group_members_group_id_book_duplicate_groups_id_fk", + "tableFrom": "book_duplicate_group_members", + "tableTo": "book_duplicate_groups", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_group_members_scan_id_book_duplicate_scans_id_fk": { + "name": "book_duplicate_group_members_scan_id_book_duplicate_scans_id_fk", + "tableFrom": "book_duplicate_group_members", + "tableTo": "book_duplicate_scans", + "columnsFrom": ["scan_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_group_members_book_id_books_id_fk": { + "name": "book_duplicate_group_members_book_id_books_id_fk", + "tableFrom": "book_duplicate_group_members", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_duplicate_group_members_scan_id_book_id_pk": { + "name": "book_duplicate_group_members_scan_id_book_id_pk", + "columns": ["scan_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_duplicate_groups": { + "name": "book_duplicate_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "scan_id": { + "name": "scan_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "root_book_id": { + "name": "root_book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reasons": { + "name": "reasons", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "max_title_similarity": { + "name": "max_title_similarity", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "member_count": { + "name": "member_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "member_bytes_total": { + "name": "member_bytes_total", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "member_bytes_max": { + "name": "member_bytes_max", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "book_duplicate_groups_scan_root_uidx": { + "name": "book_duplicate_groups_scan_root_uidx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_duplicate_groups_scan_id_idx": { + "name": "book_duplicate_groups_scan_id_idx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_duplicate_groups_scan_member_count_idx": { + "name": "book_duplicate_groups_scan_member_count_idx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "member_count", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_duplicate_groups_scan_reclaimable_idx": { + "name": "book_duplicate_groups_scan_reclaimable_idx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(\"member_bytes_total\" - \"member_bytes_max\") desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_duplicate_groups_scan_confidence_idx": { + "name": "book_duplicate_groups_scan_confidence_idx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "confidence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_duplicate_groups_scan_id_book_duplicate_scans_id_fk": { + "name": "book_duplicate_groups_scan_id_book_duplicate_scans_id_fk", + "tableFrom": "book_duplicate_groups", + "tableTo": "book_duplicate_scans", + "columnsFrom": ["scan_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_groups_root_book_id_books_id_fk": { + "name": "book_duplicate_groups_root_book_id_books_id_fk", + "tableFrom": "book_duplicate_groups", + "tableTo": "books", + "columnsFrom": ["root_book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_duplicate_groups_confidence_chk": { + "name": "book_duplicate_groups_confidence_chk", + "value": "\"book_duplicate_groups\".\"confidence\" between 1 and 4" + } + }, + "isRLSEnabled": false + }, + "public.book_duplicate_pairs": { + "name": "book_duplicate_pairs", + "schema": "", + "columns": { + "scan_id": { + "name": "scan_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "book_id_a": { + "name": "book_id_a", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id_b": { + "name": "book_id_b", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reasons": { + "name": "reasons", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "title_similarity": { + "name": "title_similarity", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "book_duplicate_pairs_group_idx": { + "name": "book_duplicate_pairs_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_duplicate_pairs_scan_a_idx": { + "name": "book_duplicate_pairs_scan_a_idx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id_a", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_duplicate_pairs_scan_b_idx": { + "name": "book_duplicate_pairs_scan_b_idx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id_b", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_duplicate_pairs_scan_id_book_duplicate_scans_id_fk": { + "name": "book_duplicate_pairs_scan_id_book_duplicate_scans_id_fk", + "tableFrom": "book_duplicate_pairs", + "tableTo": "book_duplicate_scans", + "columnsFrom": ["scan_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_pairs_group_id_book_duplicate_groups_id_fk": { + "name": "book_duplicate_pairs_group_id_book_duplicate_groups_id_fk", + "tableFrom": "book_duplicate_pairs", + "tableTo": "book_duplicate_groups", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_pairs_book_id_a_books_id_fk": { + "name": "book_duplicate_pairs_book_id_a_books_id_fk", + "tableFrom": "book_duplicate_pairs", + "tableTo": "books", + "columnsFrom": ["book_id_a"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_pairs_book_id_b_books_id_fk": { + "name": "book_duplicate_pairs_book_id_b_books_id_fk", + "tableFrom": "book_duplicate_pairs", + "tableTo": "books", + "columnsFrom": ["book_id_b"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_duplicate_pairs_scan_id_book_id_a_book_id_b_pk": { + "name": "book_duplicate_pairs_scan_id_book_id_a_book_id_b_pk", + "columns": ["scan_id", "book_id_a", "book_id_b"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_duplicate_pairs_order_chk": { + "name": "book_duplicate_pairs_order_chk", + "value": "\"book_duplicate_pairs\".\"book_id_a\" < \"book_duplicate_pairs\".\"book_id_b\"" + } + }, + "isRLSEnabled": false + }, + "public.book_duplicate_scan_keys": { + "name": "book_duplicate_scan_keys", + "schema": "", + "columns": { + "scan_id": { + "name": "scan_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "book_duplicate_scan_keys_lookup_idx": { + "name": "book_duplicate_scan_keys_lookup_idx", + "columns": [ + { + "expression": "scan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_duplicate_scan_keys_scan_id_book_duplicate_scans_id_fk": { + "name": "book_duplicate_scan_keys_scan_id_book_duplicate_scans_id_fk", + "tableFrom": "book_duplicate_scan_keys", + "tableTo": "book_duplicate_scans", + "columnsFrom": ["scan_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_duplicate_scan_keys_book_id_books_id_fk": { + "name": "book_duplicate_scan_keys_book_id_books_id_fk", + "tableFrom": "book_duplicate_scan_keys", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_duplicate_scan_keys_scan_id_book_id_kind_value_pk": { + "name": "book_duplicate_scan_keys_scan_id_book_id_kind_value_pk", + "columns": ["scan_id", "book_id", "kind", "value"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_duplicate_scan_keys_kind_chk": { + "name": "book_duplicate_scan_keys_kind_chk", + "value": "\"book_duplicate_scan_keys\".\"kind\" in ('file_hash', 'isbn', 'exact_metadata')" + } + }, + "isRLSEnabled": false + }, + "public.book_duplicate_scans": { + "name": "book_duplicate_scans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "library_ids": { + "name": "library_ids", + "type": "integer[]", + "primaryKey": false, + "notNull": true + }, + "requested_library_id": { + "name": "requested_library_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "similarity_percent": { + "name": "similarity_percent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "processed_books": { + "name": "processed_books", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_books": { + "name": "total_books", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_groups": { + "name": "total_groups", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_extra_copies": { + "name": "total_extra_copies", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_reclaimable_bytes": { + "name": "total_reclaimable_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "book_duplicate_scans_user_created_idx": { + "name": "book_duplicate_scans_user_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"created_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_duplicate_scans_status_idx": { + "name": "book_duplicate_scans_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_duplicate_scans_user_id_users_id_fk": { + "name": "book_duplicate_scans_user_id_users_id_fk", + "tableFrom": "book_duplicate_scans", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_duplicate_scans_similarity_chk": { + "name": "book_duplicate_scans_similarity_chk", + "value": "\"book_duplicate_scans\".\"similarity_percent\" between 70 and 100" + }, + "book_duplicate_scans_status_chk": { + "name": "book_duplicate_scans_status_chk", + "value": "\"book_duplicate_scans\".\"status\" in ('queued', 'running', 'completed', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.book_move_jobs": { + "name": "book_move_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "started_by": { + "name": "started_by", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "target_library_id": { + "name": "target_library_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "target_folder_id": { + "name": "target_folder_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_library_ids": { + "name": "source_library_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "total_books": { + "name": "total_books", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "succeeded": { + "name": "succeeded", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "merged": { + "name": "merged", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed": { + "name": "failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped": { + "name": "skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "book_move_jobs_status_idx": { + "name": "book_move_jobs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_move_jobs_started_at_idx": { + "name": "book_move_jobs_started_at_idx", + "columns": [ + { + "expression": "\"started_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_move_jobs_started_by_users_id_fk": { + "name": "book_move_jobs_started_by_users_id_fk", + "tableFrom": "book_move_jobs", + "tableTo": "users", + "columnsFrom": ["started_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_move_jobs_target_library_id_libraries_id_fk": { + "name": "book_move_jobs_target_library_id_libraries_id_fk", + "tableFrom": "book_move_jobs", + "tableTo": "libraries", + "columnsFrom": ["target_library_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_move_jobs_target_folder_id_library_folders_id_fk": { + "name": "book_move_jobs_target_folder_id_library_folders_id_fk", + "tableFrom": "book_move_jobs", + "tableTo": "library_folders", + "columnsFrom": ["target_folder_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_move_jobs_status_chk": { + "name": "book_move_jobs_status_chk", + "value": "\"book_move_jobs\".\"status\" in ('running', 'completed', 'failed', 'interrupted')" + }, + "book_move_jobs_counts_nonnegative_chk": { + "name": "book_move_jobs_counts_nonnegative_chk", + "value": "\"book_move_jobs\".\"total_books\" >= 0 and \"book_move_jobs\".\"succeeded\" >= 0 and \"book_move_jobs\".\"merged\" >= 0 and \"book_move_jobs\".\"failed\" >= 0 and \"book_move_jobs\".\"skipped\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.collection_books": { + "name": "collection_books", + "schema": "", + "columns": { + "collection_id": { + "name": "collection_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "position": { + "name": "position", + "type": "bigserial", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "collection_books_book_id_idx": { + "name": "collection_books_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "collection_books_collection_position_idx": { + "name": "collection_books_collection_position_idx", + "columns": [ + { + "expression": "collection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "collection_books_collection_id_collections_id_fk": { + "name": "collection_books_collection_id_collections_id_fk", + "tableFrom": "collection_books", + "tableTo": "collections", + "columnsFrom": ["collection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "collection_books_book_id_books_id_fk": { + "name": "collection_books_book_id_books_id_fk", + "tableFrom": "collection_books", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "collection_books_collection_id_book_id_pk": { + "name": "collection_books_collection_id_book_id_pk", + "columns": ["collection_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.collection_podcasts": { + "name": "collection_podcasts", + "schema": "", + "columns": { + "collection_id": { + "name": "collection_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "podcast_id": { + "name": "podcast_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "collection_podcasts_podcast_id_idx": { + "name": "collection_podcasts_podcast_id_idx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "collection_podcasts_collection_id_collections_id_fk": { + "name": "collection_podcasts_collection_id_collections_id_fk", + "tableFrom": "collection_podcasts", + "tableTo": "collections", + "columnsFrom": ["collection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "collection_podcasts_podcast_id_podcasts_id_fk": { + "name": "collection_podcasts_podcast_id_podcasts_id_fk", + "tableFrom": "collection_podcasts", + "tableTo": "podcasts", + "columnsFrom": ["podcast_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "collection_podcasts_collection_id_podcast_id_pk": { + "name": "collection_podcasts_collection_id_podcast_id_pk", + "columns": ["collection_id", "podcast_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.collections": { + "name": "collections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "media_type": { + "name": "media_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'books'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sync_to_kobo": { + "name": "sync_to_kobo", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "collections_user_media_type_name_uidx": { + "name": "collections_user_media_type_name_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "media_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "collections_user_display_name_idx": { + "name": "collections_user_display_name_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "collections_public_display_name_idx": { + "name": "collections_public_display_name_idx", + "columns": [ + { + "expression": "is_public", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "collections_user_id_users_id_fk": { + "name": "collections_user_id_users_id_fk", + "tableFrom": "collections", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "collections_media_type_chk": { + "name": "collections_media_type_chk", + "value": "\"collections\".\"media_type\" in ('books', 'podcasts')" + }, + "collections_kobo_books_only_chk": { + "name": "collections_kobo_books_only_chk", + "value": "\"collections\".\"sync_to_kobo\" = false or \"collections\".\"media_type\" = 'books'" + } + }, + "isRLSEnabled": false + }, + "public.smart_scope_kobo_subscriptions": { + "name": "smart_scope_kobo_subscriptions", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "smart_scope_id": { + "name": "smart_scope_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "smart_scope_kobo_subscriptions_smart_scope_id_idx": { + "name": "smart_scope_kobo_subscriptions_smart_scope_id_idx", + "columns": [ + { + "expression": "smart_scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "smart_scope_kobo_subscriptions_user_id_users_id_fk": { + "name": "smart_scope_kobo_subscriptions_user_id_users_id_fk", + "tableFrom": "smart_scope_kobo_subscriptions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "smart_scope_kobo_subscriptions_smart_scope_id_smart_scopes_id_fk": { + "name": "smart_scope_kobo_subscriptions_smart_scope_id_smart_scopes_id_fk", + "tableFrom": "smart_scope_kobo_subscriptions", + "tableTo": "smart_scopes", + "columnsFrom": ["smart_scope_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "smart_scope_kobo_subscriptions_user_id_smart_scope_id_pk": { + "name": "smart_scope_kobo_subscriptions_user_id_smart_scope_id_pk", + "columns": ["user_id", "smart_scope_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.smart_scopes": { + "name": "smart_scopes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "media_type": { + "name": "media_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'books'" + }, + "library_id": { + "name": "library_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "filter": { + "name": "filter", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "default_sort": { + "name": "default_sort", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sync_to_kobo": { + "name": "sync_to_kobo", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "smart_scopes_user_media_type_idx": { + "name": "smart_scopes_user_media_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "media_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "smart_scopes_user_id_users_id_fk": { + "name": "smart_scopes_user_id_users_id_fk", + "tableFrom": "smart_scopes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "smart_scopes_library_id_libraries_id_fk": { + "name": "smart_scopes_library_id_libraries_id_fk", + "tableFrom": "smart_scopes", + "tableTo": "libraries", + "columnsFrom": ["library_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "smart_scopes_user_id_media_type_name_unique": { + "name": "smart_scopes_user_id_media_type_name_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "media_type", "name"] + } + }, + "policies": {}, + "checkConstraints": { + "smart_scopes_media_type_chk": { + "name": "smart_scopes_media_type_chk", + "value": "\"smart_scopes\".\"media_type\" in ('books', 'podcasts')" + }, + "smart_scopes_library_scope_chk": { + "name": "smart_scopes_library_scope_chk", + "value": "(\"smart_scopes\".\"media_type\" = 'podcasts') = (\"smart_scopes\".\"library_id\" is not null)" + }, + "smart_scopes_kobo_books_only_chk": { + "name": "smart_scopes_kobo_books_only_chk", + "value": "\"smart_scopes\".\"sync_to_kobo\" = false or \"smart_scopes\".\"media_type\" = 'books'" + } + }, + "isRLSEnabled": false + }, + "public.book_series": { + "name": "book_series", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "normalized_name": { + "name": "normalized_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "expected_book_count": { + "name": "expected_book_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expected_book_count_source": { + "name": "expected_book_count_source", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "expected_book_count_updated_at": { + "name": "expected_book_count_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_series_normalized_name_uidx": { + "name": "book_series_normalized_name_uidx", + "columns": [ + { + "expression": "normalized_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_series_name_trgm_idx": { + "name": "book_series_name_trgm_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "book_series_name_unaccent_trgm_idx": { + "name": "book_series_name_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"name\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "book_series_name_lower_idx": { + "name": "book_series_name_lower_idx", + "columns": [ + { + "expression": "lower(\"name\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_series_expected_book_count_range_chk": { + "name": "book_series_expected_book_count_range_chk", + "value": "\"book_series\".\"expected_book_count\" IS NULL OR (\"book_series\".\"expected_book_count\" >= 1 AND \"book_series\".\"expected_book_count\" <= 10000)" + } + }, + "isRLSEnabled": false + }, + "public.book_series_memberships": { + "name": "book_series_memberships", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "series_id": { + "name": "series_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "series_index": { + "name": "series_index", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_series_memberships_book_display_uidx": { + "name": "book_series_memberships_book_display_uidx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_series_memberships_series_index_book_idx": { + "name": "book_series_memberships_series_index_book_idx", + "columns": [ + { + "expression": "series_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(CASE WHEN \"series_index\" IS NULL THEN NULL ELSE ARRAY[split_part(\"series_index\", '.', 1)::numeric, CASE WHEN strpos(\"series_index\", '.') = 0 THEN -1::numeric ELSE split_part(\"series_index\", '.', 2)::numeric END] END)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "\"series_index\" COLLATE \"C\"", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_series_memberships_book_display_idx": { + "name": "book_series_memberships_book_display_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_series_memberships_book_id_books_id_fk": { + "name": "book_series_memberships_book_id_books_id_fk", + "tableFrom": "book_series_memberships", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_series_memberships_series_id_book_series_id_fk": { + "name": "book_series_memberships_series_id_book_series_id_fk", + "tableFrom": "book_series_memberships", + "tableTo": "book_series", + "columnsFrom": ["series_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_series_memberships_book_id_series_id_pk": { + "name": "book_series_memberships_book_id_series_id_pk", + "columns": ["book_id", "series_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_series_memberships_display_order_nonnegative_chk": { + "name": "book_series_memberships_display_order_nonnegative_chk", + "value": "\"book_series_memberships\".\"display_order\" >= 0" + }, + "book_series_memberships_series_index_format_chk": { + "name": "book_series_memberships_series_index_format_chk", + "value": "\"book_series_memberships\".\"series_index\" is null or \"book_series_memberships\".\"series_index\" ~ '^[0-9]+([.][0-9]+)?$'" + } + }, + "isRLSEnabled": false + }, + "public.author_enrichment_queue": { + "name": "author_enrichment_queue", + "schema": "", + "columns": { + "author_id": { + "name": "author_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "reason": { + "name": "reason", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_success_at": { + "name": "last_success_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_http_status": { + "name": "last_http_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ignore_cache": { + "name": "ignore_cache", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "author_enrichment_queue_status_next_attempt_idx": { + "name": "author_enrichment_queue_status_next_attempt_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "author_enrichment_queue_next_attempt_idx": { + "name": "author_enrichment_queue_next_attempt_idx", + "columns": [ + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "author_enrichment_queue_single_processing_idx": { + "name": "author_enrichment_queue_single_processing_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"author_enrichment_queue\".\"status\" = 'processing'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "author_enrichment_queue_author_id_authors_id_fk": { + "name": "author_enrichment_queue_author_id_authors_id_fk", + "tableFrom": "author_enrichment_queue", + "tableTo": "authors", + "columnsFrom": ["author_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "author_enrichment_queue_status_chk": { + "name": "author_enrichment_queue_status_chk", + "value": "\"author_enrichment_queue\".\"status\" in ('queued', 'processing', 'rate_limited', 'failed', 'done')" + }, + "author_enrichment_queue_reason_chk": { + "name": "author_enrichment_queue_reason_chk", + "value": "\"author_enrichment_queue\".\"reason\" in ('unknown', 'metadata_replace', 'manual_backfill', 'manual_backfill_all', 'author_rename', 'author_merge_target')" + }, + "author_enrichment_queue_attempt_count_nonnegative_chk": { + "name": "author_enrichment_queue_attempt_count_nonnegative_chk", + "value": "\"author_enrichment_queue\".\"attempt_count\" >= 0" + } + }, + "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 + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "has_photo": { + "name": "has_photo", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "birth_date": { + "name": "birth_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "birth_year": { + "name": "birth_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "death_date": { + "name": "death_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "death_year": { + "name": "death_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false + }, + "genres": { + "name": "genres", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "influences": { + "name": "influences", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "metadata_provider": { + "name": "metadata_provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "metadata_provider_id": { + "name": "metadata_provider_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "last_enriched_at": { + "name": "last_enriched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "authors_name_trgm_idx": { + "name": "authors_name_trgm_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "authors_name_unaccent_trgm_idx": { + "name": "authors_name_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"name\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "authors_name_unique": { + "name": "authors_name_unique", + "nullsNotDistinct": false, + "columns": ["name"] + } + }, + "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": { + "book_authors_author_id_idx": { + "name": "book_authors_author_id_idx", + "columns": [ + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_authors_book_display_idx": { + "name": "book_authors_book_display_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_authors_book_display_author_idx": { + "name": "book_authors_book_display_author_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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_community_ratings": { + "name": "book_community_ratings", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "rating_count": { + "name": "rating_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_community_ratings_provider_idx": { + "name": "book_community_ratings_provider_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_community_ratings_rating_book_idx": { + "name": "book_community_ratings_rating_book_idx", + "columns": [ + { + "expression": "rating", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_community_ratings_provider_rating_book_idx": { + "name": "book_community_ratings_provider_rating_book_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "rating", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_community_ratings_count_book_idx": { + "name": "book_community_ratings_count_book_idx", + "columns": [ + { + "expression": "rating_count", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_community_ratings_provider_count_book_idx": { + "name": "book_community_ratings_provider_count_book_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "rating_count", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_community_ratings_book_id_books_id_fk": { + "name": "book_community_ratings_book_id_books_id_fk", + "tableFrom": "book_community_ratings", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_community_ratings_book_id_provider_pk": { + "name": "book_community_ratings_book_id_provider_pk", + "columns": ["book_id", "provider"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_community_ratings_rating_range_chk": { + "name": "book_community_ratings_rating_range_chk", + "value": "\"book_community_ratings\".\"rating\" >= 0 and \"book_community_ratings\".\"rating\" <= 5" + }, + "book_community_ratings_count_nonnegative_chk": { + "name": "book_community_ratings_count_nonnegative_chk", + "value": "\"book_community_ratings\".\"rating_count\" is null or \"book_community_ratings\".\"rating_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.book_genres": { + "name": "book_genres", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "genre_id": { + "name": "genre_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "book_genres_genre_id_idx": { + "name": "book_genres_genre_id_idx", + "columns": [ + { + "expression": "genre_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_genres_book_id_books_id_fk": { + "name": "book_genres_book_id_books_id_fk", + "tableFrom": "book_genres", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_genres_genre_id_genres_id_fk": { + "name": "book_genres_genre_id_genres_id_fk", + "tableFrom": "book_genres", + "tableTo": "genres", + "columnsFrom": ["genre_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_genres_book_id_genre_id_pk": { + "name": "book_genres_book_id_genre_id_pk", + "columns": ["book_id", "genre_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_date": { + "name": "published_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "published_year": { + "name": "published_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "language": { + "name": "language", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "page_count": { + "name": "page_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "series_id": { + "name": "series_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "series_name": { + "name": "series_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "series_index": { + "name": "series_index", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "rating": { + "name": "rating", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cover_source": { + "name": "cover_source", + "type": "varchar(9)", + "primaryKey": false, + "notNull": false + }, + "google_books_id": { + "name": "google_books_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "goodreads_id": { + "name": "goodreads_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "amazon_id": { + "name": "amazon_id", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "hardcover_id": { + "name": "hardcover_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "hardcover_edition_id": { + "name": "hardcover_edition_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "open_library_id": { + "name": "open_library_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "itunes_id": { + "name": "itunes_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "kobo_id": { + "name": "kobo_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "metadata_score": { + "name": "metadata_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_metadata_fetch_at": { + "name": "last_metadata_fetch_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(256)", + "primaryKey": false, + "notNull": false + }, + "last_written_at": { + "name": "last_written_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "duration_seconds": { + "name": "duration_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "abridged": { + "name": "abridged", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "audible_id": { + "name": "audible_id", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "librofm_id": { + "name": "librofm_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "comicvine_id": { + "name": "comicvine_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "ranobedb_id": { + "name": "ranobedb_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "lubimyczytac_id": { + "name": "lubimyczytac_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "aladin_id": { + "name": "aladin_id", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "chapters": { + "name": "chapters", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "locked_fields": { + "name": "locked_fields", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "cover_updated_at": { + "name": "cover_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "bm_title_trgm_idx": { + "name": "bm_title_trgm_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_title_unaccent_trgm_idx": { + "name": "bm_title_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"title\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_title_lower_idx": { + "name": "bm_title_lower_idx", + "columns": [ + { + "expression": "lower(\"title\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_title_book_id_idx": { + "name": "bm_title_book_id_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_subtitle_unaccent_trgm_idx": { + "name": "bm_subtitle_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"subtitle\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_series_trgm_idx": { + "name": "bm_series_trgm_idx", + "columns": [ + { + "expression": "series_name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_series_unaccent_trgm_idx": { + "name": "bm_series_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"series_name\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_series_id_idx": { + "name": "bm_series_id_idx", + "columns": [ + { + "expression": "series_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_series_id_index_book_id_idx": { + "name": "bm_series_id_index_book_id_idx", + "columns": [ + { + "expression": "series_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(CASE WHEN \"series_index\" IS NULL THEN NULL ELSE ARRAY[split_part(\"series_index\", '.', 1)::numeric, CASE WHEN strpos(\"series_index\", '.') = 0 THEN -1::numeric ELSE split_part(\"series_index\", '.', 2)::numeric END] END)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "\"series_index\" COLLATE \"C\"", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_series_index_sort_idx": { + "name": "bm_series_index_sort_idx", + "columns": [ + { + "expression": "(CASE WHEN \"series_index\" IS NULL THEN NULL ELSE ARRAY[split_part(\"series_index\", '.', 1)::numeric, CASE WHEN strpos(\"series_index\", '.') = 0 THEN -1::numeric ELSE split_part(\"series_index\", '.', 2)::numeric END] END)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "\"series_index\" COLLATE \"C\"", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_series_name_lower_btrim_idx": { + "name": "bm_series_name_lower_btrim_idx", + "columns": [ + { + "expression": "lower(btrim(\"series_name\"))", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_publisher_trgm_idx": { + "name": "bm_publisher_trgm_idx", + "columns": [ + { + "expression": "publisher", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_publisher_unaccent_trgm_idx": { + "name": "bm_publisher_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"publisher\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_language_idx": { + "name": "bm_language_idx", + "columns": [ + { + "expression": "language", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_language_trgm_idx": { + "name": "bm_language_trgm_idx", + "columns": [ + { + "expression": "language", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_language_unaccent_trgm_idx": { + "name": "bm_language_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"language\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "bm_published_date_idx": { + "name": "bm_published_date_idx", + "columns": [ + { + "expression": "published_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_published_year_idx": { + "name": "bm_published_year_idx", + "columns": [ + { + "expression": "published_year", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_page_count_idx": { + "name": "bm_page_count_idx", + "columns": [ + { + "expression": "page_count", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_published_date_sort_idx": { + "name": "bm_published_date_sort_idx", + "columns": [ + { + "expression": "coalesce(\"published_date\", make_date(\"published_year\", 1, 1))", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_series_name_index_idx": { + "name": "bm_series_name_index_idx", + "columns": [ + { + "expression": "series_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "(CASE WHEN \"series_index\" IS NULL THEN NULL ELSE ARRAY[split_part(\"series_index\", '.', 1)::numeric, CASE WHEN strpos(\"series_index\", '.') = 0 THEN -1::numeric ELSE split_part(\"series_index\", '.', 2)::numeric END] END)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "\"series_index\" COLLATE \"C\"", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_isbn10_idx": { + "name": "bm_isbn10_idx", + "columns": [ + { + "expression": "isbn10", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_isbn13_idx": { + "name": "bm_isbn13_idx", + "columns": [ + { + "expression": "isbn13", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bm_embedding_hnsw_cosine_idx": { + "name": "bm_embedding_hnsw_cosine_idx", + "columns": [ + { + "expression": "\"embedding\" vector_cosine_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "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" + }, + "book_metadata_series_id_book_series_id_fk": { + "name": "book_metadata_series_id_book_series_id_fk", + "tableFrom": "book_metadata", + "tableTo": "book_series", + "columnsFrom": ["series_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_metadata_rating_range_chk": { + "name": "book_metadata_rating_range_chk", + "value": "\"book_metadata\".\"rating\" is null or (\"book_metadata\".\"rating\" >= 1 and \"book_metadata\".\"rating\" <= 10)" + }, + "book_metadata_published_date_range_chk": { + "name": "book_metadata_published_date_range_chk", + "value": "\"book_metadata\".\"published_date\" is null or (extract(year from \"book_metadata\".\"published_date\") >= 1000 and extract(year from \"book_metadata\".\"published_date\") <= 2200)" + }, + "book_metadata_published_year_range_chk": { + "name": "book_metadata_published_year_range_chk", + "value": "\"book_metadata\".\"published_year\" is null or (\"book_metadata\".\"published_year\" >= 1000 and \"book_metadata\".\"published_year\" <= 2200)" + }, + "book_metadata_page_count_nonnegative_chk": { + "name": "book_metadata_page_count_nonnegative_chk", + "value": "\"book_metadata\".\"page_count\" is null or \"book_metadata\".\"page_count\" >= 0" + }, + "book_metadata_series_index_format_chk": { + "name": "book_metadata_series_index_format_chk", + "value": "\"book_metadata\".\"series_index\" is null or \"book_metadata\".\"series_index\" ~ '^[0-9]+([.][0-9]+)?$'" + }, + "book_metadata_duration_seconds_nonnegative_chk": { + "name": "book_metadata_duration_seconds_nonnegative_chk", + "value": "\"book_metadata\".\"duration_seconds\" is null or \"book_metadata\".\"duration_seconds\" >= 0" + }, + "book_metadata_cover_source_chk": { + "name": "book_metadata_cover_source_chk", + "value": "\"book_metadata\".\"cover_source\" is null or \"book_metadata\".\"cover_source\" in ('extracted', 'custom')" + } + }, + "isRLSEnabled": false + }, + "public.book_metadata_fetch_queue": { + "name": "book_metadata_fetch_queue", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "reason": { + "name": "reason", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "'manual_trigger'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_http_status": { + "name": "last_http_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ignore_cache": { + "name": "ignore_cache", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "bmfq_status_idx": { + "name": "bmfq_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bmfq_created_at_idx": { + "name": "bmfq_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bmfq_status_created_book_idx": { + "name": "bmfq_status_created_book_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_metadata_fetch_queue_book_id_books_id_fk": { + "name": "book_metadata_fetch_queue_book_id_books_id_fk", + "tableFrom": "book_metadata_fetch_queue", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_metadata_fetch_queue_status_chk": { + "name": "book_metadata_fetch_queue_status_chk", + "value": "\"book_metadata_fetch_queue\".\"status\" in ('queued', 'processing', 'failed')" + }, + "book_metadata_fetch_queue_reason_chk": { + "name": "book_metadata_fetch_queue_reason_chk", + "value": "\"book_metadata_fetch_queue\".\"reason\" in ('event_import', 'manual_trigger', 'manual_retry')" + }, + "book_metadata_fetch_queue_attempt_count_nonnegative_chk": { + "name": "book_metadata_fetch_queue_attempt_count_nonnegative_chk", + "value": "\"book_metadata_fetch_queue\".\"attempt_count\" >= 0" + } + }, + "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": { + "book_tags_tag_id_idx": { + "name": "book_tags_tag_id_idx", + "columns": [ + { + "expression": "tag_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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.genres": { + "name": "genres", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "genres_name_trgm_idx": { + "name": "genres_name_trgm_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "genres_name_unaccent_trgm_idx": { + "name": "genres_name_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"name\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "genres_name_unique": { + "name": "genres_name_unique", + "nullsNotDistinct": false, + "columns": ["name"] + } + }, + "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": { + "tags_name_trgm_idx": { + "name": "tags_name_trgm_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "tags_name_unaccent_trgm_idx": { + "name": "tags_name_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"name\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tags_name_unique": { + "name": "tags_name_unique", + "nullsNotDistinct": false, + "columns": ["name"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_narrators": { + "name": "book_narrators", + "schema": "", + "columns": { + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "narrator_id": { + "name": "narrator_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "book_narrators_narrator_id_idx": { + "name": "book_narrators_narrator_id_idx", + "columns": [ + { + "expression": "narrator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_narrators_book_id_books_id_fk": { + "name": "book_narrators_book_id_books_id_fk", + "tableFrom": "book_narrators", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_narrators_narrator_id_narrators_id_fk": { + "name": "book_narrators_narrator_id_narrators_id_fk", + "tableFrom": "book_narrators", + "tableTo": "narrators", + "columnsFrom": ["narrator_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_narrators_book_id_narrator_id_pk": { + "name": "book_narrators_book_id_narrator_id_pk", + "columns": ["book_id", "narrator_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.narrators": { + "name": "narrators", + "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 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "narrators_name_trgm_idx": { + "name": "narrators_name_trgm_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "narrators_name_unaccent_trgm_idx": { + "name": "narrators_name_unaccent_trgm_idx", + "columns": [ + { + "expression": "public.bookorbit_unaccent(\"name\") gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "narrators_name_unique": { + "name": "narrators_name_unique", + "nullsNotDistinct": false, + "columns": ["name"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.library_dir_scan_state": { + "name": "library_dir_scan_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "library_folder_id": { + "name": "library_folder_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "dir_path": { + "name": "dir_path", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": true + }, + "last_seen_mtime_ms": { + "name": "last_seen_mtime_ms", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "library_dir_scan_state_folder_dir_uidx": { + "name": "library_dir_scan_state_folder_dir_uidx", + "columns": [ + { + "expression": "library_folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dir_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "library_dir_scan_state_folder_idx": { + "name": "library_dir_scan_state_folder_idx", + "columns": [ + { + "expression": "library_folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "library_dir_scan_state_library_folder_id_library_folders_id_fk": { + "name": "library_dir_scan_state_library_folder_id_library_folders_id_fk", + "tableFrom": "library_dir_scan_state", + "tableTo": "library_folders", + "columnsFrom": ["library_folder_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "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 with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "scan_jobs_library_status_idx": { + "name": "scan_jobs_library_status_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scan_jobs_library_started_at_idx": { + "name": "scan_jobs_library_started_at_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "scan_jobs_status_chk": { + "name": "scan_jobs_status_chk", + "value": "\"scan_jobs\".\"status\" in ('running', 'completed', 'failed')" + }, + "scan_jobs_triggered_by_chk": { + "name": "scan_jobs_triggered_by_chk", + "value": "\"scan_jobs\".\"triggered_by\" in ('manual', 'watcher', 'schedule')" + }, + "scan_jobs_added_count_nonnegative_chk": { + "name": "scan_jobs_added_count_nonnegative_chk", + "value": "\"scan_jobs\".\"added_count\" >= 0" + }, + "scan_jobs_updated_count_nonnegative_chk": { + "name": "scan_jobs_updated_count_nonnegative_chk", + "value": "\"scan_jobs\".\"updated_count\" >= 0" + }, + "scan_jobs_missing_count_nonnegative_chk": { + "name": "scan_jobs_missing_count_nonnegative_chk", + "value": "\"scan_jobs\".\"missing_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.annotation_positions": { + "name": "annotation_positions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "annotation_id": { + "name": "annotation_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "format": { + "name": "format", + "type": "varchar(12)", + "primaryKey": false, + "notNull": true + }, + "pos0": { + "name": "pos0", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pos1": { + "name": "pos1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'exact'" + }, + "converter_version": { + "name": "converter_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "extras": { + "name": "extras", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_positions_annotation_format_uidx": { + "name": "annotation_positions_annotation_format_uidx", + "columns": [ + { + "expression": "annotation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "format", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotation_positions_user_idx": { + "name": "annotation_positions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotation_positions_book_file_id_idx": { + "name": "annotation_positions_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotation_positions_format_status_idx": { + "name": "annotation_positions_format_status_idx", + "columns": [ + { + "expression": "format", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_positions_annotation_id_annotations_id_fk": { + "name": "annotation_positions_annotation_id_annotations_id_fk", + "tableFrom": "annotation_positions", + "tableTo": "annotations", + "columnsFrom": ["annotation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "annotation_positions_user_id_users_id_fk": { + "name": "annotation_positions_user_id_users_id_fk", + "tableFrom": "annotation_positions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "annotation_positions_book_file_id_book_files_id_fk": { + "name": "annotation_positions_book_file_id_book_files_id_fk", + "tableFrom": "annotation_positions", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "annotation_positions_format_chk": { + "name": "annotation_positions_format_chk", + "value": "\"annotation_positions\".\"format\" in ('cfi', 'xpointer', 'pdf', 'kobo_span')" + }, + "annotation_positions_status_chk": { + "name": "annotation_positions_status_chk", + "value": "\"annotation_positions\".\"status\" in ('exact', 'repaired', 'failed', 'pending')" + }, + "annotation_positions_pos0_chk": { + "name": "annotation_positions_pos0_chk", + "value": "\"annotation_positions\".\"status\" in ('failed', 'pending') or \"annotation_positions\".\"pos0\" is not null" + } + }, + "isRLSEnabled": false + }, + "public.annotation_sync_state": { + "name": "annotation_sync_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "annotation_id": { + "name": "annotation_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "external_key": { + "name": "external_key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "external_created_at": { + "name": "external_created_at", + "type": "varchar(19)", + "primaryKey": false, + "notNull": false + }, + "last_applied_version": { + "name": "last_applied_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "delete_acked_at": { + "name": "delete_acked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_synced_at": { + "name": "first_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_sync_state_annotation_source_device_uidx": { + "name": "annotation_sync_state_annotation_source_device_uidx", + "columns": [ + { + "expression": "annotation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotation_sync_state_user_source_device_key_idx": { + "name": "annotation_sync_state_user_source_device_key_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotation_sync_state_user_key_idx": { + "name": "annotation_sync_state_user_key_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotation_sync_state_annotation_id_idx": { + "name": "annotation_sync_state_annotation_id_idx", + "columns": [ + { + "expression": "annotation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_sync_state_annotation_id_annotations_id_fk": { + "name": "annotation_sync_state_annotation_id_annotations_id_fk", + "tableFrom": "annotation_sync_state", + "tableTo": "annotations", + "columnsFrom": ["annotation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "annotation_sync_state_user_id_users_id_fk": { + "name": "annotation_sync_state_user_id_users_id_fk", + "tableFrom": "annotation_sync_state", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "annotation_sync_state_source_chk": { + "name": "annotation_sync_state_source_chk", + "value": "\"annotation_sync_state\".\"source\" in ('koreader', 'kobo')" + } + }, + "isRLSEnabled": false + }, + "public.annotations": { + "name": "annotations", + "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 + }, + "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 + }, + "origin": { + "name": "origin", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'web'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "device_created_at": { + "name": "device_created_at", + "type": "varchar(19)", + "primaryKey": false, + "notNull": false + }, + "device_updated_at": { + "name": "device_updated_at", + "type": "varchar(19)", + "primaryKey": false, + "notNull": false + }, + "source_created_at": { + "name": "source_created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "starred_at": { + "name": "starred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotations_user_id_idx": { + "name": "annotations_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotations_user_id_id_idx": { + "name": "annotations_user_id_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotations_user_book_idx": { + "name": "annotations_user_book_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotations_book_id_idx": { + "name": "annotations_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotations_user_book_active_idx": { + "name": "annotations_user_book_active_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"annotations\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotations_user_created_active_idx": { + "name": "annotations_user_created_active_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"source_created_at\", \"created_at\") desc", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"annotations\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "annotations_user_book_created_active_idx": { + "name": "annotations_user_book_created_active_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"source_created_at\", \"created_at\") desc", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"annotations\".\"deleted_at\" is null", + "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": { + "annotations_style_chk": { + "name": "annotations_style_chk", + "value": "\"annotations\".\"style\" in ('highlight', 'underline', 'strikethrough', 'squiggly', 'invert')" + }, + "annotations_origin_chk": { + "name": "annotations_origin_chk", + "value": "\"annotations\".\"origin\" in ('web', 'koreader', 'kobo')" + } + }, + "isRLSEnabled": false + }, + "public.audiobook_progress": { + "name": "audiobook_progress", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "current_file_id": { + "name": "current_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "position_seconds": { + "name": "position_seconds", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "captured_at": { + "name": "captured_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "operation_id": { + "name": "operation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "manifest_revision": { + "name": "manifest_revision", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "abp_user_id_idx": { + "name": "abp_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audiobook_progress_book_id_idx": { + "name": "audiobook_progress_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audiobook_progress_current_file_id_idx": { + "name": "audiobook_progress_current_file_id_idx", + "columns": [ + { + "expression": "current_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audiobook_progress_user_id_users_id_fk": { + "name": "audiobook_progress_user_id_users_id_fk", + "tableFrom": "audiobook_progress", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "audiobook_progress_book_id_books_id_fk": { + "name": "audiobook_progress_book_id_books_id_fk", + "tableFrom": "audiobook_progress", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "audiobook_progress_current_file_id_book_files_id_fk": { + "name": "audiobook_progress_current_file_id_book_files_id_fk", + "tableFrom": "audiobook_progress", + "tableTo": "book_files", + "columnsFrom": ["current_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "audiobook_progress_user_id_book_id_pk": { + "name": "audiobook_progress_user_id_book_id_pk", + "columns": ["user_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "audiobook_progress_percentage_range_chk": { + "name": "audiobook_progress_percentage_range_chk", + "value": "\"audiobook_progress\".\"percentage\" >= 0 and \"audiobook_progress\".\"percentage\" <= 100" + }, + "audiobook_progress_position_seconds_nonnegative_chk": { + "name": "audiobook_progress_position_seconds_nonnegative_chk", + "value": "\"audiobook_progress\".\"position_seconds\" >= 0" + }, + "audiobook_progress_revision_positive_chk": { + "name": "audiobook_progress_revision_positive_chk", + "value": "\"audiobook_progress\".\"revision\" >= 1" + } + }, + "isRLSEnabled": false + }, + "public.book_journal_entries": { + "name": "book_journal_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quote": { + "name": "quote", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "chapter_title": { + "name": "chapter_title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "position_percent": { + "name": "position_percent", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cfi": { + "name": "cfi", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": false + }, + "position_seconds": { + "name": "position_seconds", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "book_journal_entries_user_client_id_uidx": { + "name": "book_journal_entries_user_client_id_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_journal_entries_user_book_created_active_idx": { + "name": "book_journal_entries_user_book_created_active_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"book_journal_entries\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_journal_entries_book_id_idx": { + "name": "book_journal_entries_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_journal_entries_user_id_users_id_fk": { + "name": "book_journal_entries_user_id_users_id_fk", + "tableFrom": "book_journal_entries", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_journal_entries_book_id_books_id_fk": { + "name": "book_journal_entries_book_id_books_id_fk", + "tableFrom": "book_journal_entries", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_journal_entries_body_length_chk": { + "name": "book_journal_entries_body_length_chk", + "value": "char_length(\"book_journal_entries\".\"body\") between 1 and 10000" + }, + "book_journal_entries_quote_length_chk": { + "name": "book_journal_entries_quote_length_chk", + "value": "\"book_journal_entries\".\"quote\" is null or char_length(\"book_journal_entries\".\"quote\") <= 5000" + }, + "book_journal_entries_position_percent_range_chk": { + "name": "book_journal_entries_position_percent_range_chk", + "value": "\"book_journal_entries\".\"position_percent\" is null or (\"book_journal_entries\".\"position_percent\" >= 0 and \"book_journal_entries\".\"position_percent\" <= 100)" + }, + "book_journal_entries_position_seconds_nonnegative_chk": { + "name": "book_journal_entries_position_seconds_nonnegative_chk", + "value": "\"book_journal_entries\".\"position_seconds\" is null or \"book_journal_entries\".\"position_seconds\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.bookmarks": { + "name": "bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "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": false + }, + "title": { + "name": "title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "chapter_id": { + "name": "chapter_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "position_seconds": { + "name": "position_seconds", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'web'" + }, + "device_pos": { + "name": "device_pos", + "type": "varchar(4000)", + "primaryKey": false, + "notNull": false + }, + "pageno": { + "name": "pageno", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "bookmarks_user_book_client_id_uidx": { + "name": "bookmarks_user_book_client_id_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_user_book_idx": { + "name": "bookmarks_user_book_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_book_id_idx": { + "name": "bookmarks_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_deleted_at_idx": { + "name": "bookmarks_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"bookmarks\".\"deleted_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_user_book_cfi_uidx": { + "name": "bookmarks_user_book_cfi_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cfi", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"bookmarks\".\"cfi\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "bookmarks_user_book_pos_uidx": { + "name": "bookmarks_user_book_pos_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position_seconds", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"bookmarks\".\"position_seconds\" is not null and \"bookmarks\".\"cfi\" is null and \"bookmarks\".\"deleted_at\" is null", + "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": { + "bookmarks_origin_chk": { + "name": "bookmarks_origin_chk", + "value": "\"bookmarks\".\"origin\" in ('web', 'koreader', 'kobo')" + } + }, + "isRLSEnabled": false + }, + "public.reader_default_preferences": { + "name": "reader_default_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "format_group": { + "name": "format_group", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rdp_user_format_idx": { + "name": "rdp_user_format_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "format_group", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reader_default_preferences_user_id_users_id_fk": { + "name": "reader_default_preferences_user_id_users_id_fk", + "tableFrom": "reader_default_preferences", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reader_default_preferences_format_group_chk": { + "name": "reader_default_preferences_format_group_chk", + "value": "\"reader_default_preferences\".\"format_group\" in ('epub', 'pdf', 'cbx', 'audio')" + } + }, + "isRLSEnabled": false + }, + "public.reader_preferences": { + "name": "reader_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rp_user_file_idx": { + "name": "rp_user_file_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reader_preferences_book_file_id_idx": { + "name": "reader_preferences_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reader_preferences_user_id_users_id_fk": { + "name": "reader_preferences_user_id_users_id_fk", + "tableFrom": "reader_preferences", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reader_preferences_book_file_id_book_files_id_fk": { + "name": "reader_preferences_book_file_id_book_files_id_fk", + "tableFrom": "reader_preferences", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reading_attempts": { + "name": "reading_attempts", + "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 + }, + "started_on": { + "name": "started_on", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "ended_on": { + "name": "ended_on", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "external_provider": { + "name": "external_provider", + "type": "varchar(40)", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reading_attempts_user_book_idx": { + "name": "reading_attempts_user_book_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reading_attempts_book_id_idx": { + "name": "reading_attempts_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reading_attempts_user_outcome_ended_idx": { + "name": "reading_attempts_user_outcome_ended_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "outcome", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ended_on", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reading_attempts_one_active_uidx": { + "name": "reading_attempts_one_active_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"reading_attempts\".\"outcome\" is null and \"reading_attempts\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "reading_attempts_external_uidx": { + "name": "reading_attempts_external_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"reading_attempts\".\"external_provider\" is not null and \"reading_attempts\".\"external_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reading_attempts_user_id_users_id_fk": { + "name": "reading_attempts_user_id_users_id_fk", + "tableFrom": "reading_attempts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reading_attempts_book_id_books_id_fk": { + "name": "reading_attempts_book_id_books_id_fk", + "tableFrom": "reading_attempts", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reading_attempts_outcome_chk": { + "name": "reading_attempts_outcome_chk", + "value": "\"reading_attempts\".\"outcome\" is null or \"reading_attempts\".\"outcome\" in ('completed', 'skimmed', 'abandoned')" + }, + "reading_attempts_origin_chk": { + "name": "reading_attempts_origin_chk", + "value": "\"reading_attempts\".\"origin\" in ('manual', 'bookorbit', 'kobo', 'koreader', 'hardcover', 'migration')" + }, + "reading_attempts_end_after_start_chk": { + "name": "reading_attempts_end_after_start_chk", + "value": "\"reading_attempts\".\"ended_on\" is null or \"reading_attempts\".\"started_on\" is null or \"reading_attempts\".\"ended_on\" >= \"reading_attempts\".\"started_on\"" + }, + "reading_attempts_closed_has_outcome_chk": { + "name": "reading_attempts_closed_has_outcome_chk", + "value": "\"reading_attempts\".\"ended_on\" is null or \"reading_attempts\".\"outcome\" is not null" + } + }, + "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 + }, + "position_seconds": { + "name": "position_seconds", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "media_overlay_fragment": { + "name": "media_overlay_fragment", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": false + }, + "media_overlay_section_index": { + "name": "media_overlay_section_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "kobo_location_source": { + "name": "kobo_location_source", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": false + }, + "kobo_location_type": { + "name": "kobo_location_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "kobo_location_value": { + "name": "kobo_location_value", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "kobo_content_source_progress_percent": { + "name": "kobo_content_source_progress_percent", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "koreader_progress": { + "name": "koreader_progress", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "narration_percentage": { + "name": "narration_percentage", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "narration_updated_at": { + "name": "narration_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "text_updated_at": { + "name": "text_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_read_at": { + "name": "last_read_at", + "type": "timestamp with time zone", + "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": {} + }, + "reading_progress_user_last_read_at_idx": { + "name": "reading_progress_user_last_read_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_read_at", + "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": { + "reading_progress_percentage_range_chk": { + "name": "reading_progress_percentage_range_chk", + "value": "\"reading_progress\".\"percentage\" >= 0 and \"reading_progress\".\"percentage\" <= 100" + }, + "reading_progress_page_number_nonnegative_chk": { + "name": "reading_progress_page_number_nonnegative_chk", + "value": "\"reading_progress\".\"page_number\" is null or \"reading_progress\".\"page_number\" >= 0" + }, + "reading_progress_position_seconds_nonnegative_chk": { + "name": "reading_progress_position_seconds_nonnegative_chk", + "value": "\"reading_progress\".\"position_seconds\" is null or \"reading_progress\".\"position_seconds\" >= 0" + }, + "reading_progress_media_overlay_section_index_nonnegative_chk": { + "name": "reading_progress_media_overlay_section_index_nonnegative_chk", + "value": "\"reading_progress\".\"media_overlay_section_index\" is null or \"reading_progress\".\"media_overlay_section_index\" >= 0" + }, + "reading_progress_kobo_content_source_progress_range_chk": { + "name": "reading_progress_kobo_content_source_progress_range_chk", + "value": "\"reading_progress\".\"kobo_content_source_progress_percent\" is null or (\"reading_progress\".\"kobo_content_source_progress_percent\" >= 0 and \"reading_progress\".\"kobo_content_source_progress_percent\" <= 100)" + }, + "reading_progress_narration_percentage_range_chk": { + "name": "reading_progress_narration_percentage_range_chk", + "value": "\"reading_progress\".\"narration_percentage\" is null or (\"reading_progress\".\"narration_percentage\" >= 0 and \"reading_progress\".\"narration_percentage\" <= 100)" + } + }, + "isRLSEnabled": false + }, + "public.reading_session_sync_cursors": { + "name": "reading_session_sync_cursors", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "source_device_key": { + "name": "source_device_key", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_modified": { + "name": "last_modified", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rssc_book_id_idx": { + "name": "rssc_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reading_session_sync_cursors_user_id_users_id_fk": { + "name": "reading_session_sync_cursors_user_id_users_id_fk", + "tableFrom": "reading_session_sync_cursors", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reading_session_sync_cursors_book_id_books_id_fk": { + "name": "reading_session_sync_cursors_book_id_books_id_fk", + "tableFrom": "reading_session_sync_cursors", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "reading_session_sync_cursors_user_id_book_id_source_source_device_key_pk": { + "name": "reading_session_sync_cursors_user_id_book_id_source_source_device_key_pk", + "columns": ["user_id", "book_id", "source", "source_device_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "rssc_counter_nonnegative_chk": { + "name": "rssc_counter_nonnegative_chk", + "value": "\"reading_session_sync_cursors\".\"counter\" >= 0" + }, + "rssc_generation_nonnegative_chk": { + "name": "rssc_generation_nonnegative_chk", + "value": "\"reading_session_sync_cursors\".\"generation\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.reading_sessions": { + "name": "reading_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "attempt_id": { + "name": "attempt_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "source_device_key": { + "name": "source_device_key", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "session_type": { + "name": "session_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'read'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "duration_seconds": { + "name": "duration_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "progress_delta": { + "name": "progress_delta", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "end_progress": { + "name": "end_progress", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "sync_interval_started_at": { + "name": "sync_interval_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "rs_user_session_id_uidx": { + "name": "rs_user_session_id_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rs_user_started_at_idx": { + "name": "rs_user_started_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rs_book_file_started_at_idx": { + "name": "rs_book_file_started_at_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rs_user_book_file_idx": { + "name": "rs_user_book_file_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rs_user_book_started_at_idx": { + "name": "rs_user_book_started_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rs_user_book_source_device_started_idx": { + "name": "rs_user_book_source_device_started_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_device_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reading_sessions_book_id_idx": { + "name": "reading_sessions_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rs_attempt_started_at_idx": { + "name": "rs_attempt_started_at_idx", + "columns": [ + { + "expression": "attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reading_sessions_user_id_users_id_fk": { + "name": "reading_sessions_user_id_users_id_fk", + "tableFrom": "reading_sessions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reading_sessions_book_file_id_book_files_id_fk": { + "name": "reading_sessions_book_file_id_book_files_id_fk", + "tableFrom": "reading_sessions", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reading_sessions_book_id_books_id_fk": { + "name": "reading_sessions_book_id_books_id_fk", + "tableFrom": "reading_sessions", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reading_sessions_attempt_id_reading_attempts_id_fk": { + "name": "reading_sessions_attempt_id_reading_attempts_id_fk", + "tableFrom": "reading_sessions", + "tableTo": "reading_attempts", + "columnsFrom": ["attempt_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reading_sessions_source_chk": { + "name": "reading_sessions_source_chk", + "value": "\"reading_sessions\".\"source\" in ('web', 'ios', 'watchos', 'android', 'koreader', 'manual', 'kobo')" + }, + "reading_sessions_duration_seconds_nonnegative_chk": { + "name": "reading_sessions_duration_seconds_nonnegative_chk", + "value": "\"reading_sessions\".\"duration_seconds\" >= 0" + }, + "reading_sessions_end_progress_range_chk": { + "name": "reading_sessions_end_progress_range_chk", + "value": "\"reading_sessions\".\"end_progress\" is null or (\"reading_sessions\".\"end_progress\" >= 0 and \"reading_sessions\".\"end_progress\" <= 100)" + }, + "reading_sessions_ended_after_started_chk": { + "name": "reading_sessions_ended_after_started_chk", + "value": "\"reading_sessions\".\"ended_at\" >= \"reading_sessions\".\"started_at\"" + } + }, + "isRLSEnabled": false + }, + "public.user_book_notes": { + "name": "user_book_notes", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ubn_user_id_idx": { + "name": "ubn_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ubn_book_id_idx": { + "name": "ubn_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ubn_book_user_idx": { + "name": "ubn_book_user_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_book_notes_user_id_users_id_fk": { + "name": "user_book_notes_user_id_users_id_fk", + "tableFrom": "user_book_notes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_book_notes_book_id_books_id_fk": { + "name": "user_book_notes_book_id_books_id_fk", + "tableFrom": "user_book_notes", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_book_notes_user_id_book_id_pk": { + "name": "user_book_notes_user_id_book_id_pk", + "columns": ["user_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_book_notes_note_length_chk": { + "name": "user_book_notes_note_length_chk", + "value": "\"user_book_notes\".\"note\" is null or char_length(\"user_book_notes\".\"note\") <= 10000" + } + }, + "isRLSEnabled": false + }, + "public.user_book_ratings": { + "name": "user_book_ratings", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ubr_user_id_idx": { + "name": "ubr_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ubr_book_id_idx": { + "name": "ubr_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ubr_book_user_rating_idx": { + "name": "ubr_book_user_rating_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_book_ratings_user_id_users_id_fk": { + "name": "user_book_ratings_user_id_users_id_fk", + "tableFrom": "user_book_ratings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_book_ratings_book_id_books_id_fk": { + "name": "user_book_ratings_book_id_books_id_fk", + "tableFrom": "user_book_ratings", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_book_ratings_user_id_book_id_pk": { + "name": "user_book_ratings_user_id_book_id_pk", + "columns": ["user_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_book_ratings_rating_range_chk": { + "name": "user_book_ratings_rating_range_chk", + "value": "\"user_book_ratings\".\"rating\" is null or (\"user_book_ratings\".\"rating\" >= 1 and \"user_book_ratings\".\"rating\" <= 5)" + } + }, + "isRLSEnabled": false + }, + "public.user_book_read_aloud_sync_settings": { + "name": "user_book_read_aloud_sync_settings", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'auto'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ubrass_user_id_idx": { + "name": "ubrass_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_book_read_aloud_sync_settings_user_id_users_id_fk": { + "name": "user_book_read_aloud_sync_settings_user_id_users_id_fk", + "tableFrom": "user_book_read_aloud_sync_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_book_read_aloud_sync_settings_book_id_books_id_fk": { + "name": "user_book_read_aloud_sync_settings_book_id_books_id_fk", + "tableFrom": "user_book_read_aloud_sync_settings", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_book_read_aloud_sync_settings_user_id_book_id_pk": { + "name": "user_book_read_aloud_sync_settings_user_id_book_id_pk", + "columns": ["user_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_book_read_aloud_sync_settings_mode_chk": { + "name": "user_book_read_aloud_sync_settings_mode_chk", + "value": "\"user_book_read_aloud_sync_settings\".\"mode\" in ('auto', 'disabled')" + } + }, + "isRLSEnabled": false + }, + "public.user_book_status": { + "name": "user_book_status", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'unread'" + }, + "source": { + "name": "source", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'auto'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ubs_user_id_idx": { + "name": "ubs_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ubs_user_status_idx": { + "name": "ubs_user_status_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ubs_book_user_idx": { + "name": "ubs_book_user_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_book_status_user_id_users_id_fk": { + "name": "user_book_status_user_id_users_id_fk", + "tableFrom": "user_book_status", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_book_status_book_id_books_id_fk": { + "name": "user_book_status_book_id_books_id_fk", + "tableFrom": "user_book_status", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_book_status_user_id_book_id_pk": { + "name": "user_book_status_user_id_book_id_pk", + "columns": ["user_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_book_status_status_chk": { + "name": "user_book_status_status_chk", + "value": "\"user_book_status\".\"status\" in ('unread', 'want_to_read', 'reading', 'on_hold', 'rereading', 'read', 'skimmed', 'abandoned')" + }, + "user_book_status_source_chk": { + "name": "user_book_status_source_chk", + "value": "\"user_book_status\".\"source\" in ('auto', 'manual')" + }, + "user_book_status_finished_after_started_chk": { + "name": "user_book_status_finished_after_started_chk", + "value": "\"user_book_status\".\"finished_at\" is null or \"user_book_status\".\"started_at\" is null or \"user_book_status\".\"finished_at\" >= \"user_book_status\".\"started_at\"" + } + }, + "isRLSEnabled": false + }, + "public.user_reading_daily_stats": { + "name": "user_reading_daily_stats", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "library_id": { + "name": "library_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "day": { + "name": "day", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "reading_seconds": { + "name": "reading_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "progress_delta": { + "name": "progress_delta", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sessions_count": { + "name": "sessions_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "urds_user_day_idx": { + "name": "urds_user_day_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "day", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_reading_daily_stats_library_id_idx": { + "name": "user_reading_daily_stats_library_id_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_reading_daily_stats_user_id_users_id_fk": { + "name": "user_reading_daily_stats_user_id_users_id_fk", + "tableFrom": "user_reading_daily_stats", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_reading_daily_stats_library_id_libraries_id_fk": { + "name": "user_reading_daily_stats_library_id_libraries_id_fk", + "tableFrom": "user_reading_daily_stats", + "tableTo": "libraries", + "columnsFrom": ["library_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_reading_daily_stats_user_id_library_id_day_pk": { + "name": "user_reading_daily_stats_user_id_library_id_day_pk", + "columns": ["user_id", "library_id", "day"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_reading_daily_stats_reading_seconds_nonnegative_chk": { + "name": "user_reading_daily_stats_reading_seconds_nonnegative_chk", + "value": "\"user_reading_daily_stats\".\"reading_seconds\" >= 0" + }, + "user_reading_daily_stats_sessions_count_nonnegative_chk": { + "name": "user_reading_daily_stats_sessions_count_nonnegative_chk", + "value": "\"user_reading_daily_stats\".\"sessions_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.oidc_group_mappings": { + "name": "oidc_group_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "oidc_group_claim": { + "name": "oidc_group_claim", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_name": { + "name": "permission_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oidc_group_mappings_provider_claim_uidx": { + "name": "oidc_group_mappings_provider_claim_uidx", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "oidc_group_claim", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oidc_group_mappings_provider_id_oidc_providers_id_fk": { + "name": "oidc_group_mappings_provider_id_oidc_providers_id_fk", + "tableFrom": "oidc_group_mappings", + "tableTo": "oidc_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oidc_identities": { + "name": "oidc_identities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "oidc_subject": { + "name": "oidc_subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_issuer": { + "name": "oidc_issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "linked_at": { + "name": "linked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oidc_identities_provider_subject_uidx": { + "name": "oidc_identities_provider_subject_uidx", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "oidc_subject", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_identities_user_provider_uidx": { + "name": "oidc_identities_user_provider_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_identities_issuer_subject_uidx": { + "name": "oidc_identities_issuer_subject_uidx", + "columns": [ + { + "expression": "oidc_issuer", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "oidc_subject", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_identities_user_id_idx": { + "name": "oidc_identities_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oidc_identities_user_id_users_id_fk": { + "name": "oidc_identities_user_id_users_id_fk", + "tableFrom": "oidc_identities", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oidc_identities_provider_id_oidc_providers_id_fk": { + "name": "oidc_identities_provider_id_oidc_providers_id_fk", + "tableFrom": "oidc_identities", + "tableTo": "oidc_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oidc_permission_grants": { + "name": "oidc_permission_grants", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "permission_name": { + "name": "permission_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oidc_permission_grants_user_id_users_id_fk": { + "name": "oidc_permission_grants_user_id_users_id_fk", + "tableFrom": "oidc_permission_grants", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oidc_permission_grants_provider_id_oidc_providers_id_fk": { + "name": "oidc_permission_grants_provider_id_oidc_providers_id_fk", + "tableFrom": "oidc_permission_grants", + "tableTo": "oidc_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "oidc_permission_grants_user_id_provider_id_permission_name_pk": { + "name": "oidc_permission_grants_user_id_provider_id_permission_name_pk", + "columns": ["user_id", "provider_id", "permission_name"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oidc_providers": { + "name": "oidc_providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "issuer_uri": { + "name": "issuer_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'openid profile email'" + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claim_mapping": { + "name": "claim_mapping", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"username\":\"preferred_username\",\"name\":\"name\",\"email\":\"email\",\"groups\":\"groups\"}'::jsonb" + }, + "auto_provision": { + "name": "auto_provision", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"enabled\":false,\"allowLocalLinking\":false,\"defaultPermissionNames\":[]}'::jsonb" + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oidc_providers_enabled_order_idx": { + "name": "oidc_providers_enabled_order_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oidc_providers_slug_unique": { + "name": "oidc_providers_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + }, + "oidc_providers_issuer_uri_unique": { + "name": "oidc_providers_issuer_uri_unique", + "nullsNotDistinct": false, + "columns": ["issuer_uri"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oidc_sessions": { + "name": "oidc_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "oidc_subject": { + "name": "oidc_subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_issuer": { + "name": "oidc_issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_session_id": { + "name": "oidc_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token_hint": { + "name": "id_token_hint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idp_refresh_token": { + "name": "idp_refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oidc_sessions_user_active_idx": { + "name": "oidc_sessions_user_active_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"oidc_sessions\".\"revoked\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_sessions_subject_issuer_idx": { + "name": "oidc_sessions_subject_issuer_idx", + "columns": [ + { + "expression": "oidc_subject", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "oidc_issuer", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_sessions_sid_idx": { + "name": "oidc_sessions_sid_idx", + "columns": [ + { + "expression": "oidc_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_sessions_session_id_idx": { + "name": "oidc_sessions_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_sessions_expires_at_idx": { + "name": "oidc_sessions_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oidc_sessions_provider_id_idx": { + "name": "oidc_sessions_provider_id_idx", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oidc_sessions_user_id_users_id_fk": { + "name": "oidc_sessions_user_id_users_id_fk", + "tableFrom": "oidc_sessions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oidc_sessions_session_id_auth_sessions_id_fk": { + "name": "oidc_sessions_session_id_auth_sessions_id_fk", + "tableFrom": "oidc_sessions", + "tableTo": "auth_sessions", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oidc_sessions_provider_id_oidc_providers_id_fk": { + "name": "oidc_sessions_provider_id_oidc_providers_id_fk", + "tableFrom": "oidc_sessions", + "tableTo": "oidc_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oidc_states": { + "name": "oidc_states", + "schema": "", + "columns": { + "state": { + "name": "state", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "meta": { + "name": "meta", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "oidc_states_expires_at_idx": { + "name": "oidc_states_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oidc_states_provider_id_oidc_providers_id_fk": { + "name": "oidc_states_provider_id_oidc_providers_id_fk", + "tableFrom": "oidc_states", + "tableTo": "oidc_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oidc_used_jtis": { + "name": "oidc_used_jtis", + "schema": "", + "columns": { + "jti": { + "name": "jti", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oidc_used_jtis_expires_at_idx": { + "name": "oidc_used_jtis_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.opds_users": { + "name": "opds_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "read_aloud_delivery": { + "name": "read_aloud_delivery", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "opds_sort_order", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'recent'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "opds_users_username_lower_uidx": { + "name": "opds_users_username_lower_uidx", + "columns": [ + { + "expression": "lower(\"username\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "opds_users_user_id_users_id_fk": { + "name": "opds_users_user_id_users_id_fk", + "tableFrom": "opds_users", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "opds_users_username_unique": { + "name": "opds_users_username_unique", + "nullsNotDistinct": false, + "columns": ["username"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_book_entitlements": { + "name": "kobo_book_entitlements", + "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 + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cover_image_id": { + "name": "cover_image_id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "needs_legacy_numeric_removal": { + "name": "needs_legacy_numeric_removal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kobo_book_entitlements_user_id_idx": { + "name": "kobo_book_entitlements_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kobo_book_entitlements_book_id_idx": { + "name": "kobo_book_entitlements_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_book_entitlements_user_id_users_id_fk": { + "name": "kobo_book_entitlements_user_id_users_id_fk", + "tableFrom": "kobo_book_entitlements", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_book_entitlements_book_id_books_id_fk": { + "name": "kobo_book_entitlements_book_id_books_id_fk", + "tableFrom": "kobo_book_entitlements", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kobo_book_entitlements_user_book_unique": { + "name": "kobo_book_entitlements_user_book_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "book_id"] + }, + "kobo_book_entitlements_user_entitlement_unique": { + "name": "kobo_book_entitlements_user_entitlement_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "entitlement_id"] + }, + "kobo_book_entitlements_user_cover_unique": { + "name": "kobo_book_entitlements_user_cover_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "cover_image_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_bookmark_links": { + "name": "kobo_bookmark_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "bookmark_id": { + "name": "bookmark_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "external_key": { + "name": "external_key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kobo_bookmark_links_bookmark_device_uidx": { + "name": "kobo_bookmark_links_bookmark_device_uidx", + "columns": [ + { + "expression": "bookmark_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kobo_bookmark_links_device_key_uidx": { + "name": "kobo_bookmark_links_device_key_uidx", + "columns": [ + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kobo_bookmark_links_user_device_book_idx": { + "name": "kobo_bookmark_links_user_device_book_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_bookmark_links_bookmark_id_bookmarks_id_fk": { + "name": "kobo_bookmark_links_bookmark_id_bookmarks_id_fk", + "tableFrom": "kobo_bookmark_links", + "tableTo": "bookmarks", + "columnsFrom": ["bookmark_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_bookmark_links_user_id_users_id_fk": { + "name": "kobo_bookmark_links_user_id_users_id_fk", + "tableFrom": "kobo_bookmark_links", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_bookmark_links_book_id_books_id_fk": { + "name": "kobo_bookmark_links_book_id_books_id_fk", + "tableFrom": "kobo_bookmark_links", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_bookmark_links_owner_fk": { + "name": "kobo_bookmark_links_owner_fk", + "tableFrom": "kobo_bookmark_links", + "tableTo": "kobo_devices", + "columnsFrom": ["device_id", "user_id"], + "columnsTo": ["id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_device_deliveries": { + "name": "kobo_device_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_file_id": { + "name": "source_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'offered'" + }, + "previous_id": { + "name": "previous_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "current": { + "name": "current", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kobo_device_deliveries_current_idx": { + "name": "kobo_device_deliveries_current_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kobo_device_deliveries\".\"current\" = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kobo_device_deliveries_device_book_idx": { + "name": "kobo_device_deliveries_device_book_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_device_deliveries_user_id_users_id_fk": { + "name": "kobo_device_deliveries_user_id_users_id_fk", + "tableFrom": "kobo_device_deliveries", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_device_deliveries_book_id_books_id_fk": { + "name": "kobo_device_deliveries_book_id_books_id_fk", + "tableFrom": "kobo_device_deliveries", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_device_deliveries_source_file_id_book_files_id_fk": { + "name": "kobo_device_deliveries_source_file_id_book_files_id_fk", + "tableFrom": "kobo_device_deliveries", + "tableTo": "book_files", + "columnsFrom": ["source_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "kobo_device_deliveries_owner_fk": { + "name": "kobo_device_deliveries_owner_fk", + "tableFrom": "kobo_device_deliveries", + "tableTo": "kobo_devices", + "columnsFrom": ["device_id", "user_id"], + "columnsTo": ["id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_device_reading_states": { + "name": "kobo_device_reading_states", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at_kobo": { + "name": "created_at_kobo", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "last_modified_kobo": { + "name": "last_modified_kobo", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "priority_timestamp": { + "name": "priority_timestamp", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "current_bookmark": { + "name": "current_bookmark", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "statistics": { + "name": "statistics", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status_info": { + "name": "status_info", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "kobo_device_reading_states_user_id_users_id_fk": { + "name": "kobo_device_reading_states_user_id_users_id_fk", + "tableFrom": "kobo_device_reading_states", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_device_reading_states_book_id_books_id_fk": { + "name": "kobo_device_reading_states_book_id_books_id_fk", + "tableFrom": "kobo_device_reading_states", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_device_reading_states_delivery_id_kobo_device_deliveries_id_fk": { + "name": "kobo_device_reading_states_delivery_id_kobo_device_deliveries_id_fk", + "tableFrom": "kobo_device_reading_states", + "tableTo": "kobo_device_deliveries", + "columnsFrom": ["delivery_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "kobo_device_reading_states_owner_fk": { + "name": "kobo_device_reading_states_owner_fk", + "tableFrom": "kobo_device_reading_states", + "tableTo": "kobo_devices", + "columnsFrom": ["device_id", "user_id"], + "columnsTo": ["id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "kobo_device_reading_states_user_id_device_id_book_id_pk": { + "name": "kobo_device_reading_states_user_id_device_id_book_id_pk", + "columns": ["user_id", "device_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_devices": { + "name": "kobo_devices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "client_device_id": { + "name": "client_device_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kobo_devices_user_id_idx": { + "name": "kobo_devices_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_devices_user_id_users_id_fk": { + "name": "kobo_devices_user_id_users_id_fk", + "tableFrom": "kobo_devices", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kobo_devices_token_unique": { + "name": "kobo_devices_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + }, + "kobo_devices_client_device_id_unique": { + "name": "kobo_devices_client_device_id_unique", + "nullsNotDistinct": false, + "columns": ["client_device_id"] + }, + "kobo_devices_id_user_id_unique": { + "name": "kobo_devices_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_library_snapshots": { + "name": "kobo_library_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "legacy_device_cutoff_at": { + "name": "legacy_device_cutoff_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "kobo_library_snapshots_user_id_users_id_fk": { + "name": "kobo_library_snapshots_user_id_users_id_fk", + "tableFrom": "kobo_library_snapshots", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kobo_library_snapshots_user_id_unique": { + "name": "kobo_library_snapshots_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_snapshot_books": { + "name": "kobo_snapshot_books", + "schema": "", + "columns": { + "snapshot_id": { + "name": "snapshot_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "synced": { + "name": "synced", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pending_delete": { + "name": "pending_delete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_new": { + "name": "is_new", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "removed_by_device": { + "name": "removed_by_device", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_hash": { + "name": "file_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "delivery_hash": { + "name": "delivery_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "metadata_hash": { + "name": "metadata_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kobo_snapshot_books_book_id_idx": { + "name": "kobo_snapshot_books_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kobo_snapshot_books_snapshot_synced_book_idx": { + "name": "kobo_snapshot_books_snapshot_synced_book_idx", + "columns": [ + { + "expression": "snapshot_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "synced", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_snapshot_books_snapshot_id_kobo_library_snapshots_id_fk": { + "name": "kobo_snapshot_books_snapshot_id_kobo_library_snapshots_id_fk", + "tableFrom": "kobo_snapshot_books", + "tableTo": "kobo_library_snapshots", + "columnsFrom": ["snapshot_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_snapshot_books_book_id_books_id_fk": { + "name": "kobo_snapshot_books_book_id_books_id_fk", + "tableFrom": "kobo_snapshot_books", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "kobo_snapshot_books_snapshot_id_book_id_pk": { + "name": "kobo_snapshot_books_snapshot_id_book_id_pk", + "columns": ["snapshot_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_device_library_snapshots": { + "name": "kobo_device_library_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kobo_device_library_snapshots_user_id_idx": { + "name": "kobo_device_library_snapshots_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_device_library_snapshots_user_id_users_id_fk": { + "name": "kobo_device_library_snapshots_user_id_users_id_fk", + "tableFrom": "kobo_device_library_snapshots", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_device_library_snapshots_device_owner_fk": { + "name": "kobo_device_library_snapshots_device_owner_fk", + "tableFrom": "kobo_device_library_snapshots", + "tableTo": "kobo_devices", + "columnsFrom": ["device_id", "user_id"], + "columnsTo": ["id", "user_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kobo_device_library_snapshots_device_id_unique": { + "name": "kobo_device_library_snapshots_device_id_unique", + "nullsNotDistinct": false, + "columns": ["device_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_reading_states": { + "name": "kobo_reading_states", + "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 + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at_kobo": { + "name": "created_at_kobo", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "last_modified_kobo": { + "name": "last_modified_kobo", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "priority_timestamp": { + "name": "priority_timestamp", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "current_bookmark": { + "name": "current_bookmark", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "statistics": { + "name": "statistics", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status_info": { + "name": "status_info", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kobo_reading_states_book_id_idx": { + "name": "kobo_reading_states_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_reading_states_user_id_users_id_fk": { + "name": "kobo_reading_states_user_id_users_id_fk", + "tableFrom": "kobo_reading_states", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_reading_states_book_id_books_id_fk": { + "name": "kobo_reading_states_book_id_books_id_fk", + "tableFrom": "kobo_reading_states", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kobo_reading_states_user_id_book_id_unique": { + "name": "kobo_reading_states_user_id_book_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "book_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_device_snapshot_books": { + "name": "kobo_device_snapshot_books", + "schema": "", + "columns": { + "snapshot_id": { + "name": "snapshot_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "delivery_retry_at": { + "name": "delivery_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "delivery_failure_code": { + "name": "delivery_failure_code", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "synced": { + "name": "synced", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pending_delete": { + "name": "pending_delete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_new": { + "name": "is_new", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "removed_by_device": { + "name": "removed_by_device", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "needs_legacy_numeric_removal": { + "name": "needs_legacy_numeric_removal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_hash": { + "name": "file_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "delivery_hash": { + "name": "delivery_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "metadata_hash": { + "name": "metadata_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kobo_device_snapshot_books_book_id_idx": { + "name": "kobo_device_snapshot_books_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kobo_device_snapshot_books_snapshot_synced_book_idx": { + "name": "kobo_device_snapshot_books_snapshot_synced_book_idx", + "columns": [ + { + "expression": "snapshot_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "synced", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_device_snapshot_books_snapshot_id_kobo_device_library_snapshots_id_fk": { + "name": "kobo_device_snapshot_books_snapshot_id_kobo_device_library_snapshots_id_fk", + "tableFrom": "kobo_device_snapshot_books", + "tableTo": "kobo_device_library_snapshots", + "columnsFrom": ["snapshot_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_device_snapshot_books_book_id_books_id_fk": { + "name": "kobo_device_snapshot_books_book_id_books_id_fk", + "tableFrom": "kobo_device_snapshot_books", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "kobo_device_snapshot_books_snapshot_id_book_id_pk": { + "name": "kobo_device_snapshot_books_snapshot_id_book_id_pk", + "columns": ["snapshot_id", "book_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kobo_sync_history": { + "name": "kobo_sync_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "event": { + "name": "event", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "counts": { + "name": "counts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "error_class": { + "name": "error_class", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kobo_sync_history_user_created_idx": { + "name": "kobo_sync_history_user_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kobo_sync_history_device_created_idx": { + "name": "kobo_sync_history_device_created_idx", + "columns": [ + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kobo_sync_history_user_id_users_id_fk": { + "name": "kobo_sync_history_user_id_users_id_fk", + "tableFrom": "kobo_sync_history", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kobo_sync_history_device_id_kobo_devices_id_fk": { + "name": "kobo_sync_history_device_id_kobo_devices_id_fk", + "tableFrom": "kobo_sync_history", + "tableTo": "kobo_devices", + "columnsFrom": ["device_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kobo_sync_history_event_chk": { + "name": "kobo_sync_history_event_chk", + "value": "\"kobo_sync_history\".\"event\" in ('library_sync', 'book_download', 'progress_update', 'annotations_pull', 'annotations_push')" + }, + "kobo_sync_history_status_chk": { + "name": "kobo_sync_history_status_chk", + "value": "\"kobo_sync_history\".\"status\" in ('success', 'failed')" + }, + "kobo_sync_history_duration_nonnegative_chk": { + "name": "kobo_sync_history_duration_nonnegative_chk", + "value": "\"kobo_sync_history\".\"duration_ms\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.kobo_sync_settings": { + "name": "kobo_sync_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reading_threshold": { + "name": "reading_threshold", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "finished_threshold": { + "name": "finished_threshold", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 99 + }, + "convert_to_kepub": { + "name": "convert_to_kepub", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "force_enable_hyphenation": { + "name": "force_enable_hyphenation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "kepub_conversion_limit_mb": { + "name": "kepub_conversion_limit_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "two_way_progress_sync": { + "name": "two_way_progress_sync", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sync_bookorbit_annotations_to_kobo": { + "name": "sync_bookorbit_annotations_to_kobo", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "read_aloud_delivery": { + "name": "read_aloud_delivery", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "store_sync": { + "name": "store_sync", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "kobo_sync_settings_user_id_users_id_fk": { + "name": "kobo_sync_settings_user_id_users_id_fk", + "tableFrom": "kobo_sync_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kobo_sync_settings_user_id_unique": { + "name": "kobo_sync_settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": { + "kobo_sync_settings_reading_threshold_range_chk": { + "name": "kobo_sync_settings_reading_threshold_range_chk", + "value": "\"kobo_sync_settings\".\"reading_threshold\" >= 0 and \"kobo_sync_settings\".\"reading_threshold\" <= 100" + }, + "kobo_sync_settings_finished_threshold_range_chk": { + "name": "kobo_sync_settings_finished_threshold_range_chk", + "value": "\"kobo_sync_settings\".\"finished_threshold\" >= 0 and \"kobo_sync_settings\".\"finished_threshold\" <= 100" + }, + "kobo_sync_settings_conversion_limit_nonnegative_chk": { + "name": "kobo_sync_settings_conversion_limit_nonnegative_chk", + "value": "\"kobo_sync_settings\".\"kepub_conversion_limit_mb\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.book_dock_files": { + "name": "book_dock_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "absolute_path": { + "name": "absolute_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "format": { + "name": "format", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "unit_directory": { + "name": "unit_directory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "embedded_metadata": { + "name": "embedded_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "selected_metadata": { + "name": "selected_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "fetched_metadata": { + "name": "fetched_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cover_path": { + "name": "cover_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_library_id": { + "name": "target_library_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "target_folder_id": { + "name": "target_folder_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "fetched_metadata_sources": { + "name": "fetched_metadata_sources", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata_edited_at": { + "name": "metadata_edited_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "auto_finalize_suppressed": { + "name": "auto_finalize_suppressed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_dock_files_status_idx": { + "name": "book_dock_files_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_dock_files_target_library_id_idx": { + "name": "book_dock_files_target_library_id_idx", + "columns": [ + { + "expression": "target_library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_dock_files_uploaded_by_idx": { + "name": "book_dock_files_uploaded_by_idx", + "columns": [ + { + "expression": "uploaded_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_dock_files_target_library_id_libraries_id_fk": { + "name": "book_dock_files_target_library_id_libraries_id_fk", + "tableFrom": "book_dock_files", + "tableTo": "libraries", + "columnsFrom": ["target_library_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_dock_files_target_folder_id_library_folders_id_fk": { + "name": "book_dock_files_target_folder_id_library_folders_id_fk", + "tableFrom": "book_dock_files", + "tableTo": "library_folders", + "columnsFrom": ["target_folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_dock_files_uploaded_by_users_id_fk": { + "name": "book_dock_files_uploaded_by_users_id_fk", + "tableFrom": "book_dock_files", + "tableTo": "users", + "columnsFrom": ["uploaded_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "book_dock_files_absolute_path_unique": { + "name": "book_dock_files_absolute_path_unique", + "nullsNotDistinct": false, + "columns": ["absolute_path"] + }, + "book_dock_files_unit_directory_unique": { + "name": "book_dock_files_unit_directory_unique", + "nullsNotDistinct": false, + "columns": ["unit_directory"] + } + }, + "policies": {}, + "checkConstraints": { + "book_dock_files_status_chk": { + "name": "book_dock_files_status_chk", + "value": "\"book_dock_files\".\"status\" in ('pending', 'extracting', 'fetching', 'ready', 'error')" + }, + "book_dock_files_confidence_range_chk": { + "name": "book_dock_files_confidence_range_chk", + "value": "\"book_dock_files\".\"confidence\" is null or (\"book_dock_files\".\"confidence\" >= 0 and \"book_dock_files\".\"confidence\" <= 100)" + } + }, + "isRLSEnabled": false + }, + "public.book_dock_unit_files": { + "name": "book_dock_unit_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "dock_file_id": { + "name": "dock_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "absolute_path": { + "name": "absolute_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "bigint", + "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": "'content'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_dock_unit_files_dock_file_id_sort_order_idx": { + "name": "book_dock_unit_files_dock_file_id_sort_order_idx", + "columns": [ + { + "expression": "dock_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_dock_unit_files_dock_file_id_book_dock_files_id_fk": { + "name": "book_dock_unit_files_dock_file_id_book_dock_files_id_fk", + "tableFrom": "book_dock_unit_files", + "tableTo": "book_dock_files", + "columnsFrom": ["dock_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "book_dock_unit_files_absolute_path_unique": { + "name": "book_dock_unit_files_absolute_path_unique", + "nullsNotDistinct": false, + "columns": ["absolute_path"] + } + }, + "policies": {}, + "checkConstraints": { + "book_dock_unit_files_role_chk": { + "name": "book_dock_unit_files_role_chk", + "value": "\"book_dock_unit_files\".\"role\" in ('content', 'cover', 'metadata', 'supplement')" + } + }, + "isRLSEnabled": false + }, + "public.book_request_dedupe_aliases": { + "name": "book_request_dedupe_aliases", + "schema": "", + "columns": { + "request_id": { + "name": "request_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "book_request_dedupe_aliases_key_idx": { + "name": "book_request_dedupe_aliases_key_idx", + "columns": [ + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_request_dedupe_aliases_request_id_book_requests_id_fk": { + "name": "book_request_dedupe_aliases_request_id_book_requests_id_fk", + "tableFrom": "book_request_dedupe_aliases", + "tableTo": "book_requests", + "columnsFrom": ["request_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_request_dedupe_aliases_request_id_dedupe_key_pk": { + "name": "book_request_dedupe_aliases_request_id_dedupe_key_pk", + "columns": ["request_id", "dedupe_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_request_dismissals": { + "name": "book_request_dismissals", + "schema": "", + "columns": { + "request_id": { + "name": "request_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_request_dismissals_user_id_idx": { + "name": "book_request_dismissals_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_request_dismissals_request_id_book_requests_id_fk": { + "name": "book_request_dismissals_request_id_book_requests_id_fk", + "tableFrom": "book_request_dismissals", + "tableTo": "book_requests", + "columnsFrom": ["request_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_request_dismissals_user_id_users_id_fk": { + "name": "book_request_dismissals_user_id_users_id_fk", + "tableFrom": "book_request_dismissals", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_request_dismissals_request_id_user_id_pk": { + "name": "book_request_dismissals_request_id_user_id_pk", + "columns": ["request_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_request_subscribers": { + "name": "book_request_subscribers", + "schema": "", + "columns": { + "request_id": { + "name": "request_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_request_subscribers_user_id_idx": { + "name": "book_request_subscribers_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_request_subscribers_request_id_book_requests_id_fk": { + "name": "book_request_subscribers_request_id_book_requests_id_fk", + "tableFrom": "book_request_subscribers", + "tableTo": "book_requests", + "columnsFrom": ["request_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_request_subscribers_user_id_users_id_fk": { + "name": "book_request_subscribers_user_id_users_id_fk", + "tableFrom": "book_request_subscribers", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "book_request_subscribers_request_id_user_id_pk": { + "name": "book_request_subscribers_request_id_user_id_pk", + "columns": ["request_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_requests": { + "name": "book_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "media_kind": { + "name": "media_kind", + "type": "book_request_media_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "title": { + "name": "title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "authors": { + "name": "authors", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "series_name": { + "name": "series_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "series_index": { + "name": "series_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "isbn10": { + "name": "isbn10", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "isbn13": { + "name": "isbn13", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "published_year": { + "name": "published_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "language": { + "name": "language", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "cover_url": { + "name": "cover_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_key": { + "name": "provider_key", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "metadata_sources": { + "name": "metadata_sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "preferred_formats": { + "name": "preferred_formats", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_library_id": { + "name": "target_library_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "target_folder_id": { + "name": "target_folder_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "decided_by_user_id": { + "name": "decided_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "decision_note": { + "name": "decision_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "matched_book_id": { + "name": "matched_book_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "book_dock_file_id": { + "name": "book_dock_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status_reason": { + "name": "status_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "failure_meta": { + "name": "failure_meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "self_serve": { + "name": "self_serve", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "fulfiller_user_id": { + "name": "fulfiller_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "companion_request_id": { + "name": "companion_request_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "target_book_id": { + "name": "target_book_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_requests_user_status_idx": { + "name": "book_requests_user_status_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_status_created_at_idx": { + "name": "book_requests_status_created_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_target_library_id_idx": { + "name": "book_requests_target_library_id_idx", + "columns": [ + { + "expression": "target_library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_created_at_idx": { + "name": "book_requests_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_title_idx": { + "name": "book_requests_title_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_matched_book_user_idx": { + "name": "book_requests_matched_book_user_idx", + "columns": [ + { + "expression": "matched_book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"book_requests\".\"matched_book_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_companion_request_id_idx": { + "name": "book_requests_companion_request_id_idx", + "columns": [ + { + "expression": "companion_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"book_requests\".\"companion_request_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_target_book_id_idx": { + "name": "book_requests_target_book_id_idx", + "columns": [ + { + "expression": "target_book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"book_requests\".\"target_book_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_self_serve_idx": { + "name": "book_requests_self_serve_idx", + "columns": [ + { + "expression": "self_serve", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"book_requests\".\"self_serve\"", + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_requests_active_dedupe_key_uidx": { + "name": "book_requests_active_dedupe_key_uidx", + "columns": [ + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"book_requests\".\"status\" in ('pending', 'approved', 'searching', 'grabbed', 'downloading', 'importing', 'needs_review')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_requests_user_id_users_id_fk": { + "name": "book_requests_user_id_users_id_fk", + "tableFrom": "book_requests", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_requests_target_library_id_libraries_id_fk": { + "name": "book_requests_target_library_id_libraries_id_fk", + "tableFrom": "book_requests", + "tableTo": "libraries", + "columnsFrom": ["target_library_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_target_folder_id_library_folders_id_fk": { + "name": "book_requests_target_folder_id_library_folders_id_fk", + "tableFrom": "book_requests", + "tableTo": "library_folders", + "columnsFrom": ["target_folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_created_by_user_id_users_id_fk": { + "name": "book_requests_created_by_user_id_users_id_fk", + "tableFrom": "book_requests", + "tableTo": "users", + "columnsFrom": ["created_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_decided_by_user_id_users_id_fk": { + "name": "book_requests_decided_by_user_id_users_id_fk", + "tableFrom": "book_requests", + "tableTo": "users", + "columnsFrom": ["decided_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_matched_book_id_books_id_fk": { + "name": "book_requests_matched_book_id_books_id_fk", + "tableFrom": "book_requests", + "tableTo": "books", + "columnsFrom": ["matched_book_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_book_dock_file_id_book_dock_files_id_fk": { + "name": "book_requests_book_dock_file_id_book_dock_files_id_fk", + "tableFrom": "book_requests", + "tableTo": "book_dock_files", + "columnsFrom": ["book_dock_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_fulfiller_user_id_users_id_fk": { + "name": "book_requests_fulfiller_user_id_users_id_fk", + "tableFrom": "book_requests", + "tableTo": "users", + "columnsFrom": ["fulfiller_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_companion_request_id_book_requests_id_fk": { + "name": "book_requests_companion_request_id_book_requests_id_fk", + "tableFrom": "book_requests", + "tableTo": "book_requests", + "columnsFrom": ["companion_request_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_requests_target_book_id_books_id_fk": { + "name": "book_requests_target_book_id_books_id_fk", + "tableFrom": "book_requests", + "tableTo": "books", + "columnsFrom": ["target_book_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_requests_status_chk": { + "name": "book_requests_status_chk", + "value": "\"book_requests\".\"status\" in ('pending', 'approved', 'rejected', 'cancelled', 'searching', 'grabbed', 'downloading', 'importing', 'needs_review', 'available', 'failed')" + }, + "book_requests_series_index_nonnegative_chk": { + "name": "book_requests_series_index_nonnegative_chk", + "value": "\"book_requests\".\"series_index\" is null or \"book_requests\".\"series_index\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.book_request_downloads": { + "name": "book_request_downloads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "download_client_id": { + "name": "download_client_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "indexer_id": { + "name": "indexer_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "automated": { + "name": "automated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "release_title": { + "name": "release_title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "release_guid": { + "name": "release_guid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_size_bytes": { + "name": "release_size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "release_seeders": { + "name": "release_seeders", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "release_format": { + "name": "release_format", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "freeleech": { + "name": "freeleech", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "client_key": { + "name": "client_key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "progress_percent": { + "name": "progress_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "downloaded_bytes": { + "name": "downloaded_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_bytes": { + "name": "total_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "direct_url": { + "name": "direct_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "direct_file_name": { + "name": "direct_file_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "direct_etag": { + "name": "direct_etag", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": false + }, + "direct_last_modified": { + "name": "direct_last_modified", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "content_path": { + "name": "content_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "local_path": { + "name": "local_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "book_dock_file_id": { + "name": "book_dock_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "release_units": { + "name": "release_units", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grabbed_at": { + "name": "grabbed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_progress_at": { + "name": "last_progress_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "imported_at": { + "name": "imported_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_request_downloads_status_idx": { + "name": "book_request_downloads_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_request_downloads_request_latest_idx": { + "name": "book_request_downloads_request_latest_idx", + "columns": [ + { + "expression": "request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_request_downloads_book_dock_file_id_idx": { + "name": "book_request_downloads_book_dock_file_id_idx", + "columns": [ + { + "expression": "book_dock_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_request_downloads_active_hash_uidx": { + "name": "book_request_downloads_active_hash_uidx", + "columns": [ + { + "expression": "coalesce(\"download_client_id\", 0)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "client_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"book_request_downloads\".\"status\" in ('queued', 'downloading', 'completed', 'importing', 'needs_review')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_request_downloads_request_id_book_requests_id_fk": { + "name": "book_request_downloads_request_id_book_requests_id_fk", + "tableFrom": "book_request_downloads", + "tableTo": "book_requests", + "columnsFrom": ["request_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "book_request_downloads_download_client_id_download_clients_id_fk": { + "name": "book_request_downloads_download_client_id_download_clients_id_fk", + "tableFrom": "book_request_downloads", + "tableTo": "download_clients", + "columnsFrom": ["download_client_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_request_downloads_indexer_id_request_indexers_id_fk": { + "name": "book_request_downloads_indexer_id_request_indexers_id_fk", + "tableFrom": "book_request_downloads", + "tableTo": "request_indexers", + "columnsFrom": ["indexer_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "book_request_downloads_book_dock_file_id_book_dock_files_id_fk": { + "name": "book_request_downloads_book_dock_file_id_book_dock_files_id_fk", + "tableFrom": "book_request_downloads", + "tableTo": "book_dock_files", + "columnsFrom": ["book_dock_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_request_downloads_source_chk": { + "name": "book_request_downloads_source_chk", + "value": "\"book_request_downloads\".\"source\" in ('magnet', 'torrent_file', 'direct_url', 'nzb_file')" + }, + "book_request_downloads_status_chk": { + "name": "book_request_downloads_status_chk", + "value": "\"book_request_downloads\".\"status\" in ('queued', 'downloading', 'completed', 'importing', 'needs_review', 'imported', 'failed')" + }, + "book_request_downloads_progress_range_chk": { + "name": "book_request_downloads_progress_range_chk", + "value": "\"book_request_downloads\".\"progress_percent\" >= 0 and \"book_request_downloads\".\"progress_percent\" <= 100" + } + }, + "isRLSEnabled": false + }, + "public.download_client_path_mappings": { + "name": "download_client_path_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "download_client_id": { + "name": "download_client_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "remote_path": { + "name": "remote_path", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": true + }, + "local_path": { + "name": "local_path", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "download_client_path_mappings_client_remote_uidx": { + "name": "download_client_path_mappings_client_remote_uidx", + "columns": [ + { + "expression": "download_client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "remote_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "download_client_path_mappings_download_client_id_download_clients_id_fk": { + "name": "download_client_path_mappings_download_client_id_download_clients_id_fk", + "tableFrom": "download_client_path_mappings", + "tableTo": "download_clients", + "columnsFrom": ["download_client_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.download_clients": { + "name": "download_clients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "adapter_type": { + "name": "adapter_type", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "credentials_enc": { + "name": "credentials_enc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "default": "'bookorbit'" + }, + "use_hardlinks": { + "name": "use_hardlinks", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_private_address": { + "name": "allow_private_address", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_tested_at": { + "name": "last_tested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_test_ok": { + "name": "last_test_ok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "download_clients_name_lower_uidx": { + "name": "download_clients_name_lower_uidx", + "columns": [ + { + "expression": "lower(\"name\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "download_clients_enabled_priority_idx": { + "name": "download_clients_enabled_priority_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "download_clients_adapter_type_chk": { + "name": "download_clients_adapter_type_chk", + "value": "\"download_clients\".\"adapter_type\" in ('qbittorrent', 'transmission', 'deluge', 'nzbget', 'sabnzbd')" + }, + "download_clients_priority_range_chk": { + "name": "download_clients_priority_range_chk", + "value": "\"download_clients\".\"priority\" >= 1 and \"download_clients\".\"priority\" <= 100" + } + }, + "isRLSEnabled": false + }, + "public.request_indexers": { + "name": "request_indexers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "adapter_type": { + "name": "adapter_type", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "manager_id": { + "name": "manager_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "manager_external_id": { + "name": "manager_external_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "manager_available": { + "name": "manager_available", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "manager_last_seen_at": { + "name": "manager_last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "manager_metadata": { + "name": "manager_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials_enc": { + "name": "credentials_enc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allow_private_address": { + "name": "allow_private_address", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "apply_tracker_seed_goals": { + "name": "apply_tracker_seed_goals", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "seed_ratio_goal": { + "name": "seed_ratio_goal", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "seed_time_minutes": { + "name": "seed_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "categories": { + "name": "categories", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "disabled_media_kinds": { + "name": "disabled_media_kinds", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "isbn_search_disabled": { + "name": "isbn_search_disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "network_profile": { + "name": "network_profile", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_tested_at": { + "name": "last_tested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_test_ok": { + "name": "last_test_ok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_search_at": { + "name": "last_search_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_search_ok": { + "name": "last_search_ok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "last_search_error": { + "name": "last_search_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "search_failure_streak": { + "name": "search_failure_streak", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "request_indexers_name_lower_uidx": { + "name": "request_indexers_name_lower_uidx", + "columns": [ + { + "expression": "lower(\"name\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "request_indexers_manager_external_uidx": { + "name": "request_indexers_manager_external_uidx", + "columns": [ + { + "expression": "manager_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manager_external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "request_indexers_enabled_idx": { + "name": "request_indexers_enabled_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "request_indexers_manager_idx": { + "name": "request_indexers_manager_idx", + "columns": [ + { + "expression": "manager_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "request_indexers_manager_id_request_indexer_managers_id_fk": { + "name": "request_indexers_manager_id_request_indexer_managers_id_fk", + "tableFrom": "request_indexers", + "tableTo": "request_indexer_managers", + "columnsFrom": ["manager_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "request_indexers_adapter_type_chk": { + "name": "request_indexers_adapter_type_chk", + "value": "\"request_indexers\".\"adapter_type\" ~ '^[a-z0-9][a-z0-9-]{0,29}$'" + }, + "request_indexers_manager_fields_chk": { + "name": "request_indexers_manager_fields_chk", + "value": "(\"request_indexers\".\"manager_id\" is null and \"request_indexers\".\"manager_external_id\" is null and \"request_indexers\".\"manager_metadata\" is null) or (\"request_indexers\".\"manager_id\" is not null and \"request_indexers\".\"manager_external_id\" is not null and \"request_indexers\".\"manager_metadata\" is not null and \"request_indexers\".\"credentials_enc\" is null and \"request_indexers\".\"adapter_type\" in ('torznab', 'newznab'))" + }, + "request_indexers_seed_ratio_goal_chk": { + "name": "request_indexers_seed_ratio_goal_chk", + "value": "\"request_indexers\".\"seed_ratio_goal\" is null or (\"request_indexers\".\"seed_ratio_goal\" > 0 and \"request_indexers\".\"seed_ratio_goal\" < 'Infinity'::double precision)" + }, + "request_indexers_seed_time_minutes_chk": { + "name": "request_indexers_seed_time_minutes_chk", + "value": "\"request_indexers\".\"seed_time_minutes\" is null or \"request_indexers\".\"seed_time_minutes\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.request_indexer_managers": { + "name": "request_indexer_managers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credentials_enc": { + "name": "credentials_enc", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allow_private_address": { + "name": "allow_private_address", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sync_new_indexers": { + "name": "sync_new_indexers", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "per_indexer_timeout_seconds": { + "name": "per_indexer_timeout_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20 + }, + "overall_search_budget_seconds": { + "name": "overall_search_budget_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 60 + }, + "auto_expand_categories": { + "name": "auto_expand_categories", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "inherit_seed_limits": { + "name": "inherit_seed_limits", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "network_profile": { + "name": "network_profile", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "last_tested_at": { + "name": "last_tested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_test_ok": { + "name": "last_test_ok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_sync_ok": { + "name": "last_sync_ok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "last_sync_error": { + "name": "last_sync_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "request_indexer_managers_name_lower_uidx": { + "name": "request_indexer_managers_name_lower_uidx", + "columns": [ + { + "expression": "lower(\"name\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "request_indexer_managers_type_chk": { + "name": "request_indexer_managers_type_chk", + "value": "\"request_indexer_managers\".\"type\" in ('prowlarr')" + }, + "request_indexer_managers_timeout_chk": { + "name": "request_indexer_managers_timeout_chk", + "value": "\"request_indexer_managers\".\"per_indexer_timeout_seconds\" between 5 and 180" + }, + "request_indexer_managers_budget_chk": { + "name": "request_indexer_managers_budget_chk", + "value": "\"request_indexer_managers\".\"overall_search_budget_seconds\" between 5 and 300" + } + }, + "isRLSEnabled": false + }, + "public.file_write_log": { + "name": "file_write_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "book_id": { + "name": "book_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "format": { + "name": "format", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "fields_written": { + "name": "fields_written", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "triggered_by": { + "name": "triggered_by", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "written_at": { + "name": "written_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "fwl_book_id_written_at_idx": { + "name": "fwl_book_id_written_at_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "written_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "file_write_log_book_file_id_idx": { + "name": "file_write_log_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_write_log_book_id_books_id_fk": { + "name": "file_write_log_book_id_books_id_fk", + "tableFrom": "file_write_log", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "file_write_log_book_file_id_book_files_id_fk": { + "name": "file_write_log_book_file_id_book_files_id_fk", + "tableFrom": "file_write_log", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "file_write_log_user_id_users_id_fk": { + "name": "file_write_log_user_id_users_id_fk", + "tableFrom": "file_write_log", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "file_write_log_status_chk": { + "name": "file_write_log_status_chk", + "value": "\"file_write_log\".\"status\" in ('success', 'skipped', 'failed')" + }, + "file_write_log_triggered_by_chk": { + "name": "file_write_log_triggered_by_chk", + "value": "\"file_write_log\".\"triggered_by\" in ('auto', 'sync')" + }, + "file_write_log_duration_ms_nonnegative_chk": { + "name": "file_write_log_duration_ms_nonnegative_chk", + "value": "\"file_write_log\".\"duration_ms\" is null or \"file_write_log\".\"duration_ms\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.email_templates": { + "name": "email_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_text": { + "name": "body_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_default": { + "name": "is_default", + "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 with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_templates_one_default_per_user_uidx": { + "name": "email_templates_one_default_per_user_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"email_templates\".\"is_default\" = true and \"email_templates\".\"user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_templates_system_name_unique": { + "name": "email_templates_system_name_unique", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"email_templates\".\"user_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_templates_user_id_users_id_fk": { + "name": "email_templates_user_id_users_id_fk", + "tableFrom": "email_templates", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_templates_user_id_name_unique": { + "name": "email_templates_user_id_name_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "name"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_providers": { + "name": "email_providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "host": { + "name": "host", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "password_enc": { + "name": "password_enc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_name": { + "name": "from_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "from_address": { + "name": "from_address", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "auth": { + "name": "auth", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "ssl": { + "name": "ssl", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "start_tls": { + "name": "start_tls", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_shared": { + "name": "is_shared", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_system_provider": { + "name": "is_system_provider", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "tls_reject_unauthorized": { + "name": "tls_reject_unauthorized", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_tested_at": { + "name": "last_tested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_test_ok": { + "name": "last_test_ok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "last_test_error": { + "name": "last_test_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_providers_one_default_per_user_uidx": { + "name": "email_providers_one_default_per_user_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"email_providers\".\"is_default\" = true and \"email_providers\".\"user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_providers_one_system_provider_uidx": { + "name": "email_providers_one_system_provider_uidx", + "columns": [ + { + "expression": "is_system_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"email_providers\".\"is_system_provider\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_providers_user_id_users_id_fk": { + "name": "email_providers_user_id_users_id_fk", + "tableFrom": "email_providers", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_providers_user_id_name_unique": { + "name": "email_providers_user_id_name_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "name"] + }, + "email_providers_id_user_id_unique": { + "name": "email_providers_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": { + "email_providers_port_range_chk": { + "name": "email_providers_port_range_chk", + "value": "\"email_providers\".\"port\" >= 1 and \"email_providers\".\"port\" <= 65535" + } + }, + "isRLSEnabled": false + }, + "public.email_recipient_group_members": { + "name": "email_recipient_group_members", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "recipient_id": { + "name": "recipient_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "email_recipient_group_members_user_id_users_id_fk": { + "name": "email_recipient_group_members_user_id_users_id_fk", + "tableFrom": "email_recipient_group_members", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_recipient_group_members_group_id_email_recipient_groups_id_fk": { + "name": "email_recipient_group_members_group_id_email_recipient_groups_id_fk", + "tableFrom": "email_recipient_group_members", + "tableTo": "email_recipient_groups", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_recipient_group_members_recipient_id_email_recipients_id_fk": { + "name": "email_recipient_group_members_recipient_id_email_recipients_id_fk", + "tableFrom": "email_recipient_group_members", + "tableTo": "email_recipients", + "columnsFrom": ["recipient_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_recipient_group_members_group_user_fk": { + "name": "email_recipient_group_members_group_user_fk", + "tableFrom": "email_recipient_group_members", + "tableTo": "email_recipient_groups", + "columnsFrom": ["group_id", "user_id"], + "columnsTo": ["id", "user_id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "email_recipient_group_members_recipient_user_fk": { + "name": "email_recipient_group_members_recipient_user_fk", + "tableFrom": "email_recipient_group_members", + "tableTo": "email_recipients", + "columnsFrom": ["recipient_id", "user_id"], + "columnsTo": ["id", "user_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "email_recipient_group_members_group_id_recipient_id_pk": { + "name": "email_recipient_group_members_group_id_recipient_id_pk", + "columns": ["group_id", "recipient_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_recipient_groups": { + "name": "email_recipient_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "default_template_id": { + "name": "default_template_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "email_recipient_groups_user_id_users_id_fk": { + "name": "email_recipient_groups_user_id_users_id_fk", + "tableFrom": "email_recipient_groups", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_recipient_groups_default_template_id_email_templates_id_fk": { + "name": "email_recipient_groups_default_template_id_email_templates_id_fk", + "tableFrom": "email_recipient_groups", + "tableTo": "email_templates", + "columnsFrom": ["default_template_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_recipient_groups_user_id_name_unique": { + "name": "email_recipient_groups_user_id_name_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "name"] + }, + "email_recipient_groups_id_user_id_unique": { + "name": "email_recipient_groups_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_recipients": { + "name": "email_recipients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "device_type": { + "name": "device_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "read_aloud_delivery": { + "name": "read_aloud_delivery", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "preferred_format": { + "name": "preferred_format", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "default_template_id": { + "name": "default_template_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_recipients_user_email_lower_uidx": { + "name": "email_recipients_user_email_lower_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"email\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_recipients_one_default_per_user_uidx": { + "name": "email_recipients_one_default_per_user_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"email_recipients\".\"is_default\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_recipients_user_id_users_id_fk": { + "name": "email_recipients_user_id_users_id_fk", + "tableFrom": "email_recipients", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_recipients_default_template_id_email_templates_id_fk": { + "name": "email_recipients_default_template_id_email_templates_id_fk", + "tableFrom": "email_recipients", + "tableTo": "email_templates", + "columnsFrom": ["default_template_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_recipients_user_id_email_unique": { + "name": "email_recipients_user_id_email_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "email"] + }, + "email_recipients_id_user_id_unique": { + "name": "email_recipients_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": { + "email_recipients_device_type_chk": { + "name": "email_recipients_device_type_chk", + "value": "\"email_recipients\".\"device_type\" is null or \"email_recipients\".\"device_type\" in ('kindle', 'kobo', 'other')" + }, + "email_recipients_preferred_format_chk": { + "name": "email_recipients_preferred_format_chk", + "value": "\"email_recipients\".\"preferred_format\" is null or \"email_recipients\".\"preferred_format\" in ('epub', 'pdf', 'mobi', 'azw3', 'cbz', 'cbr', 'djvu')" + } + }, + "isRLSEnabled": false + }, + "public.email_preferences": { + "name": "email_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "default_provider_id": { + "name": "default_provider_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "default_recipient_id": { + "name": "default_recipient_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "default_template_id": { + "name": "default_template_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "email_preferences_user_id_users_id_fk": { + "name": "email_preferences_user_id_users_id_fk", + "tableFrom": "email_preferences", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_preferences_default_provider_id_email_providers_id_fk": { + "name": "email_preferences_default_provider_id_email_providers_id_fk", + "tableFrom": "email_preferences", + "tableTo": "email_providers", + "columnsFrom": ["default_provider_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_preferences_default_recipient_id_email_recipients_id_fk": { + "name": "email_preferences_default_recipient_id_email_recipients_id_fk", + "tableFrom": "email_preferences", + "tableTo": "email_recipients", + "columnsFrom": ["default_recipient_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_preferences_default_template_id_email_templates_id_fk": { + "name": "email_preferences_default_template_id_email_templates_id_fk", + "tableFrom": "email_preferences", + "tableTo": "email_templates", + "columnsFrom": ["default_template_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_preferences_default_recipient_owner_fk": { + "name": "email_preferences_default_recipient_owner_fk", + "tableFrom": "email_preferences", + "tableTo": "email_recipients", + "columnsFrom": ["default_recipient_id", "user_id"], + "columnsTo": ["id", "user_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_preferences_user_id_unique": { + "name": "email_preferences_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_send_log": { + "name": "email_send_log", + "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": false + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "template_id": { + "name": "template_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "to_email": { + "name": "to_email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "to_name": { + "name": "to_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "delivery_policy": { + "name": "delivery_policy", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "source_revision": { + "name": "source_revision", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "artifact_id": { + "name": "artifact_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "delivery_metadata": { + "name": "delivery_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "actual_variant": { + "name": "actual_variant", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "device_type": { + "name": "device_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "recipient_id": { + "name": "recipient_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "dispatch_phase": { + "name": "dispatch_phase", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_send_log_user_created_at_idx": { + "name": "email_send_log_user_created_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_send_log_book_id_idx": { + "name": "email_send_log_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_send_log_book_file_id_idx": { + "name": "email_send_log_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_send_log_created_at_idx": { + "name": "email_send_log_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_send_log_status_next_retry_idx": { + "name": "email_send_log_status_next_retry_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_retry_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_send_log_user_id_users_id_fk": { + "name": "email_send_log_user_id_users_id_fk", + "tableFrom": "email_send_log", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_send_log_book_id_books_id_fk": { + "name": "email_send_log_book_id_books_id_fk", + "tableFrom": "email_send_log", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_send_log_book_file_id_book_files_id_fk": { + "name": "email_send_log_book_file_id_book_files_id_fk", + "tableFrom": "email_send_log", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_send_log_provider_id_email_providers_id_fk": { + "name": "email_send_log_provider_id_email_providers_id_fk", + "tableFrom": "email_send_log", + "tableTo": "email_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "email_send_log_template_id_email_templates_id_fk": { + "name": "email_send_log_template_id_email_templates_id_fk", + "tableFrom": "email_send_log", + "tableTo": "email_templates", + "columnsFrom": ["template_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "email_send_log_status_chk": { + "name": "email_send_log_status_chk", + "value": "\"email_send_log\".\"status\" in ('pending', 'sent', 'failed')" + }, + "email_send_log_attempt_count_nonnegative_chk": { + "name": "email_send_log_attempt_count_nonnegative_chk", + "value": "\"email_send_log\".\"attempt_count\" >= 0" + }, + "email_send_log_sent_after_created_chk": { + "name": "email_send_log_sent_after_created_chk", + "value": "\"email_send_log\".\"sent_at\" is null or \"email_send_log\".\"sent_at\" >= \"email_send_log\".\"created_at\"" + } + }, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_url": { + "name": "action_url", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "read": { + "name": "read", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "group_key": { + "name": "group_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_user_id_idx": { + "name": "notifications_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notifications_user_unread_idx": { + "name": "notifications_user_unread_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "read", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notifications_created_at_idx": { + "name": "notifications_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notifications_user_updated_at_idx": { + "name": "notifications_user_updated_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notifications_updated_at_idx": { + "name": "notifications_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notifications_user_unread_group_key_idx": { + "name": "notifications_user_unread_group_key_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"notifications\".\"read\" = false and \"notifications\".\"group_key\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dismissed_duplicate_pairs": { + "name": "dismissed_duplicate_pairs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "entity_id_a": { + "name": "entity_id_a", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "entity_id_b": { + "name": "entity_id_b", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dismissed_by": { + "name": "dismissed_by", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dismissed_duplicate_pairs_entity_a_idx": { + "name": "dismissed_duplicate_pairs_entity_a_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id_a", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dismissed_duplicate_pairs_entity_b_idx": { + "name": "dismissed_duplicate_pairs_entity_b_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id_b", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dismissed_duplicate_pairs_dismissed_by_users_id_fk": { + "name": "dismissed_duplicate_pairs_dismissed_by_users_id_fk", + "tableFrom": "dismissed_duplicate_pairs", + "tableTo": "users", + "columnsFrom": ["dismissed_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "dismissed_duplicate_pairs_unique": { + "name": "dismissed_duplicate_pairs_unique", + "nullsNotDistinct": false, + "columns": ["entity_type", "entity_id_a", "entity_id_b"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dismissed_inline_duplicate_pairs": { + "name": "dismissed_inline_duplicate_pairs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "value_a": { + "name": "value_a", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "value_b": { + "name": "value_b", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dismissed_by": { + "name": "dismissed_by", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dismissed_inline_pairs_entity_a_idx": { + "name": "dismissed_inline_pairs_entity_a_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value_a", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "dismissed_inline_pairs_entity_b_idx": { + "name": "dismissed_inline_pairs_entity_b_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value_b", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dismissed_inline_duplicate_pairs_dismissed_by_users_id_fk": { + "name": "dismissed_inline_duplicate_pairs_dismissed_by_users_id_fk", + "tableFrom": "dismissed_inline_duplicate_pairs", + "tableTo": "users", + "columnsFrom": ["dismissed_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "dismissed_inline_duplicate_pairs_unique": { + "name": "dismissed_inline_duplicate_pairs_unique", + "nullsNotDistinct": false, + "columns": ["entity_type", "value_a", "value_b"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entity_duplicate_candidates": { + "name": "entity_duplicate_candidates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "entity_id_a": { + "name": "entity_id_a", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "entity_id_b": { + "name": "entity_id_b", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sim_score": { + "name": "sim_score", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "computed_at": { + "name": "computed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "entity_dup_candidates_type_sim_idx": { + "name": "entity_dup_candidates_type_sim_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sim_score", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "entity_dup_candidates_entity_a_idx": { + "name": "entity_dup_candidates_entity_a_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id_a", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "entity_dup_candidates_entity_b_idx": { + "name": "entity_dup_candidates_entity_b_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id_b", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entity_duplicate_candidates_unique": { + "name": "entity_duplicate_candidates_unique", + "nullsNotDistinct": false, + "columns": ["entity_type", "entity_id_a", "entity_id_b"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entity_duplicate_scan_status": { + "name": "entity_duplicate_scan_status", + "schema": "", + "columns": { + "entity_type": { + "name": "entity_type", + "type": "varchar(50)", + "primaryKey": true, + "notNull": true + }, + "is_computing": { + "name": "is_computing", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "computed_at": { + "name": "computed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "total_pairs": { + "name": "total_pairs", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "threshold": { + "name": "threshold", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "processed_count": { + "name": "processed_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_count": { + "name": "total_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server_fonts": { + "name": "server_fonts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "family_name": { + "name": "family_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "stored_file_name": { + "name": "stored_file_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "format": { + "name": "format", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 400 + }, + "style": { + "name": "style", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "weight_min": { + "name": "weight_min", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "weight_max": { + "name": "weight_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "instances": { + "name": "instances", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "file_hash": { + "name": "file_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sf_hash_uidx": { + "name": "sf_hash_uidx", + "columns": [ + { + "expression": "file_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sf_family_weight_style_uidx": { + "name": "sf_family_weight_style_uidx", + "columns": [ + { + "expression": "family_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "weight", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "style", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "server_fonts_uploaded_by_users_id_fk": { + "name": "server_fonts_uploaded_by_users_id_fk", + "tableFrom": "server_fonts", + "tableTo": "users", + "columnsFrom": ["uploaded_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "server_fonts_format_chk": { + "name": "server_fonts_format_chk", + "value": "\"server_fonts\".\"format\" in ('ttf', 'otf', 'woff', 'woff2')" + }, + "server_fonts_weight_chk": { + "name": "server_fonts_weight_chk", + "value": "\"server_fonts\".\"weight\" >= 1 and \"server_fonts\".\"weight\" <= 1000" + }, + "server_fonts_style_chk": { + "name": "server_fonts_style_chk", + "value": "\"server_fonts\".\"style\" in ('normal', 'italic')" + }, + "server_fonts_weight_range_chk": { + "name": "server_fonts_weight_range_chk", + "value": "(\"server_fonts\".\"weight_min\" is null) = (\"server_fonts\".\"weight_max\" is null) and (\"server_fonts\".\"weight_min\" is null or (\"server_fonts\".\"weight_min\" >= 1 and \"server_fonts\".\"weight_max\" <= 1000 and \"server_fonts\".\"weight_min\" < \"server_fonts\".\"weight_max\"))" + } + }, + "isRLSEnabled": false + }, + "public.user_fonts": { + "name": "user_fonts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "family_name": { + "name": "family_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "stored_file_name": { + "name": "stored_file_name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "format": { + "name": "format", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 400 + }, + "style": { + "name": "style", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "weight_min": { + "name": "weight_min", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "weight_max": { + "name": "weight_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "instances": { + "name": "instances", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "file_hash": { + "name": "file_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uf_user_hash_uidx": { + "name": "uf_user_hash_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "uf_user_family_weight_style_uidx": { + "name": "uf_user_family_weight_style_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "family_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "weight", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "style", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_fonts_user_id_users_id_fk": { + "name": "user_fonts_user_id_users_id_fk", + "tableFrom": "user_fonts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_fonts_format_chk": { + "name": "user_fonts_format_chk", + "value": "\"user_fonts\".\"format\" in ('ttf', 'otf', 'woff', 'woff2')" + }, + "user_fonts_weight_chk": { + "name": "user_fonts_weight_chk", + "value": "\"user_fonts\".\"weight\" >= 1 and \"user_fonts\".\"weight\" <= 1000" + }, + "user_fonts_style_chk": { + "name": "user_fonts_style_chk", + "value": "\"user_fonts\".\"style\" in ('normal', 'italic')" + }, + "user_fonts_weight_range_chk": { + "name": "user_fonts_weight_range_chk", + "value": "(\"user_fonts\".\"weight_min\" is null) = (\"user_fonts\".\"weight_max\" is null) and (\"user_fonts\".\"weight_min\" is null or (\"user_fonts\".\"weight_min\" >= 1 and \"user_fonts\".\"weight_max\" <= 1000 and \"user_fonts\".\"weight_min\" < \"user_fonts\".\"weight_max\"))" + } + }, + "isRLSEnabled": false + }, + "public.book_file_chapters": { + "name": "book_file_chapters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "chapter_index": { + "name": "chapter_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "href": { + "name": "href", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "spine_index": { + "name": "spine_index", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "book_file_chapters_book_file_id_chapter_index_uidx": { + "name": "book_file_chapters_book_file_id_chapter_index_uidx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chapter_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_file_chapters_book_file_id_idx": { + "name": "book_file_chapters_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_file_chapters_book_file_id_book_files_id_fk": { + "name": "book_file_chapters_book_file_id_book_files_id_fk", + "tableFrom": "book_file_chapters", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.book_file_hash_history": { + "name": "book_file_hash_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "file_hash": { + "name": "file_hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "book_file_hash_history_book_file_id_file_hash_idx": { + "name": "book_file_hash_history_book_file_id_file_hash_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "book_file_hash_history_file_hash_idx": { + "name": "book_file_hash_history_file_hash_idx", + "columns": [ + { + "expression": "file_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "book_file_hash_history_book_file_id_book_files_id_fk": { + "name": "book_file_hash_history_book_file_id_book_files_id_fk", + "tableFrom": "book_file_hash_history", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "book_file_hash_history_reason_chk": { + "name": "book_file_hash_history_reason_chk", + "value": "\"book_file_hash_history\".\"reason\" in ('file_write', 'external_change', 'rescan', 'kobo_download')" + } + }, + "isRLSEnabled": false + }, + "public.koreader_book_hash_links": { + "name": "koreader_book_hash_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "koreader_title": { + "name": "koreader_title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "koreader_authors": { + "name": "koreader_authors", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "koreader_last_open": { + "name": "koreader_last_open", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_book_hash_links_user_hash_uidx": { + "name": "koreader_book_hash_links_user_hash_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_book_hash_links_user_updated_idx": { + "name": "koreader_book_hash_links_user_updated_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"updated_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_book_hash_links_book_file_id_idx": { + "name": "koreader_book_hash_links_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_book_hash_links_user_id_users_id_fk": { + "name": "koreader_book_hash_links_user_id_users_id_fk", + "tableFrom": "koreader_book_hash_links", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "koreader_book_hash_links_book_file_id_book_files_id_fk": { + "name": "koreader_book_hash_links_book_file_id_book_files_id_fk", + "tableFrom": "koreader_book_hash_links", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_book_hash_links_hash_chk": { + "name": "koreader_book_hash_links_hash_chk", + "value": "\"koreader_book_hash_links\".\"hash\" ~ '^[0-9a-f]{32}$'" + } + }, + "isRLSEnabled": false + }, + "public.koreader_bookmark_links": { + "name": "koreader_bookmark_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "bookmark_id": { + "name": "bookmark_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "koreader_key": { + "name": "koreader_key", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "device_datetime": { + "name": "device_datetime", + "type": "varchar(19)", + "primaryKey": false, + "notNull": false + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_bookmark_links_bookmark_device_uidx": { + "name": "koreader_bookmark_links_bookmark_device_uidx", + "columns": [ + { + "expression": "bookmark_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_bookmark_links_user_device_key_idx": { + "name": "koreader_bookmark_links_user_device_key_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "koreader_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_bookmark_links_user_device_idx": { + "name": "koreader_bookmark_links_user_device_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_bookmark_links_bookmark_id_idx": { + "name": "koreader_bookmark_links_bookmark_id_idx", + "columns": [ + { + "expression": "bookmark_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_bookmark_links_bookmark_id_bookmarks_id_fk": { + "name": "koreader_bookmark_links_bookmark_id_bookmarks_id_fk", + "tableFrom": "koreader_bookmark_links", + "tableTo": "bookmarks", + "columnsFrom": ["bookmark_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "koreader_bookmark_links_user_id_users_id_fk": { + "name": "koreader_bookmark_links_user_id_users_id_fk", + "tableFrom": "koreader_bookmark_links", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.koreader_device_delivery_settings": { + "name": "koreader_device_delivery_settings", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "read_aloud_delivery": { + "name": "read_aloud_delivery", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "koreader_device_delivery_settings_user_id_users_id_fk": { + "name": "koreader_device_delivery_settings_user_id_users_id_fk", + "tableFrom": "koreader_device_delivery_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "koreader_device_delivery_settings_credential_id_koreader_users_id_fk": { + "name": "koreader_device_delivery_settings_credential_id_koreader_users_id_fk", + "tableFrom": "koreader_device_delivery_settings", + "tableTo": "koreader_users", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_device_delivery_settings_user_id_device_id_pk": { + "name": "koreader_device_delivery_settings_user_id_device_id_pk", + "columns": ["user_id", "device_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_device_delivery_chk": { + "name": "koreader_device_delivery_chk", + "value": "\"koreader_device_delivery_settings\".\"read_aloud_delivery\" in ('original', 'audioless_epub')" + } + }, + "isRLSEnabled": false + }, + "public.koreader_device_progress": { + "name": "koreader_device_progress", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device": { + "name": "device", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "default": "'KOReader'" + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "progress": { + "name": "progress", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "chapter_index": { + "name": "chapter_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sync_timestamp": { + "name": "sync_timestamp", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "orphaned": { + "name": "orphaned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "orphaned_hash": { + "name": "orphaned_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_device_progress_book_user_device_uidx": { + "name": "koreader_device_progress_book_user_device_uidx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"koreader_device_progress\".\"orphaned\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_device_progress_orphaned_hash_idx": { + "name": "koreader_device_progress_orphaned_hash_idx", + "columns": [ + { + "expression": "orphaned_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"koreader_device_progress\".\"orphaned\" = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_device_progress_user_updated_at_idx": { + "name": "koreader_device_progress_user_updated_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_device_progress_book_file_id_idx": { + "name": "koreader_device_progress_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"koreader_device_progress\".\"book_file_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_device_progress_book_file_id_book_files_id_fk": { + "name": "koreader_device_progress_book_file_id_book_files_id_fk", + "tableFrom": "koreader_device_progress", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "koreader_device_progress_user_id_users_id_fk": { + "name": "koreader_device_progress_user_id_users_id_fk", + "tableFrom": "koreader_device_progress", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_device_progress_percentage_range_chk": { + "name": "koreader_device_progress_percentage_range_chk", + "value": "\"koreader_device_progress\".\"percentage\" is null or (\"koreader_device_progress\".\"percentage\" >= 0 and \"koreader_device_progress\".\"percentage\" <= 1)" + } + }, + "isRLSEnabled": false + }, + "public.koreader_device_retirements": { + "name": "koreader_device_retirements", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_device_retirements_user_id_idx": { + "name": "koreader_device_retirements_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_device_retirements_user_id_users_id_fk": { + "name": "koreader_device_retirements_user_id_users_id_fk", + "tableFrom": "koreader_device_retirements", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_device_retirements_user_id_device_id_pk": { + "name": "koreader_device_retirements_user_id_device_id_pk", + "columns": ["user_id", "device_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.koreader_device_settings": { + "name": "koreader_device_settings", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "file_naming_pattern": { + "name": "file_naming_pattern", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "series_file_naming_pattern": { + "name": "series_file_naming_pattern", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "standalone_file_naming_pattern": { + "name": "standalone_file_naming_pattern", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_device_settings_user_id_idx": { + "name": "koreader_device_settings_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_device_settings_user_id_users_id_fk": { + "name": "koreader_device_settings_user_id_users_id_fk", + "tableFrom": "koreader_device_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_device_settings_user_id_device_id_pk": { + "name": "koreader_device_settings_user_id_device_id_pk", + "columns": ["user_id", "device_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.koreader_device_sweeps": { + "name": "koreader_device_sweeps", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "device_model": { + "name": "device_model", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "default": "'KOReader'" + }, + "plugin_version": { + "name": "plugin_version", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "last_sweep_at": { + "name": "last_sweep_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_sweep_books_matched": { + "name": "last_sweep_books_matched", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_sweep_page_stats": { + "name": "last_sweep_page_stats", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_sweep_annotations": { + "name": "last_sweep_annotations", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kds_user_id_idx": { + "name": "kds_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_device_sweeps_user_id_users_id_fk": { + "name": "koreader_device_sweeps_user_id_users_id_fk", + "tableFrom": "koreader_device_sweeps", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_device_sweeps_user_id_device_id_pk": { + "name": "koreader_device_sweeps_user_id_device_id_pk", + "columns": ["user_id", "device_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.koreader_page_stats": { + "name": "koreader_page_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "page": { + "name": "page", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "duration_seconds": { + "name": "duration_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_pages": { + "name": "total_pages", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kps_user_file_device_page_start_uidx": { + "name": "kps_user_file_device_page_start_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "start_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kps_user_file_device_start_idx": { + "name": "kps_user_file_device_start_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "start_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kps_user_id_idx": { + "name": "kps_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_page_stats_book_file_id_idx": { + "name": "koreader_page_stats_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_page_stats_user_id_users_id_fk": { + "name": "koreader_page_stats_user_id_users_id_fk", + "tableFrom": "koreader_page_stats", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "koreader_page_stats_book_file_id_book_files_id_fk": { + "name": "koreader_page_stats_book_file_id_book_files_id_fk", + "tableFrom": "koreader_page_stats", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_page_stats_duration_nonnegative_chk": { + "name": "koreader_page_stats_duration_nonnegative_chk", + "value": "\"koreader_page_stats\".\"duration_seconds\" >= 0" + }, + "koreader_page_stats_page_nonnegative_chk": { + "name": "koreader_page_stats_page_nonnegative_chk", + "value": "\"koreader_page_stats\".\"page\" >= 0" + }, + "koreader_page_stats_total_pages_positive_chk": { + "name": "koreader_page_stats_total_pages_positive_chk", + "value": "\"koreader_page_stats\".\"total_pages\" > 0" + }, + "koreader_page_stats_start_time_positive_chk": { + "name": "koreader_page_stats_start_time_positive_chk", + "value": "\"koreader_page_stats\".\"start_time\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.koreader_progress_reset_devices": { + "name": "koreader_progress_reset_devices", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "converged_at": { + "name": "converged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "koreader_progress_reset_devices_reset_fk": { + "name": "koreader_progress_reset_devices_reset_fk", + "tableFrom": "koreader_progress_reset_devices", + "tableTo": "koreader_progress_resets", + "columnsFrom": ["user_id", "book_file_id"], + "columnsTo": ["user_id", "book_file_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_progress_reset_devices_user_id_book_file_id_device_id_pk": { + "name": "koreader_progress_reset_devices_user_id_book_file_id_device_id_pk", + "columns": ["user_id", "book_file_id", "device_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.koreader_progress_resets": { + "name": "koreader_progress_resets", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reset_at": { + "name": "reset_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_progress_resets_book_file_id_idx": { + "name": "koreader_progress_resets_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_progress_resets_user_id_users_id_fk": { + "name": "koreader_progress_resets_user_id_users_id_fk", + "tableFrom": "koreader_progress_resets", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "koreader_progress_resets_book_file_id_book_files_id_fk": { + "name": "koreader_progress_resets_book_file_id_book_files_id_fk", + "tableFrom": "koreader_progress_resets", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_progress_resets_user_id_book_file_id_pk": { + "name": "koreader_progress_resets_user_id_book_file_id_pk", + "columns": ["user_id", "book_file_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.koreader_session_tombstones": { + "name": "koreader_session_tombstones", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_key": { + "name": "device_key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kst_user_file_device_start_idx": { + "name": "kst_user_file_device_start_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "start_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_session_tombstones_book_file_id_idx": { + "name": "koreader_session_tombstones_book_file_id_idx", + "columns": [ + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_session_tombstones_user_id_users_id_fk": { + "name": "koreader_session_tombstones_user_id_users_id_fk", + "tableFrom": "koreader_session_tombstones", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "koreader_session_tombstones_book_file_id_book_files_id_fk": { + "name": "koreader_session_tombstones_book_file_id_book_files_id_fk", + "tableFrom": "koreader_session_tombstones", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_session_tombstones_range_chk": { + "name": "koreader_session_tombstones_range_chk", + "value": "\"koreader_session_tombstones\".\"end_time\" > \"koreader_session_tombstones\".\"start_time\"" + } + }, + "isRLSEnabled": false + }, + "public.koreader_unmatched_book_devices": { + "name": "koreader_unmatched_book_devices", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "device_id": { + "name": "device_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_unmatched_book_devices_user_device_idx": { + "name": "koreader_unmatched_book_devices_user_device_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_unmatched_book_devices_user_id_users_id_fk": { + "name": "koreader_unmatched_book_devices_user_id_users_id_fk", + "tableFrom": "koreader_unmatched_book_devices", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "koreader_unmatched_book_devices_user_hash_fk": { + "name": "koreader_unmatched_book_devices_user_hash_fk", + "tableFrom": "koreader_unmatched_book_devices", + "tableTo": "koreader_unmatched_books", + "columnsFrom": ["user_id", "hash"], + "columnsTo": ["user_id", "hash"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_unmatched_book_devices_user_id_hash_device_id_pk": { + "name": "koreader_unmatched_book_devices_user_id_hash_device_id_pk", + "columns": ["user_id", "hash", "device_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_unmatched_book_devices_hash_chk": { + "name": "koreader_unmatched_book_devices_hash_chk", + "value": "\"koreader_unmatched_book_devices\".\"hash\" ~ '^[0-9a-f]{32}$'" + } + }, + "isRLSEnabled": false + }, + "public.koreader_unmatched_books": { + "name": "koreader_unmatched_books", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "authors": { + "name": "authors", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_open": { + "name": "last_open", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'statistics'" + }, + "metadata_ambiguous": { + "name": "metadata_ambiguous", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "manual_link_requested": { + "name": "manual_link_requested", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_unmatched_books_user_seen_idx": { + "name": "koreader_unmatched_books_user_seen_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"last_seen_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_unmatched_books_user_id_users_id_fk": { + "name": "koreader_unmatched_books_user_id_users_id_fk", + "tableFrom": "koreader_unmatched_books", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "koreader_unmatched_books_user_id_hash_pk": { + "name": "koreader_unmatched_books_user_id_hash_pk", + "columns": ["user_id", "hash"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_unmatched_books_hash_chk": { + "name": "koreader_unmatched_books_hash_chk", + "value": "\"koreader_unmatched_books\".\"hash\" ~ '^[0-9a-f]{32}$'" + }, + "koreader_unmatched_books_source_chk": { + "name": "koreader_unmatched_books_source_chk", + "value": "\"koreader_unmatched_books\".\"source\" in ('current_file', 'file', 'statistics')" + } + }, + "isRLSEnabled": false + }, + "public.koreader_user_settings": { + "name": "koreader_user_settings", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "default_file_naming_pattern": { + "name": "default_file_naming_pattern", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "koreader_user_settings_user_id_users_id_fk": { + "name": "koreader_user_settings_user_id_users_id_fk", + "tableFrom": "koreader_user_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.koreader_users": { + "name": "koreader_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "password_md5": { + "name": "password_md5", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "sync_enabled": { + "name": "sync_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "read_aloud_delivery": { + "name": "read_aloud_delivery", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "koreader_users_user_id_uidx": { + "name": "koreader_users_user_id_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "koreader_users_username_uidx": { + "name": "koreader_users_username_uidx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "koreader_users_user_id_users_id_fk": { + "name": "koreader_users_user_id_users_id_fk", + "tableFrom": "koreader_users", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "koreader_users_delivery_chk": { + "name": "koreader_users_delivery_chk", + "value": "\"koreader_users\".\"read_aloud_delivery\" in ('original', 'audioless_epub')" + } + }, + "isRLSEnabled": false + }, + "public.achievements": { + "name": "achievements", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "group_key": { + "name": "group_key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "tier": { + "name": "tier", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "icon_name": { + "name": "icon_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "rarity": { + "name": "rarity", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "threshold": { + "name": "threshold", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "achievements_category_sort_idx": { + "name": "achievements_category_sort_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "achievements_group_key_idx": { + "name": "achievements_group_key_idx", + "columns": [ + { + "expression": "group_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "achievements_category_chk": { + "name": "achievements_category_chk", + "value": "\"achievements\".\"category\" in ('reading', 'library', 'exploration', 'dedication', 'devices')" + }, + "achievements_rarity_chk": { + "name": "achievements_rarity_chk", + "value": "\"achievements\".\"rarity\" in ('common', 'rare', 'epic', 'legendary')" + }, + "achievements_tier_chk": { + "name": "achievements_tier_chk", + "value": "\"achievements\".\"tier\" is null or (\"achievements\".\"tier\" >= 1 and \"achievements\".\"tier\" <= 4)" + } + }, + "isRLSEnabled": false + }, + "public.user_achievements": { + "name": "user_achievements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "achievement_key": { + "name": "achievement_key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "awarded_at": { + "name": "awarded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "context_json": { + "name": "context_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "celebration_claim_id": { + "name": "celebration_claim_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "celebration_claimed_at": { + "name": "celebration_claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "celebrated_at": { + "name": "celebrated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_achievements_user_key_uidx": { + "name": "user_achievements_user_key_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "achievement_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_achievements_user_id_idx": { + "name": "user_achievements_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_achievements_user_awarded_at_idx": { + "name": "user_achievements_user_awarded_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "awarded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_achievements_pending_celebration_idx": { + "name": "user_achievements_pending_celebration_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "celebrated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "awarded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_achievements_celebration_claim_uidx": { + "name": "user_achievements_celebration_claim_uidx", + "columns": [ + { + "expression": "celebration_claim_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_achievements_user_id_users_id_fk": { + "name": "user_achievements_user_id_users_id_fk", + "tableFrom": "user_achievements", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_achievements_achievement_key_achievements_key_fk": { + "name": "user_achievements_achievement_key_achievements_key_fk", + "tableFrom": "user_achievements", + "tableTo": "achievements", + "columnsFrom": ["achievement_key"], + "columnsTo": ["key"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hardcover_book_state": { + "name": "hardcover_book_state", + "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 + }, + "hardcover_book_id": { + "name": "hardcover_book_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hardcover_edition_id": { + "name": "hardcover_edition_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hardcover_user_book_id": { + "name": "hardcover_user_book_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hardcover_read_id": { + "name": "hardcover_read_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "match_method": { + "name": "match_method", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "match_basis": { + "name": "match_basis", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "match_error": { + "name": "match_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_status": { + "name": "last_synced_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "last_synced_progress": { + "name": "last_synced_progress", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "last_synced_rating": { + "name": "last_synced_rating", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_synced_started_at": { + "name": "last_synced_started_at", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "last_synced_finished_at": { + "name": "last_synced_finished_at", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "sync_error": { + "name": "sync_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_override": { + "name": "sync_override", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "sync_excluded": { + "name": "sync_excluded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hardcover_book_state_book_id_idx": { + "name": "hardcover_book_state_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hardcover_book_state_user_id_users_id_fk": { + "name": "hardcover_book_state_user_id_users_id_fk", + "tableFrom": "hardcover_book_state", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "hardcover_book_state_book_id_books_id_fk": { + "name": "hardcover_book_state_book_id_books_id_fk", + "tableFrom": "hardcover_book_state", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "hardcover_book_state_user_book_uidx": { + "name": "hardcover_book_state_user_book_uidx", + "nullsNotDistinct": false, + "columns": ["user_id", "book_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hardcover_user_settings": { + "name": "hardcover_user_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "api_token": { + "name": "api_token", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "book_sync_mode": { + "name": "book_sync_mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'all_eligible'" + }, + "auto_sync_on_status_change": { + "name": "auto_sync_on_status_change", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_sync_on_progress_update": { + "name": "auto_sync_on_progress_update", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_sync_on_rating_change": { + "name": "auto_sync_on_rating_change", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "privacy_setting_id": { + "name": "privacy_setting_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "hardcover_user_settings_user_id_users_id_fk": { + "name": "hardcover_user_settings_user_id_users_id_fk", + "tableFrom": "hardcover_user_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "hardcover_user_settings_user_id_unique": { + "name": "hardcover_user_settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.readwise_user_settings": { + "name": "readwise_user_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "api_token": { + "name": "api_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_synced_annotation_id": { + "name": "last_synced_annotation_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "disabled_reason": { + "name": "disabled_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "readwise_user_settings_user_id_users_id_fk": { + "name": "readwise_user_settings_user_id_users_id_fk", + "tableFrom": "readwise_user_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "readwise_user_settings_user_id_unique": { + "name": "readwise_user_settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.storygraph_book_state": { + "name": "storygraph_book_state", + "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 + }, + "storygraph_book_id": { + "name": "storygraph_book_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "match_method": { + "name": "match_method", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "match_error": { + "name": "match_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_status": { + "name": "last_synced_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "last_synced_progress": { + "name": "last_synced_progress", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "sync_error": { + "name": "sync_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_override": { + "name": "sync_override", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "storygraph_book_state_book_id_idx": { + "name": "storygraph_book_state_book_id_idx", + "columns": [ + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "storygraph_book_state_user_sync_override_idx": { + "name": "storygraph_book_state_user_sync_override_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sync_override", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "storygraph_book_state_user_sync_error_idx": { + "name": "storygraph_book_state_user_sync_error_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"storygraph_book_state\".\"sync_error\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "storygraph_book_state_user_id_users_id_fk": { + "name": "storygraph_book_state_user_id_users_id_fk", + "tableFrom": "storygraph_book_state", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "storygraph_book_state_book_id_books_id_fk": { + "name": "storygraph_book_state_book_id_books_id_fk", + "tableFrom": "storygraph_book_state", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "storygraph_book_state_user_book_uidx": { + "name": "storygraph_book_state_user_book_uidx", + "nullsNotDistinct": false, + "columns": ["user_id", "book_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.storygraph_user_settings": { + "name": "storygraph_user_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "session_cookie": { + "name": "session_cookie", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": true + }, + "remember_token": { + "name": "remember_token", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "book_sync_mode": { + "name": "book_sync_mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'all_eligible'" + }, + "auto_sync_on_status_change": { + "name": "auto_sync_on_status_change", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_sync_on_progress_update": { + "name": "auto_sync_on_progress_update", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "storygraph_user_settings_user_id_users_id_fk": { + "name": "storygraph_user_settings_user_id_users_id_fk", + "tableFrom": "storygraph_user_settings", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "storygraph_user_settings_user_id_unique": { + "name": "storygraph_user_settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shared_reading_insight_view_sessions": { + "name": "shared_reading_insight_view_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "viewer_user_id": { + "name": "viewer_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "subject_user_id": { + "name": "subject_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sharing_level": { + "name": "sharing_level", + "type": "reading_insights_sharing_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "shared_insight_view_subject_created_idx": { + "name": "shared_insight_view_subject_created_idx", + "columns": [ + { + "expression": "subject_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "shared_insight_view_expires_idx": { + "name": "shared_insight_view_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shared_reading_insight_view_sessions_viewer_user_id_users_id_fk": { + "name": "shared_reading_insight_view_sessions_viewer_user_id_users_id_fk", + "tableFrom": "shared_reading_insight_view_sessions", + "tableTo": "users", + "columnsFrom": ["viewer_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shared_reading_insight_view_sessions_subject_user_id_users_id_fk": { + "name": "shared_reading_insight_view_sessions_subject_user_id_users_id_fk", + "tableFrom": "shared_reading_insight_view_sessions", + "tableTo": "users", + "columnsFrom": ["subject_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_preferences": { + "name": "user_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "up_user_category_idx": { + "name": "up_user_category_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_preferences_user_id_users_id_fk": { + "name": "user_preferences_user_id_users_id_fk", + "tableFrom": "user_preferences", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tts_book_preferences": { + "name": "tts_book_preferences", + "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 + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "voice_id": { + "name": "voice_id", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "speed": { + "name": "speed", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tts_book_prefs_user_book_uidx": { + "name": "tts_book_prefs_user_book_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tts_book_preferences_user_id_users_id_fk": { + "name": "tts_book_preferences_user_id_users_id_fk", + "tableFrom": "tts_book_preferences", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tts_book_preferences_book_id_books_id_fk": { + "name": "tts_book_preferences_book_id_books_id_fk", + "tableFrom": "tts_book_preferences", + "tableTo": "books", + "columnsFrom": ["book_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tts_book_preferences_provider_id_tts_providers_id_fk": { + "name": "tts_book_preferences_provider_id_tts_providers_id_fk", + "tableFrom": "tts_book_preferences", + "tableTo": "tts_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tts_providers": { + "name": "tts_providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "base_url": { + "name": "base_url", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "api_key": { + "name": "api_key", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "default_model": { + "name": "default_model", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "static_voices": { + "name": "static_voices", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "supports_voice_discovery": { + "name": "supports_voice_discovery", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tts_reading_position": { + "name": "tts_reading_position", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cfi": { + "name": "cfi", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chapter_index": { + "name": "chapter_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tts_reading_pos_user_file_uidx": { + "name": "tts_reading_pos_user_file_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tts_reading_position_user_id_users_id_fk": { + "name": "tts_reading_position_user_id_users_id_fk", + "tableFrom": "tts_reading_position", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tts_reading_position_book_file_id_book_files_id_fk": { + "name": "tts_reading_position_book_file_id_book_files_id_fk", + "tableFrom": "tts_reading_position", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tts_user_preferences": { + "name": "tts_user_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "voice_id": { + "name": "voice_id", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "speed": { + "name": "speed", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tts_user_prefs_user_uidx": { + "name": "tts_user_prefs_user_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tts_user_preferences_user_id_users_id_fk": { + "name": "tts_user_preferences_user_id_users_id_fk", + "tableFrom": "tts_user_preferences", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tts_user_preferences_provider_id_tts_providers_id_fk": { + "name": "tts_user_preferences_provider_id_tts_providers_id_fk", + "tableFrom": "tts_user_preferences", + "tableTo": "tts_providers", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.upload_sessions": { + "name": "upload_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "target_kind": { + "name": "target_kind", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "target_library_id": { + "name": "target_library_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "target_folder_id": { + "name": "target_folder_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "target_book_id": { + "name": "target_book_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "received_bytes": { + "name": "received_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "staging_path": { + "name": "staging_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'receiving'" + }, + "expected_sha256": { + "name": "expected_sha256", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_book_id": { + "name": "result_book_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "result_book_dock_file_id": { + "name": "result_book_dock_file_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "upload_sessions_user_idempotency_uidx": { + "name": "upload_sessions_user_idempotency_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "upload_sessions_user_created_idx": { + "name": "upload_sessions_user_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"created_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "upload_sessions_status_expires_idx": { + "name": "upload_sessions_status_expires_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "upload_sessions_user_id_users_id_fk": { + "name": "upload_sessions_user_id_users_id_fk", + "tableFrom": "upload_sessions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "upload_sessions_target_library_id_libraries_id_fk": { + "name": "upload_sessions_target_library_id_libraries_id_fk", + "tableFrom": "upload_sessions", + "tableTo": "libraries", + "columnsFrom": ["target_library_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "upload_sessions_target_folder_id_library_folders_id_fk": { + "name": "upload_sessions_target_folder_id_library_folders_id_fk", + "tableFrom": "upload_sessions", + "tableTo": "library_folders", + "columnsFrom": ["target_folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "upload_sessions_target_book_id_books_id_fk": { + "name": "upload_sessions_target_book_id_books_id_fk", + "tableFrom": "upload_sessions", + "tableTo": "books", + "columnsFrom": ["target_book_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "upload_sessions_result_book_id_books_id_fk": { + "name": "upload_sessions_result_book_id_books_id_fk", + "tableFrom": "upload_sessions", + "tableTo": "books", + "columnsFrom": ["result_book_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "upload_sessions_staging_path_unique": { + "name": "upload_sessions_staging_path_unique", + "nullsNotDistinct": false, + "columns": ["staging_path"] + } + }, + "policies": {}, + "checkConstraints": { + "upload_sessions_target_kind_chk": { + "name": "upload_sessions_target_kind_chk", + "value": "\"upload_sessions\".\"target_kind\" in ('library', 'existing_book', 'book_dock')" + }, + "upload_sessions_status_chk": { + "name": "upload_sessions_status_chk", + "value": "\"upload_sessions\".\"status\" in ('receiving', 'processing', 'completed', 'failed', 'cancelled', 'expired')" + }, + "upload_sessions_size_positive_chk": { + "name": "upload_sessions_size_positive_chk", + "value": "\"upload_sessions\".\"size_bytes\" > 0" + }, + "upload_sessions_received_range_chk": { + "name": "upload_sessions_received_range_chk", + "value": "\"upload_sessions\".\"received_bytes\" >= 0 and \"upload_sessions\".\"received_bytes\" <= \"upload_sessions\".\"size_bytes\"" + } + }, + "isRLSEnabled": false + }, + "public.podcast_bookmarks": { + "name": "podcast_bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "episode_id": { + "name": "episode_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "position_seconds": { + "name": "position_seconds", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "podcast_bookmarks_user_episode_idx": { + "name": "podcast_bookmarks_user_episode_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "episode_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_bookmarks_user_episode_position_idx": { + "name": "podcast_bookmarks_user_episode_position_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "episode_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position_seconds", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "podcast_bookmarks_user_id_users_id_fk": { + "name": "podcast_bookmarks_user_id_users_id_fk", + "tableFrom": "podcast_bookmarks", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "podcast_bookmarks_episode_id_podcast_episodes_id_fk": { + "name": "podcast_bookmarks_episode_id_podcast_episodes_id_fk", + "tableFrom": "podcast_bookmarks", + "tableTo": "podcast_episodes", + "columnsFrom": ["episode_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "podcast_bookmarks_position_nonnegative_chk": { + "name": "podcast_bookmarks_position_nonnegative_chk", + "value": "\"podcast_bookmarks\".\"position_seconds\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.podcast_episode_media": { + "name": "podcast_episode_media", + "schema": "", + "columns": { + "episode_id": { + "name": "episode_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'remote'" + }, + "local_path": { + "name": "local_path", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "format": { + "name": "format", + "type": "varchar(30)", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "checksum": { + "name": "checksum", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": false + }, + "downloaded_at": { + "name": "downloaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_remote_validated_at": { + "name": "last_remote_validated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "podcast_episode_media_status_idx": { + "name": "podcast_episode_media_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_episode_media_downloaded_idx": { + "name": "podcast_episode_media_downloaded_idx", + "columns": [ + { + "expression": "downloaded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "podcast_episode_media_episode_id_podcast_episodes_id_fk": { + "name": "podcast_episode_media_episode_id_podcast_episodes_id_fk", + "tableFrom": "podcast_episode_media", + "tableTo": "podcast_episodes", + "columnsFrom": ["episode_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "podcast_episode_media_status_chk": { + "name": "podcast_episode_media_status_chk", + "value": "\"podcast_episode_media\".\"status\" in ('remote', 'queued', 'downloading', 'local', 'failed', 'unavailable')" + }, + "podcast_episode_media_size_nonnegative_chk": { + "name": "podcast_episode_media_size_nonnegative_chk", + "value": "\"podcast_episode_media\".\"size_bytes\" is null or \"podcast_episode_media\".\"size_bytes\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.podcast_episodes": { + "name": "podcast_episodes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "podcast_id": { + "name": "podcast_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "identity_hash": { + "name": "identity_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "origin": { + "name": "origin", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'feed'" + }, + "guid": { + "name": "guid", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "season": { + "name": "season", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "episode": { + "name": "episode", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "episode_type": { + "name": "episode_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "duration_seconds": { + "name": "duration_seconds", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "explicit": { + "name": "explicit", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enclosure_url_encrypted": { + "name": "enclosure_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enclosure_url_hash": { + "name": "enclosure_url_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "enclosure_type": { + "name": "enclosure_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "enclosure_size_bytes": { + "name": "enclosure_size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "chapters": { + "name": "chapters", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "transcripts": { + "name": "transcripts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "locked_fields": { + "name": "locked_fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "in_feed": { + "name": "in_feed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "unavailable_at": { + "name": "unavailable_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "podcast_episodes_podcast_identity_uidx": { + "name": "podcast_episodes_podcast_identity_uidx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "identity_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_episodes_podcast_id_idx": { + "name": "podcast_episodes_podcast_id_idx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_episodes_podcast_created_idx": { + "name": "podcast_episodes_podcast_created_idx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_episodes_podcast_published_id_idx": { + "name": "podcast_episodes_podcast_published_id_idx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"published_at\" desc nulls last", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "\"id\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_episodes_title_trgm_idx": { + "name": "podcast_episodes_title_trgm_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "podcast_episodes_duration_idx": { + "name": "podcast_episodes_duration_idx", + "columns": [ + { + "expression": "duration_seconds", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_episodes_enclosure_hash_idx": { + "name": "podcast_episodes_enclosure_hash_idx", + "columns": [ + { + "expression": "enclosure_url_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_episodes_in_feed_idx": { + "name": "podcast_episodes_in_feed_idx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "in_feed", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "podcast_episodes_podcast_id_podcasts_id_fk": { + "name": "podcast_episodes_podcast_id_podcasts_id_fk", + "tableFrom": "podcast_episodes", + "tableTo": "podcasts", + "columnsFrom": ["podcast_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "podcast_episodes_origin_chk": { + "name": "podcast_episodes_origin_chk", + "value": "\"podcast_episodes\".\"origin\" in ('feed', 'local')" + }, + "podcast_episodes_feed_origin_requires_enclosure_chk": { + "name": "podcast_episodes_feed_origin_requires_enclosure_chk", + "value": "\"podcast_episodes\".\"origin\" = 'local' or \"podcast_episodes\".\"enclosure_url_hash\" is not null" + }, + "podcast_episodes_duration_nonnegative_chk": { + "name": "podcast_episodes_duration_nonnegative_chk", + "value": "\"podcast_episodes\".\"duration_seconds\" is null or \"podcast_episodes\".\"duration_seconds\" >= 0" + }, + "podcast_episodes_size_nonnegative_chk": { + "name": "podcast_episodes_size_nonnegative_chk", + "value": "\"podcast_episodes\".\"enclosure_size_bytes\" is null or \"podcast_episodes\".\"enclosure_size_bytes\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.podcast_feed_aliases": { + "name": "podcast_feed_aliases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "podcast_id": { + "name": "podcast_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "url_hash": { + "name": "url_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "url_encrypted": { + "name": "url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "podcast_feed_aliases_podcast_hash_uidx": { + "name": "podcast_feed_aliases_podcast_hash_uidx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "url_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_feed_aliases_hash_idx": { + "name": "podcast_feed_aliases_hash_idx", + "columns": [ + { + "expression": "url_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "podcast_feed_aliases_podcast_id_podcasts_id_fk": { + "name": "podcast_feed_aliases_podcast_id_podcasts_id_fk", + "tableFrom": "podcast_feed_aliases", + "tableTo": "podcasts", + "columnsFrom": ["podcast_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.podcast_jobs": { + "name": "podcast_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "library_id": { + "name": "library_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "podcast_id": { + "name": "podcast_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "episode_id": { + "name": "episode_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "requested_by_user_id": { + "name": "requested_by_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "download_batch_id": { + "name": "download_batch_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "progress_current": { + "name": "progress_current", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "progress_total": { + "name": "progress_total", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lease_until": { + "name": "lease_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": false + }, + "cancel_requested": { + "name": "cancel_requested", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "podcast_jobs_active_dedupe_uidx": { + "name": "podcast_jobs_active_dedupe_uidx", + "columns": [ + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"podcast_jobs\".\"status\" in ('queued', 'processing')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_jobs_due_idx": { + "name": "podcast_jobs_due_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_jobs_library_status_idx": { + "name": "podcast_jobs_library_status_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_jobs_status_updated_idx": { + "name": "podcast_jobs_status_updated_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_jobs_download_batch_idx": { + "name": "podcast_jobs_download_batch_idx", + "columns": [ + { + "expression": "requested_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "download_batch_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"podcast_jobs\".\"type\" = 'download' and \"podcast_jobs\".\"download_batch_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_jobs_active_podcast_type_idx": { + "name": "podcast_jobs_active_podcast_type_idx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requested_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"podcast_jobs\".\"status\" in ('queued', 'processing') and \"podcast_jobs\".\"podcast_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_jobs_active_episode_type_idx": { + "name": "podcast_jobs_active_episode_type_idx", + "columns": [ + { + "expression": "episode_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"podcast_jobs\".\"status\" in ('queued', 'processing') and \"podcast_jobs\".\"episode_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "podcast_jobs_library_id_libraries_id_fk": { + "name": "podcast_jobs_library_id_libraries_id_fk", + "tableFrom": "podcast_jobs", + "tableTo": "libraries", + "columnsFrom": ["library_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "podcast_jobs_podcast_id_podcasts_id_fk": { + "name": "podcast_jobs_podcast_id_podcasts_id_fk", + "tableFrom": "podcast_jobs", + "tableTo": "podcasts", + "columnsFrom": ["podcast_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "podcast_jobs_episode_id_podcast_episodes_id_fk": { + "name": "podcast_jobs_episode_id_podcast_episodes_id_fk", + "tableFrom": "podcast_jobs", + "tableTo": "podcast_episodes", + "columnsFrom": ["episode_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "podcast_jobs_requested_by_user_id_users_id_fk": { + "name": "podcast_jobs_requested_by_user_id_users_id_fk", + "tableFrom": "podcast_jobs", + "tableTo": "users", + "columnsFrom": ["requested_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "podcast_jobs_type_chk": { + "name": "podcast_jobs_type_chk", + "value": "\"podcast_jobs\".\"type\" in ('refresh', 'reparse', 'download', 'retention', 'purge', 'opml_import', 'import_scan', 'local_import', 'merge', 'digest', 'file_cleanup')" + }, + "podcast_jobs_status_chk": { + "name": "podcast_jobs_status_chk", + "value": "\"podcast_jobs\".\"status\" in ('queued', 'processing', 'completed', 'failed', 'cancelled')" + }, + "podcast_jobs_attempt_nonnegative_chk": { + "name": "podcast_jobs_attempt_nonnegative_chk", + "value": "\"podcast_jobs\".\"attempt_count\" >= 0" + }, + "podcast_jobs_progress_nonnegative_chk": { + "name": "podcast_jobs_progress_nonnegative_chk", + "value": "\"podcast_jobs\".\"progress_current\" >= 0 and (\"podcast_jobs\".\"progress_total\" is null or \"podcast_jobs\".\"progress_total\" >= 0)" + } + }, + "isRLSEnabled": false + }, + "public.podcast_library_settings": { + "name": "podcast_library_settings", + "schema": "", + "columns": { + "library_id": { + "name": "library_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "storage_quota_bytes": { + "name": "storage_quota_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "107374182400" + }, + "minimum_free_space_bytes": { + "name": "minimum_free_space_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "5368709120" + }, + "default_refresh_interval_minutes": { + "name": "default_refresh_interval_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 60 + }, + "completion_remaining_seconds": { + "name": "completion_remaining_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 60 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "podcast_library_settings_library_id_libraries_id_fk": { + "name": "podcast_library_settings_library_id_libraries_id_fk", + "tableFrom": "podcast_library_settings", + "tableTo": "libraries", + "columnsFrom": ["library_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "podcast_library_storage_quota_nonnegative_chk": { + "name": "podcast_library_storage_quota_nonnegative_chk", + "value": "\"podcast_library_settings\".\"storage_quota_bytes\" >= 0" + }, + "podcast_library_free_space_nonnegative_chk": { + "name": "podcast_library_free_space_nonnegative_chk", + "value": "\"podcast_library_settings\".\"minimum_free_space_bytes\" >= 0" + }, + "podcast_library_refresh_positive_chk": { + "name": "podcast_library_refresh_positive_chk", + "value": "\"podcast_library_settings\".\"default_refresh_interval_minutes\" > 0" + }, + "podcast_library_completion_nonnegative_chk": { + "name": "podcast_library_completion_nonnegative_chk", + "value": "\"podcast_library_settings\".\"completion_remaining_seconds\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.podcast_listening_sessions": { + "name": "podcast_listening_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "episode_id": { + "name": "episode_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "duration_seconds": { + "name": "duration_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_position_seconds": { + "name": "end_position_seconds", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "podcast_sessions_user_session_uidx": { + "name": "podcast_sessions_user_session_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_sessions_user_started_idx": { + "name": "podcast_sessions_user_started_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcast_sessions_episode_started_idx": { + "name": "podcast_sessions_episode_started_idx", + "columns": [ + { + "expression": "episode_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "podcast_listening_sessions_user_id_users_id_fk": { + "name": "podcast_listening_sessions_user_id_users_id_fk", + "tableFrom": "podcast_listening_sessions", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "podcast_listening_sessions_episode_id_podcast_episodes_id_fk": { + "name": "podcast_listening_sessions_episode_id_podcast_episodes_id_fk", + "tableFrom": "podcast_listening_sessions", + "tableTo": "podcast_episodes", + "columnsFrom": ["episode_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "podcast_sessions_duration_nonnegative_chk": { + "name": "podcast_sessions_duration_nonnegative_chk", + "value": "\"podcast_listening_sessions\".\"duration_seconds\" >= 0" + }, + "podcast_sessions_position_nonnegative_chk": { + "name": "podcast_sessions_position_nonnegative_chk", + "value": "\"podcast_listening_sessions\".\"end_position_seconds\" >= 0" + }, + "podcast_sessions_time_order_chk": { + "name": "podcast_sessions_time_order_chk", + "value": "\"podcast_listening_sessions\".\"ended_at\" >= \"podcast_listening_sessions\".\"started_at\"" + } + }, + "isRLSEnabled": false + }, + "public.podcasts": { + "name": "podcasts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "library_id": { + "name": "library_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": true + }, + "author": { + "name": "author", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "default": "'feed'" + }, + "feed_url_encrypted": { + "name": "feed_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feed_url_hash": { + "name": "feed_url_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "local_folder_path": { + "name": "local_folder_path", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": false + }, + "image_url_encrypted": { + "name": "image_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "site_url": { + "name": "site_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "language": { + "name": "language", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "podcast_type": { + "name": "podcast_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "explicit": { + "name": "explicit", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "categories": { + "name": "categories", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "acquisition_policy": { + "name": "acquisition_policy", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'remote_only'" + }, + "auto_download_limit": { + "name": "auto_download_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "auto_download_window_days": { + "name": "auto_download_window_days", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "download_cleanup": { + "name": "download_cleanup", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'keep'" + }, + "download_cleanup_delay_hours": { + "name": "download_cleanup_delay_hours", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 24 + }, + "refresh_interval_minutes": { + "name": "refresh_interval_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 60 + }, + "next_refresh_at": { + "name": "next_refresh_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_refresh_at": { + "name": "last_refresh_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_refresh_success_at": { + "name": "last_refresh_success_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "consecutive_failures": { + "name": "consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": false + }, + "last_http_status": { + "name": "last_http_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "etag": { + "name": "etag", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "last_modified": { + "name": "last_modified", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "locked_fields": { + "name": "locked_fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "custom_artwork_at": { + "name": "custom_artwork_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "feed_snapshot_at": { + "name": "feed_snapshot_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "missing_at": { + "name": "missing_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "podcasts_library_feed_hash_uidx": { + "name": "podcasts_library_feed_hash_uidx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "feed_url_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"podcasts\".\"feed_url_hash\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcasts_library_local_folder_uidx": { + "name": "podcasts_library_local_folder_uidx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "local_folder_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"podcasts\".\"local_folder_path\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcasts_library_archived_title_idx": { + "name": "podcasts_library_archived_title_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcasts_title_trgm_idx": { + "name": "podcasts_title_trgm_idx", + "columns": [ + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "podcasts_author_trgm_idx": { + "name": "podcasts_author_trgm_idx", + "columns": [ + { + "expression": "author", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "podcasts_due_refresh_idx": { + "name": "podcasts_due_refresh_idx", + "columns": [ + { + "expression": "next_refresh_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"podcasts\".\"archived_at\" is null and \"podcasts\".\"origin\" = 'feed'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "podcasts_library_cleanup_idx": { + "name": "podcasts_library_cleanup_idx", + "columns": [ + { + "expression": "library_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"podcasts\".\"download_cleanup\" <> 'keep'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "podcasts_library_id_libraries_id_fk": { + "name": "podcasts_library_id_libraries_id_fk", + "tableFrom": "podcasts", + "tableTo": "libraries", + "columnsFrom": ["library_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "podcasts_origin_chk": { + "name": "podcasts_origin_chk", + "value": "\"podcasts\".\"origin\" in ('feed', 'local')" + }, + "podcasts_feed_origin_requires_feed_chk": { + "name": "podcasts_feed_origin_requires_feed_chk", + "value": "\"podcasts\".\"origin\" = 'local' or \"podcasts\".\"feed_url_hash\" is not null" + }, + "podcasts_acquisition_policy_chk": { + "name": "podcasts_acquisition_policy_chk", + "value": "\"podcasts\".\"acquisition_policy\" in ('remote_only', 'manual', 'newest', 'window')" + }, + "podcasts_download_cleanup_chk": { + "name": "podcasts_download_cleanup_chk", + "value": "\"podcasts\".\"download_cleanup\" in ('keep', 'after_finished')" + }, + "podcasts_download_cleanup_delay_chk": { + "name": "podcasts_download_cleanup_delay_chk", + "value": "\"podcasts\".\"download_cleanup_delay_hours\" >= 0" + }, + "podcasts_refresh_interval_positive_chk": { + "name": "podcasts_refresh_interval_positive_chk", + "value": "\"podcasts\".\"refresh_interval_minutes\" > 0" + }, + "podcasts_failures_nonnegative_chk": { + "name": "podcasts_failures_nonnegative_chk", + "value": "\"podcasts\".\"consecutive_failures\" >= 0" + }, + "podcasts_auto_download_limit_chk": { + "name": "podcasts_auto_download_limit_chk", + "value": "\"podcasts\".\"auto_download_limit\" is null or \"podcasts\".\"auto_download_limit\" > 0" + }, + "podcasts_auto_download_window_chk": { + "name": "podcasts_auto_download_window_chk", + "value": "\"podcasts\".\"auto_download_window_days\" is null or \"podcasts\".\"auto_download_window_days\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.user_podcast_episode_state": { + "name": "user_podcast_episode_state", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "episode_id": { + "name": "episode_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "position_seconds": { + "name": "position_seconds", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "progress_percent": { + "name": "progress_percent", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "finished": { + "name": "finished", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "pinned": { + "name": "pinned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_listened_at": { + "name": "last_listened_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_podcast_episode_state_user_finished_idx": { + "name": "user_podcast_episode_state_user_finished_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "finished", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"updated_at\" desc", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_podcast_episode_state_episode_active_idx": { + "name": "user_podcast_episode_state_episode_active_idx", + "columns": [ + { + "expression": "episode_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "finished", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position_seconds", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_podcast_episode_state_user_id_users_id_fk": { + "name": "user_podcast_episode_state_user_id_users_id_fk", + "tableFrom": "user_podcast_episode_state", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_podcast_episode_state_episode_id_podcast_episodes_id_fk": { + "name": "user_podcast_episode_state_episode_id_podcast_episodes_id_fk", + "tableFrom": "user_podcast_episode_state", + "tableTo": "podcast_episodes", + "columnsFrom": ["episode_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_podcast_episode_state_user_id_episode_id_pk": { + "name": "user_podcast_episode_state_user_id_episode_id_pk", + "columns": ["user_id", "episode_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_podcast_episode_state_position_chk": { + "name": "user_podcast_episode_state_position_chk", + "value": "\"user_podcast_episode_state\".\"position_seconds\" >= 0" + }, + "user_podcast_episode_state_progress_chk": { + "name": "user_podcast_episode_state_progress_chk", + "value": "\"user_podcast_episode_state\".\"progress_percent\" >= 0 and \"user_podcast_episode_state\".\"progress_percent\" <= 100" + } + }, + "isRLSEnabled": false + }, + "public.user_podcast_follows": { + "name": "user_podcast_follows", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "podcast_id": { + "name": "podcast_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "notification_mode": { + "name": "notification_mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'off'" + }, + "last_notified_at": { + "name": "last_notified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_podcast_follows_podcast_idx": { + "name": "user_podcast_follows_podcast_idx", + "columns": [ + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_podcast_follows_due_digest_idx": { + "name": "user_podcast_follows_due_digest_idx", + "columns": [ + { + "expression": "notification_mode", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_notified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "podcast_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"user_podcast_follows\".\"notification_mode\" in ('daily', 'weekly')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_podcast_follows_user_id_users_id_fk": { + "name": "user_podcast_follows_user_id_users_id_fk", + "tableFrom": "user_podcast_follows", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_podcast_follows_podcast_id_podcasts_id_fk": { + "name": "user_podcast_follows_podcast_id_podcasts_id_fk", + "tableFrom": "user_podcast_follows", + "tableTo": "podcasts", + "columnsFrom": ["podcast_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_podcast_follows_user_id_podcast_id_pk": { + "name": "user_podcast_follows_user_id_podcast_id_pk", + "columns": ["user_id", "podcast_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_podcast_follows_notification_chk": { + "name": "user_podcast_follows_notification_chk", + "value": "\"user_podcast_follows\".\"notification_mode\" in ('off', 'immediate', 'daily', 'weekly')" + } + }, + "isRLSEnabled": false + }, + "public.user_podcast_queue": { + "name": "user_podcast_queue", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "episode_id": { + "name": "episode_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_podcast_queue_user_position_uidx": { + "name": "user_podcast_queue_user_position_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_podcast_queue_user_id_users_id_fk": { + "name": "user_podcast_queue_user_id_users_id_fk", + "tableFrom": "user_podcast_queue", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_podcast_queue_episode_id_podcast_episodes_id_fk": { + "name": "user_podcast_queue_episode_id_podcast_episodes_id_fk", + "tableFrom": "user_podcast_queue", + "tableTo": "podcast_episodes", + "columnsFrom": ["episode_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_podcast_queue_user_id_episode_id_pk": { + "name": "user_podcast_queue_user_id_episode_id_pk", + "columns": ["user_id", "episode_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_podcast_queue_position_nonnegative_chk": { + "name": "user_podcast_queue_position_nonnegative_chk", + "value": "\"user_podcast_queue\".\"position\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.provider_metadata_cache": { + "name": "provider_metadata_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "char(64)", + "primaryKey": false, + "notNull": true + }, + "namespace": { + "name": "namespace", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "schema_version": { + "name": "schema_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_version": { + "name": "provider_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "config_fingerprint": { + "name": "config_fingerprint", + "type": "char(16)", + "primaryKey": false, + "notNull": true + }, + "max_candidates": { + "name": "max_candidates", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "covers_validated": { + "name": "covers_validated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload_bytes": { + "name": "payload_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "fetched_at": { + "name": "fetched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_accessed_at": { + "name": "last_accessed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "upstream_ms": { + "name": "upstream_ms", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "provider_metadata_cache_ns_provider_fetched_idx": { + "name": "provider_metadata_cache_ns_provider_fetched_idx", + "columns": [ + { + "expression": "namespace", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "fetched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "provider_metadata_cache_last_accessed_idx": { + "name": "provider_metadata_cache_last_accessed_idx", + "columns": [ + { + "expression": "last_accessed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "provider_metadata_cache_key_hash_unique": { + "name": "provider_metadata_cache_key_hash_unique", + "nullsNotDistinct": false, + "columns": ["key_hash"] + } + }, + "policies": {}, + "checkConstraints": { + "provider_metadata_cache_namespace_chk": { + "name": "provider_metadata_cache_namespace_chk", + "value": "\"provider_metadata_cache\".\"namespace\" in ('book', 'author')" + }, + "provider_metadata_cache_operation_chk": { + "name": "provider_metadata_cache_operation_chk", + "value": "\"provider_metadata_cache\".\"operation\" in ('search', 'lookup')" + }, + "provider_metadata_cache_status_chk": { + "name": "provider_metadata_cache_status_chk", + "value": "\"provider_metadata_cache\".\"status\" in ('results', 'empty', 'found', 'not_found')" + }, + "provider_metadata_cache_payload_bytes_nonnegative_chk": { + "name": "provider_metadata_cache_payload_bytes_nonnegative_chk", + "value": "\"provider_metadata_cache\".\"payload_bytes\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.delivery_artifacts": { + "name": "delivery_artifacts", + "schema": "", + "columns": { + "selection_id": { + "name": "selection_id", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "delivery_artifacts_owner_file_idx": { + "name": "delivery_artifacts_owner_file_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "book_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "delivery_artifacts_user_id_users_id_fk": { + "name": "delivery_artifacts_user_id_users_id_fk", + "tableFrom": "delivery_artifacts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "delivery_artifacts_book_file_id_book_files_id_fk": { + "name": "delivery_artifacts_book_file_id_book_files_id_fk", + "tableFrom": "delivery_artifacts", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.delivery_inspections": { + "name": "delivery_inspections", + "schema": "", + "columns": { + "book_file_id": { + "name": "book_file_id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "source_revision": { + "name": "source_revision", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "detector_version": { + "name": "detector_version", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true + }, + "retry_at": { + "name": "retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "checked_at": { + "name": "checked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "delivery_inspections_book_file_id_book_files_id_fk": { + "name": "delivery_inspections_book_file_id_book_files_id_fk", + "tableFrom": "delivery_inspections", + "tableTo": "book_files", + "columnsFrom": ["book_file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server_identity": { + "name": "server_identity", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "default": 1 + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "server_identity_singleton": { + "name": "server_identity_singleton", + "value": "\"server_identity\".\"id\" = 1" + } + }, + "isRLSEnabled": false + } + }, + "enums": { + "public.content_filter_type": { + "name": "content_filter_type", + "schema": "public", + "values": ["include", "exclude"] + }, + "public.library_access_level": { + "name": "library_access_level", + "schema": "public", + "values": ["viewer", "editor", "owner"] + }, + "public.reading_insights_sharing_level": { + "name": "reading_insights_sharing_level", + "schema": "public", + "values": ["private", "summary", "detailed"] + }, + "public.user_avatar_source": { + "name": "user_avatar_source", + "schema": "public", + "values": ["none", "external", "uploaded"] + }, + "public.opds_sort_order": { + "name": "opds_sort_order", + "schema": "public", + "values": ["recent", "title_asc", "title_desc", "author_asc", "author_desc", "series_asc", "series_desc"] + }, + "public.book_request_media_kind": { + "name": "book_request_media_kind", + "schema": "public", + "values": ["ebook", "audiobook", "comic"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/server/src/db/migrations/meta/_journal.json b/server/src/db/migrations/meta/_journal.json index 738389234..c45e9d1da 100644 --- a/server/src/db/migrations/meta/_journal.json +++ b/server/src/db/migrations/meta/_journal.json @@ -827,6 +827,13 @@ "when": 1792191772317, "tag": "0117_add_reading_session_sync_interval", "breakpoints": true + }, + { + "idx": 118, + "version": "7", + "when": 1792201109071, + "tag": "0118_add_kobo_bookmark_links", + "breakpoints": true } ] } diff --git a/server/src/db/schema/kobo.ts b/server/src/db/schema/kobo.ts index ac4185442..693079f94 100644 --- a/server/src/db/schema/kobo.ts +++ b/server/src/db/schema/kobo.ts @@ -19,6 +19,7 @@ import { import { books, bookFiles } from './books'; import { users } from './auth'; +import { bookmarks } from './reader'; export const koboDevices = pgTable( 'kobo_devices', @@ -317,3 +318,46 @@ export const koboDeviceReadingStates = pgTable( }).onDelete('cascade'), ], ); + +/** + * One row per (bookmark, Kobo device): a bookmark the device holds as a dogear, under the + * annotation id it gave it, with the location it reported. The device reconciles its annotations + * against the set it is served, so this keeps the dogear served back as a dogear, and lets a + * bookmark deleted in BookOrbit leave the device by being left out. + */ +export const koboBookmarkLinks = pgTable( + 'kobo_bookmark_links', + { + id: serial('id').primaryKey(), + bookmarkId: integer('bookmark_id') + .notNull() + .references(() => bookmarks.id, { onDelete: 'cascade' }), + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + deviceId: integer('device_id').notNull(), + bookId: integer('book_id') + .notNull() + .references(() => books.id, { onDelete: 'cascade' }), + externalKey: varchar('external_key', { length: 64 }).notNull(), + location: jsonb('location').notNull(), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), + updatedAt: timestamp('updated_at', { withTimezone: true }) + .notNull() + .defaultNow() + .$onUpdateFn(() => new Date()), + }, + (table) => [ + foreignKey({ + columns: [table.deviceId, table.userId], + foreignColumns: [koboDevices.id, koboDevices.userId], + name: 'kobo_bookmark_links_owner_fk', + }).onDelete('cascade'), + uniqueIndex('kobo_bookmark_links_bookmark_device_uidx').on(table.bookmarkId, table.deviceId), + uniqueIndex('kobo_bookmark_links_device_key_uidx').on(table.deviceId, table.externalKey), + index('kobo_bookmark_links_user_device_book_idx').on(table.userId, table.deviceId, table.bookId), + ], +); + +export type KoboBookmarkLink = typeof koboBookmarkLinks.$inferSelect; +export type NewKoboBookmarkLink = typeof koboBookmarkLinks.$inferInsert; diff --git a/server/src/db/schema/reader.ts b/server/src/db/schema/reader.ts index 5a374af86..e55e7077c 100644 --- a/server/src/db/schema/reader.ts +++ b/server/src/db/schema/reader.ts @@ -426,7 +426,7 @@ export const audiobookProgress = pgTable( export type AudiobookProgress = typeof audiobookProgress.$inferSelect; export type NewAudiobookProgress = typeof audiobookProgress.$inferInsert; -export type BookmarkOrigin = 'web' | 'koreader'; +export type BookmarkOrigin = 'web' | 'koreader' | 'kobo'; export const userBookReadAloudSyncSettings = pgTable( 'user_book_read_aloud_sync_settings', @@ -498,7 +498,7 @@ export const bookmarks = pgTable( uniqueIndex('bookmarks_user_book_pos_uidx') .on(t.userId, t.bookId, t.positionSeconds) .where(sql`${t.positionSeconds} is not null and ${t.cfi} is null and ${t.deletedAt} is null`), - check('bookmarks_origin_chk', sql`${t.origin} in ('web', 'koreader')`), + check('bookmarks_origin_chk', sql`${t.origin} in ('web', 'koreader', 'kobo')`), ], ); diff --git a/server/src/modules/bookmark/bookmark-sync.service.ts b/server/src/modules/bookmark/bookmark-sync.service.ts index fe4f2e6a4..6545d3a67 100644 --- a/server/src/modules/bookmark/bookmark-sync.service.ts +++ b/server/src/modules/bookmark/bookmark-sync.service.ts @@ -35,7 +35,7 @@ export class BookmarkSyncService { const restored = await this.bookmarkRepo.restoreAtLocation(userId, bookId, location, { title: fields.title, - origin: 'koreader', + origin: fields.origin ?? 'koreader', devicePos: fields.devicePos, pageno: fields.pageno, }); diff --git a/server/src/modules/bookmark/bookmark.repository.ts b/server/src/modules/bookmark/bookmark.repository.ts index 29322c647..739222039 100644 --- a/server/src/modules/bookmark/bookmark.repository.ts +++ b/server/src/modules/bookmark/bookmark.repository.ts @@ -4,7 +4,7 @@ import { NodePgDatabase } from 'drizzle-orm/node-postgres'; import { DB } from '../../db'; import * as schema from '../../db/schema'; -import { bookmarks, type BookmarkRow, type NewBookmark } from '../../db/schema'; +import { bookmarks, type BookmarkOrigin, type BookmarkRow, type NewBookmark } from '../../db/schema'; type Db = NodePgDatabase; @@ -14,6 +14,8 @@ export interface DeviceBookmarkFields { title: string; devicePos: string; pageno: number | null; + /** The device family the bookmark came from; KOReader when omitted. */ + origin?: BookmarkOrigin; } @Injectable() @@ -175,7 +177,7 @@ export class BookmarkRepository { bookId, cfi: fields.cfi, title: fields.title, - origin: 'koreader', + origin: fields.origin ?? 'koreader', devicePos: fields.devicePos, pageno: fields.pageno, }) diff --git a/server/src/modules/kobo/kobo-bookmark-link.repository.ts b/server/src/modules/kobo/kobo-bookmark-link.repository.ts new file mode 100644 index 000000000..610e5ab60 --- /dev/null +++ b/server/src/modules/kobo/kobo-bookmark-link.repository.ts @@ -0,0 +1,116 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { and, eq, inArray, isNotNull } from 'drizzle-orm'; +import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; + +import { isUniqueViolation } from '../../common/utils/db-error.utils'; +import { DB } from '../../db'; +import * as schema from '../../db/schema'; + +/** A device's dogear link with the state of the bookmark behind it. */ +export interface KoboBookmarkLinkState { + bookmarkId: number; + externalKey: string; + location: unknown; + bookmarkUpdatedAt: Date; + bookmarkDeletedAt: Date | null; +} + +const REMOVED_BOOK_LIMIT = 500; + +@Injectable() +export class KoboBookmarkLinkRepository { + constructor(@Inject(DB) private readonly db: NodePgDatabase) {} + + findForBook(userId: number, deviceId: number, bookId: number): Promise { + return this.db + .select({ + bookmarkId: schema.koboBookmarkLinks.bookmarkId, + externalKey: schema.koboBookmarkLinks.externalKey, + location: schema.koboBookmarkLinks.location, + bookmarkUpdatedAt: schema.bookmarks.updatedAt, + bookmarkDeletedAt: schema.bookmarks.deletedAt, + }) + .from(schema.koboBookmarkLinks) + .innerJoin(schema.bookmarks, eq(schema.bookmarks.id, schema.koboBookmarkLinks.bookmarkId)) + .where( + and( + eq(schema.koboBookmarkLinks.userId, userId), + eq(schema.koboBookmarkLinks.deviceId, deviceId), + eq(schema.koboBookmarkLinks.bookId, bookId), + ), + ); + } + + findByExternalKeys(userId: number, deviceId: number, externalKeys: string[]) { + if (externalKeys.length === 0) return Promise.resolve([]); + return this.db + .select() + .from(schema.koboBookmarkLinks) + .where( + and( + eq(schema.koboBookmarkLinks.userId, userId), + eq(schema.koboBookmarkLinks.deviceId, deviceId), + inArray(schema.koboBookmarkLinks.externalKey, externalKeys), + ), + ); + } + + /** + * Links a dogear to its bookmark. Two dogears the device placed at one spot land on the same + * bookmark, which keeps the first link; the device drops the other once it is no longer served. + */ + async link(values: typeof schema.koboBookmarkLinks.$inferInsert): Promise { + try { + await this.db + .insert(schema.koboBookmarkLinks) + .values(values) + .onConflictDoUpdate({ + target: [schema.koboBookmarkLinks.deviceId, schema.koboBookmarkLinks.externalKey], + set: { bookmarkId: values.bookmarkId, bookId: values.bookId, location: values.location, updatedAt: new Date() }, + }); + return true; + } catch (error) { + if (isUniqueViolation(error)) return false; + throw error; + } + } + + async updateLocation(userId: number, deviceId: number, externalKey: string, location: unknown): Promise { + await this.db + .update(schema.koboBookmarkLinks) + .set({ location, updatedAt: new Date() }) + .where( + and( + eq(schema.koboBookmarkLinks.userId, userId), + eq(schema.koboBookmarkLinks.deviceId, deviceId), + eq(schema.koboBookmarkLinks.externalKey, externalKey), + ), + ); + } + + async deleteForBookmarks(userId: number, deviceId: number, bookmarkIds: number[]): Promise { + if (bookmarkIds.length === 0) return 0; + const deleted = await this.db + .delete(schema.koboBookmarkLinks) + .where( + and( + eq(schema.koboBookmarkLinks.userId, userId), + eq(schema.koboBookmarkLinks.deviceId, deviceId), + inArray(schema.koboBookmarkLinks.bookmarkId, bookmarkIds), + ), + ) + .returning({ id: schema.koboBookmarkLinks.id }); + return deleted.length; + } + + /** Books holding a dogear on this device whose bookmark was deleted in BookOrbit. */ + async findBookIdsWithRemovedBookmarks(userId: number, deviceId: number): Promise { + const rows = await this.db + .selectDistinct({ bookId: schema.koboBookmarkLinks.bookId }) + .from(schema.koboBookmarkLinks) + .innerJoin(schema.bookmarks, eq(schema.bookmarks.id, schema.koboBookmarkLinks.bookmarkId)) + .where(and(eq(schema.koboBookmarkLinks.userId, userId), eq(schema.koboBookmarkLinks.deviceId, deviceId), isNotNull(schema.bookmarks.deletedAt))) + .limit(REMOVED_BOOK_LIMIT); + return rows.map((row) => row.bookId); + } +} diff --git a/server/src/modules/kobo/kobo.module.test.ts b/server/src/modules/kobo/kobo.module.test.ts index 832a3311d..3e1d34339 100644 --- a/server/src/modules/kobo/kobo.module.test.ts +++ b/server/src/modules/kobo/kobo.module.test.ts @@ -18,6 +18,8 @@ import { KoboBookIdentityService } from './services/kobo-book-identity.service'; import { KoboDeviceService } from './services/kobo-device.service'; import { KoboDeliveryService } from './services/kobo-delivery.service'; import { KoboDeliveryRepository } from './kobo-delivery.repository'; +import { KoboBookmarkLinkRepository } from './kobo-bookmark-link.repository'; +import { BookmarkModule } from '../bookmark/bookmark.module'; import { KoboDownloadRepository } from './kobo-download.repository'; import { KoboDownloadHashBackfillService } from './services/kobo-download-hash-backfill.service'; import { AppSettingsModule } from '../app-settings/app-settings.module'; @@ -47,6 +49,7 @@ describe('KoboModule', () => { const imports = Reflect.getMetadata(MODULE_METADATA.IMPORTS, KoboModule) as unknown[]; expect(imports).toContain(BookModule); + expect(imports).toContain(BookmarkModule); expect(imports).toContain(AppSettingsModule); expect(imports).toContain(ReadingSessionModule); expect(imports).toContain(AnnotationModule); @@ -72,6 +75,7 @@ describe('KoboModule', () => { KoboDownloadService, KoboDownloadRepository, KoboDeliveryRepository, + KoboBookmarkLinkRepository, KoboReadingProgressRepository, KoboDeliveryService, KoboDownloadHashBackfillService, diff --git a/server/src/modules/kobo/kobo.module.ts b/server/src/modules/kobo/kobo.module.ts index 6bad5932a..b0eb3ba12 100644 --- a/server/src/modules/kobo/kobo.module.ts +++ b/server/src/modules/kobo/kobo.module.ts @@ -7,6 +7,7 @@ import { BookModule } from '../book/book.module'; import { BookCoverStoreModule } from '../book-cover-store/book-cover-store.module'; import { MetadataModule } from '../metadata/metadata.module'; import { PositionConverterModule } from '../position-converter/position-converter.module'; +import { BookmarkModule } from '../bookmark/bookmark.module'; import { ReadingSessionModule } from '../reading-session/reading-session.module'; import { SmartScopeModule } from '../smart-scope/smart-scope.module'; import { UserModule } from '../user/user.module'; @@ -26,6 +27,7 @@ import { KoboDeviceService } from './services/kobo-device.service'; import { KoboDownloadService } from './services/kobo-download.service'; import { KoboReadingProgressRepository } from './kobo-reading-progress.repository'; import { KoboDeliveryRepository } from './kobo-delivery.repository'; +import { KoboBookmarkLinkRepository } from './kobo-bookmark-link.repository'; import { KoboDeliveryService } from './services/kobo-delivery.service'; import { KoboDownloadRepository } from './kobo-download.repository'; import { KoboDownloadHashRegistrationService } from './services/kobo-download-hash-registration.service'; @@ -51,6 +53,7 @@ import { KoboAnalyticsService } from './services/kobo-analytics.service'; AchievementModule, AnnotationModule, BookModule, + BookmarkModule, BookCoverStoreModule, MetadataModule, PositionConverterModule, @@ -80,6 +83,7 @@ import { KoboAnalyticsService } from './services/kobo-analytics.service'; KoboDownloadService, KoboDownloadRepository, KoboDeliveryRepository, + KoboBookmarkLinkRepository, KoboReadingProgressRepository, KoboDeliveryService, KoboDownloadHashBackfillService, diff --git a/server/src/modules/kobo/services/kobo-annotation-exchange.service.test.ts b/server/src/modules/kobo/services/kobo-annotation-exchange.service.test.ts new file mode 100644 index 000000000..fc3b1013c --- /dev/null +++ b/server/src/modules/kobo/services/kobo-annotation-exchange.service.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { KoboAnnotationExchangeService } from './kobo-annotation-exchange.service'; + +function makeService(onDevice: number[]) { + const annotationSync = { listPendingKoboChangeBookIds: vi.fn().mockResolvedValue([1, 2]) }; + const bookIdentityService = { + findByBookIds: vi.fn((_userId: number, bookIds: number[]) => + Promise.resolve(new Map(bookIds.map((id) => [id, { entitlementId: `content-${id}` }]))), + ), + }; + const bookmarkLinks = { findBookIdsWithRemovedBookmarks: vi.fn().mockResolvedValue([2, 3]) }; + const syncService = { + findBookIdsOnDevice: vi.fn((_userId: number, _deviceId: number, bookIds: number[]) => + Promise.resolve(bookIds.filter((id) => onDevice.includes(id))), + ), + }; + const service = new KoboAnnotationExchangeService( + annotationSync as never, + bookIdentityService as never, + {} as never, + {} as never, + { version: 1 } as never, + { getSettings: vi.fn().mockResolvedValue({ syncBookOrbitAnnotationsToKobo: false }) } as never, + {} as never, + bookmarkLinks as never, + syncService as never, + ); + return { service, syncService, bookIdentityService }; +} + +describe('KoboAnnotationExchangeService.getChangedContentIds', () => { + it('reports only books the device holds, once each, from annotation and bookmark changes', async () => { + const { service, syncService } = makeService([2, 3]); + + await expect(service.getChangedContentIds(7, 11)).resolves.toEqual(['content-2', 'content-3']); + + expect(syncService.findBookIdsOnDevice).toHaveBeenCalledWith(7, 11, [1, 2, 3]); + }); + + it('reports nothing for changes on books the device never received', async () => { + const { service, bookIdentityService } = makeService([]); + + await expect(service.getChangedContentIds(7, 11)).resolves.toEqual([]); + + expect(bookIdentityService.findByBookIds).not.toHaveBeenCalled(); + }); +}); diff --git a/server/src/modules/kobo/services/kobo-annotation-exchange.service.ts b/server/src/modules/kobo/services/kobo-annotation-exchange.service.ts index 175ae1537..1d1f93ce0 100644 --- a/server/src/modules/kobo/services/kobo-annotation-exchange.service.ts +++ b/server/src/modules/kobo/services/kobo-annotation-exchange.service.ts @@ -6,16 +6,21 @@ import type { AnnotationPosition, AnnotationRow, AnnotationSyncStateRow } from ' import { sanitizeLogValue } from '../../../common/utils/log-sanitize.utils'; import { AnnotationSyncService, formatDeviceDatetime, type IncomingDeviceAnnotation } from '../../annotation/annotation-sync.service'; import { AnnotationConversionService } from '../../annotation/annotation-conversion.service'; +import { BookmarkSyncService } from '../../bookmark/bookmark-sync.service'; import { KoboSpanConverterService, serializeKoboSpanPos } from '../../position-converter/kobo-span-converter.service'; import { parseSpanSelector } from '../../position-converter/kobo-span.core'; import { KoboDeliveryService, isUnconfirmedReplacement } from './kobo-delivery.service'; import { KoboBookIdentityService } from './kobo-book-identity.service'; +import { KoboSyncService } from './kobo-sync.service'; import { KoboAnnotationMaterializerService } from './kobo-annotation-materializer.service'; import { KoboKepubContextService } from './kobo-kepub-context.service'; import { KoboSettingsService } from './kobo-settings.service'; +import type { KepubContextResult } from './kobo-kepub-context.service'; +import { KoboBookmarkLinkRepository } from '../kobo-bookmark-link.repository'; import { extractKoboAnnotationOperations, toKoboReadingServiceAnnotation, + toKoboReadingServiceDogear, type IncomingUpsert, type KoboReadingServiceAnnotationsResponse, type ServeAnnotation, @@ -24,6 +29,9 @@ import { const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; const GET_EVENT = 'kobo.annotation_get'; const PATCH_EVENT = 'kobo.annotation_patch'; +const BOOKMARK_TITLE_MAX_LENGTH = 500; + +type ReadyKepub = Extract; /** * Raised when a content id the device asks about is not a BookOrbit book. Kobo devices route every @@ -40,6 +48,8 @@ export class KoboContentNotFoundException extends NotFoundException { export interface ServedAckPlan { entries: { annotationId: number; version: number; externalKey: string; externalCreatedAt: string | null }[]; tombstoneStateIds: number[]; + /** Bookmarks deleted in BookOrbit and left out of this response, which the device deletes. */ + removedBookmarkIds?: number[]; } export interface KoboContentAnnotationsResult { @@ -72,6 +82,9 @@ export class KoboAnnotationExchangeService { private readonly materializer: KoboAnnotationMaterializerService, private readonly koboSpanConverter: KoboSpanConverterService, private readonly settingsService: KoboSettingsService, + private readonly bookmarkSync: BookmarkSyncService, + private readonly bookmarkLinks: KoboBookmarkLinkRepository, + private readonly syncService: KoboSyncService, @Optional() private readonly deliveries?: KoboDeliveryService, @Optional() private readonly annotationConversion?: AnnotationConversionService, ) {} @@ -91,6 +104,7 @@ export class KoboAnnotationExchangeService { const includeAllOrigins = kepub.settings?.syncBookOrbitAnnotationsToKobo ?? false; if (this.deliveries && !kepub.ok) throw new ConflictException({ code: 'KOBO_PRECISE_SYNC_UNAVAILABLE' }); if (this.deliveries && kepub.ok) await this.annotationConversion?.ensureCfiPositionsForBook(userId, bookId, 200); + if (kepub.ok) await this.convertLegacyDogears(userId, deviceId, bookId, kepub); if (kepub.ok && !this.deliveries) { await this.materializer.ensureKoboSpanPositionsForBook(userId, bookId, kepub.file, kepub.ctx, { includeAllOrigins }); } @@ -129,7 +143,8 @@ export class KoboAnnotationExchangeService { (candidate) => candidate.annotationId === annotation.id && candidate.format === 'cfi' && candidate.status !== 'failed', ); const raw = allPositions.find((candidate) => candidate.annotationId === annotation.id && candidate.format === 'kobo_span'); - location = raw && (raw.extras as { renditionHash?: string } | null)?.renditionHash === renditionHash ? this.usableLocation(raw) : null; + const placed = raw && (raw.extras as { renditionHash?: string } | null)?.renditionHash === renditionHash ? this.usableLocation(raw) : null; + location = placed; if (canonical?.pos0 && (!location || (raw?.extras as { sourceCfi?: string } | null)?.sourceCfi !== canonical.pos0)) { const converted = await this.koboSpanConverter.canonicalToKoboSpan({ bookFileId: kepub.file.id, @@ -140,6 +155,10 @@ export class KoboAnnotationExchangeService { }); location = converted.status === 'failed' ? null : (converted.location ?? null); } + // The device deletes what it is not served, and clears a book's annotations when its download is + // removed, so refusing the pull would leave them gone from the device. Where this copy cannot + // place an annotation, the location already placed in it, then the one the device reported, stand in. + if (!location && known) location = placed ?? (raw?.extras as { koboLocation?: unknown } | null)?.koboLocation ?? null; if (!location && known) throw new ConflictException({ code: 'KOBO_ANNOTATION_RENDITION_UNAVAILABLE' }); } if (!location) { @@ -171,9 +190,14 @@ export class KoboAnnotationExchangeService { // flags do not work - the device keeps any annotation still present in the // response and ignores deleted markers. const tombstones = await this.annotationSync.listDeleteCandidates(userId, 'kobo', String(deviceId), bookId, 500); - const servedAck: ServedAckPlan = { entries: ackEntries, tombstoneStateIds: tombstones.map((row) => row.state.id) }; + const dogears = await this.listServedDogears(userId, deviceId, bookId); + const servedAck: ServedAckPlan = { + entries: ackEntries, + tombstoneStateIds: tombstones.map((row) => row.state.id), + removedBookmarkIds: dogears.removedBookmarkIds, + }; - const etag = this.buildEtag(served.length, ackEntries, renditionHash); + const etag = this.buildEtag(served.length, ackEntries, renditionHash, dogears.served); const notModified = this.etagMatches(ifNoneMatch, etag); this.logger.log( @@ -191,7 +215,7 @@ export class KoboAnnotationExchangeService { servedCount: served.length, tombstoneCount: servedAck.tombstoneStateIds.length, response: { - annotations: served.map((row) => toKoboReadingServiceAnnotation(row)), + annotations: [...served.map((row) => toKoboReadingServiceAnnotation(row)), ...dogears.served.map((row) => toKoboReadingServiceDogear(row))], nextPageOffsetToken: null, }, }; @@ -209,6 +233,8 @@ export class KoboAnnotationExchangeService { entries: ack.entries, tombstoneStateIds: ack.tombstoneStateIds, }); + // A dogear left out of the response is deleted on the device, so its link has nothing left to track. + await this.bookmarkLinks.deleteForBookmarks(userId, deviceId, ack.removedBookmarkIds ?? []); } async patchContentAnnotations(userId: number, contentId: string, body: unknown, deviceId: number): Promise { @@ -227,8 +253,25 @@ export class KoboAnnotationExchangeService { const kepub = await this.kepubContextService.resolveForBook(userId, bookId, deviceId); try { + if (kepub.ok) await this.convertLegacyDogears(userId, deviceId, bookId, kepub); + const dogears = kepub.ok + ? await this.ingestDogears( + userId, + deviceId, + bookId, + kepub, + upserts.filter((operation) => operation.type === 'dogear'), + ) + : { created: 0, updated: 0, placed: new Set() }; + const annotationUpserts = upserts.filter((operation) => !dogears.placed.has(operation.id)); + const removedBookmarks = await this.removeDeviceBookmarks( + userId, + deviceId, + deletes.map((operation) => operation.id), + ); + const delivery = await this.deliveries?.current(userId, deviceId, bookId); - const incoming = upserts.map((operation) => { + const incoming = annotationUpserts.map((operation) => { const annotation = this.toIncomingAnnotation(operation); annotation.posExtras = { ...(annotation.posExtras ?? {}), @@ -254,23 +297,30 @@ export class KoboAnnotationExchangeService { source: 'kobo', deviceId: String(deviceId), bookId, - deletes: deletes.map((operation) => ({ externalKey: operation.id })), + deletes: deletes.filter((operation) => !removedBookmarks.keys.has(operation.id)).map((operation) => ({ externalKey: operation.id })), }); - if (kepub.ok && upserts.length > 0) { + if (kepub.ok && annotationUpserts.length > 0) { await this.materializer.convertIncomingForBook( userId, bookId, kepub.file, kepub.ctx, - upserts.map((operation) => operation.id), + annotationUpserts.map((operation) => operation.id), ); } this.logger.log( `[${PATCH_EVENT}] [end] userId=${userId} bookId=${bookId} deviceId=${deviceId} durationMs=${Date.now() - startedAtMs} created=${result.created} updated=${result.updated} unchanged=${result.unchanged} deleted=${deletedCount} kepubReady=${kepub.ok} - kobo annotation operations applied`, ); - return { bookId, created: result.created, updated: result.updated, unchanged: result.unchanged, deleted: deletedCount, kepubReady: kepub.ok }; + return { + bookId, + created: result.created + dogears.created, + updated: result.updated + dogears.updated, + unchanged: result.unchanged, + deleted: deletedCount + removedBookmarks.removed, + kepubReady: kepub.ok, + }; } finally { if (kepub.ok) await kepub.release?.(); } @@ -278,15 +328,166 @@ export class KoboAnnotationExchangeService { async getChangedContentIds(userId: number, deviceId: number): Promise { const settings = await this.settingsService.getSettings(userId); - const bookIds = await this.annotationSync.listPendingKoboChangeBookIds(userId, String(deviceId), { + const annotationBookIds = await this.annotationSync.listPendingKoboChangeBookIds(userId, String(deviceId), { includeAllOrigins: settings.syncBookOrbitAnnotationsToKobo, resolverVersion: this.koboSpanConverter.version, }); + const changed = [...new Set([...annotationBookIds, ...(await this.bookmarkLinks.findBookIdsWithRemovedBookmarks(userId, deviceId))])]; + // A device can only fetch and acknowledge annotations for books it holds. Another device's + // annotations on a book this one never received stay unserved, so reporting them would list the + // book on every sync, and a device busy with those leaves the fetch for a real change detached + // from its sync, where it is never applied. + const bookIds = await this.syncService.findBookIdsOnDevice(userId, deviceId, changed); if (bookIds.length === 0) return []; const identities = await this.bookIdentityService.findByBookIds(userId, bookIds); return [...identities.values()].map((identity) => identity.entitlementId); } + /** + * Lands the device's dogears as bookmarks. One the book cannot place stays on the annotation path, + * where it is still served back, since the device deletes whatever it is not served. + */ + private async ingestDogears( + userId: number, + deviceId: number, + bookId: number, + kepub: ReadyKepub, + dogears: IncomingUpsert[], + ): Promise<{ created: number; updated: number; placed: Set }> { + const placed = new Set(); + let created = 0; + let updated = 0; + if (dogears.length === 0) return { created, updated, placed }; + const known = new Set( + ( + await this.bookmarkLinks.findByExternalKeys( + userId, + deviceId, + dogears.map((dogear) => dogear.id), + ) + ).map((link) => link.externalKey), + ); + for (const dogear of dogears) { + if (known.has(dogear.id)) { + await this.bookmarkLinks.updateLocation(userId, deviceId, dogear.id, dogear.location); + placed.add(dogear.id); + updated += 1; + } else if ( + await this.landBookmark(userId, deviceId, bookId, kepub, dogear.id, dogear.location, this.bookmarkTitle(dogear.note, dogear.chapterTitle)) + ) { + placed.add(dogear.id); + created += 1; + } + } + return { created, updated, placed }; + } + + /** + * Dogears uploaded before Kobo bookmarks existed were stored as empty highlights. Each one this + * device holds moves to a bookmark under the same id, so the device keeps it as a dogear. + */ + private async convertLegacyDogears(userId: number, deviceId: number, bookId: number, kepub: ReadyKepub): Promise { + const legacy = (await this.annotationSync.listStatesBySourceForBook(userId, 'kobo', bookId)).filter( + ({ state, annotation }) => + state.deviceId === String(deviceId) && annotation.origin === 'kobo' && annotation.text === '' && annotation.deletedAt === null, + ); + if (legacy.length === 0) return; + const positions = await this.annotationSync.findPositions( + legacy.map(({ annotation }) => annotation.id), + ['kobo_span'], + ); + const locations = new Map( + positions.map((position) => [position.annotationId, (position.extras as { koboLocation?: unknown } | null)?.koboLocation]), + ); + const converted: string[] = []; + for (const { state, annotation } of legacy) { + const location = locations.get(annotation.id); + if (!location) continue; + const title = this.bookmarkTitle(annotation.note, annotation.chapterTitle); + if (await this.landBookmark(userId, deviceId, bookId, kepub, state.externalKey, location, title)) converted.push(state.externalKey); + } + if (converted.length === 0) return; + await this.annotationSync.applyDeviceDeletes({ + userId, + source: 'kobo', + deviceId: String(deviceId), + bookId, + deletes: converted.map((externalKey) => ({ externalKey })), + }); + this.logger.log( + `[${PATCH_EVENT}] userId=${userId} bookId=${bookId} deviceId=${deviceId} converted=${converted.length} - kobo dogears moved from highlights to bookmarks`, + ); + } + + private async landBookmark( + userId: number, + deviceId: number, + bookId: number, + kepub: ReadyKepub, + externalKey: string, + location: unknown, + title: string, + ): Promise { + const point = await this.dogearToCanonical(kepub, location); + if (!point) return false; + const landed = await this.bookmarkSync.upsertFromDevice(userId, bookId, { + cfi: point.cfi, + title, + devicePos: point.xpointer, + pageno: null, + origin: 'kobo', + }); + if (!landed) return false; + await this.bookmarkLinks.link({ bookmarkId: landed.row.id, userId, deviceId, bookId, externalKey, location }); + return true; + } + + private async dogearToCanonical(kepub: ReadyKepub, location: unknown): Promise<{ cfi: string; xpointer: string } | null> { + const span = location && typeof location === 'object' ? (location as { span?: unknown }).span : null; + if (!span || typeof span !== 'object') return null; + const { chapterFilename, startPath } = span as { chapterFilename?: unknown; startPath?: unknown }; + const spanId = typeof startPath === 'string' ? parseSpanSelector(startPath) : null; + if (typeof chapterFilename !== 'string' || !spanId) return null; + const outcome = await this.koboSpanConverter.koboBookmarkToPositions({ bookFileId: kepub.file.id, ctx: kepub.ctx, chapterFilename, spanId }); + return outcome.status === 'failed' || !outcome.cfi || !outcome.xpointer ? null : { cfi: outcome.cfi, xpointer: outcome.xpointer }; + } + + /** A device delete of a dogear deletes its bookmark everywhere, as a device delete of a highlight does. */ + private async removeDeviceBookmarks(userId: number, deviceId: number, externalKeys: string[]): Promise<{ removed: number; keys: Set }> { + const links = await this.bookmarkLinks.findByExternalKeys(userId, deviceId, externalKeys); + if (links.length === 0) return { removed: 0, keys: new Set() }; + const bookmarkIds = links.map((link) => link.bookmarkId); + const removed = await this.bookmarkSync.tombstone(userId, bookmarkIds); + await this.bookmarkLinks.deleteForBookmarks(userId, deviceId, bookmarkIds); + return { removed, keys: new Set(links.map((link) => link.externalKey)) }; + } + + private async listServedDogears( + userId: number, + deviceId: number, + bookId: number, + ): Promise<{ served: { externalKey: string; clientLastModifiedUtc: string; location: unknown }[]; removedBookmarkIds: number[] }> { + const served: { externalKey: string; clientLastModifiedUtc: string; location: unknown }[] = []; + const removedBookmarkIds: number[] = []; + for (const link of await this.bookmarkLinks.findForBook(userId, deviceId, bookId)) { + if (link.bookmarkDeletedAt) { + removedBookmarkIds.push(link.bookmarkId); + continue; + } + served.push({ + externalKey: link.externalKey, + clientLastModifiedUtc: link.bookmarkUpdatedAt.toISOString().replace(/\.\d{3}Z$/, 'Z'), + location: link.location, + }); + } + return { served, removedBookmarkIds }; + } + + /** A dogear has no text of its own; its note, then its chapter, names it, as KOReader's do. */ + private bookmarkTitle(note: string | null, chapterTitle: string | null): string { + return (note?.trim() || chapterTitle?.trim() || 'Bookmark').slice(0, BOOKMARK_TITLE_MAX_LENGTH); + } + private toIncomingAnnotation(operation: IncomingUpsert): IncomingDeviceAnnotation { const datetime = formatDeviceDatetime(operation.clientModifiedAt); const span = this.parseLocationSpans(operation.location); @@ -351,9 +552,17 @@ export class KoboAnnotationExchangeService { } } - private buildEtag(count: number, entries: ServedAckPlan['entries'], renditionHash?: string | null): string { + private buildEtag( + count: number, + entries: ServedAckPlan['entries'], + renditionHash?: string | null, + dogears: { clientLastModifiedUtc: string }[] = [], + ): string { const maxVersion = entries.reduce((max, entry) => Math.max(max, entry.version), 0); - return `W/"${maxVersion}-${count}${renditionHash ? `-${renditionHash}` : ''}"`; + const bookmarks = dogears.length + ? `-b${dogears.length}-${dogears.reduce((max, row) => (row.clientLastModifiedUtc > max ? row.clientLastModifiedUtc : max), '')}` + : ''; + return `W/"${maxVersion}-${count}${renditionHash ? `-${renditionHash}` : ''}${bookmarks}"`; } private etagMatches(ifNoneMatch: string | undefined, etag: string): boolean { diff --git a/server/src/modules/kobo/services/kobo-annotation-payload.test.ts b/server/src/modules/kobo/services/kobo-annotation-payload.test.ts new file mode 100644 index 000000000..a2bef36d8 --- /dev/null +++ b/server/src/modules/kobo/services/kobo-annotation-payload.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from 'vitest'; + +import { extractKoboAnnotationOperations, toKoboReadingServiceDogear } from './kobo-annotation-payload'; + +const location = { + span: { + chapterFilename: 'OEBPS/Text/018.xhtml', + chapterProgress: 0.25, + chapterTitle: 'Chapter Eleven', + startPath: 'OEBPS/Text/018.xhtml#kobo.23.1', + endPath: 'OEBPS/Text/018.xhtml#kobo.23.1', + startChar: 0, + endChar: 0, + }, +}; + +describe('kobo annotation payload', () => { + it('reads a device dogear with its type and chapter', () => { + const [operation] = extractKoboAnnotationOperations({ + updatedAnnotations: [ + { clientLastModifiedUtc: '2026-10-17T01:28:39Z', highlightedText: '', id: '13b9bf63-c3d8-4f56-98f8-468f744f1423', location, type: 'dogear' }, + ], + }); + + expect(operation).toMatchObject({ kind: 'upsert', type: 'dogear', text: '', chapterTitle: 'Chapter Eleven', location }); + }); + + it('serves a bookmark back in the shape the device uploads a dogear, with no text or colour', () => { + const served = toKoboReadingServiceDogear({ externalKey: 'dogear-1', clientLastModifiedUtc: '2026-10-17T01:28:39Z', location }); + + expect(served).toEqual({ + attachments: {}, + clientLastModifiedUtc: '2026-10-17T01:28:39Z', + context: null, + highlightedText: '', + id: 'dogear-1', + location, + type: 'dogear', + }); + expect(served).not.toHaveProperty('highlightColor'); + }); +}); diff --git a/server/src/modules/kobo/services/kobo-annotation-payload.ts b/server/src/modules/kobo/services/kobo-annotation-payload.ts index 50509312a..2e4690216 100644 --- a/server/src/modules/kobo/services/kobo-annotation-payload.ts +++ b/server/src/modules/kobo/services/kobo-annotation-payload.ts @@ -4,7 +4,7 @@ export interface KoboReadingServiceAnnotation { attachments: Record; clientLastModifiedUtc: string; context: null; - highlightColor: string; + highlightColor?: string; highlightedText: string; id: string; location: unknown; @@ -63,6 +63,23 @@ export function toKoboReadingServiceAnnotation(row: ServeAnnotation): KoboReadin }; } +/** A bookmark, served back as the dogear the device created: no text and no colour, as it uploads one. */ +export function toKoboReadingServiceDogear(row: { + externalKey: string; + clientLastModifiedUtc: string; + location: unknown; +}): KoboReadingServiceAnnotation { + return { + attachments: {}, + clientLastModifiedUtc: row.clientLastModifiedUtc, + context: null, + highlightedText: '', + id: row.externalKey, + location: row.location, + type: 'dogear', + }; +} + export function extractKoboAnnotationOperations(body: unknown): IncomingOperation[] { if (!isRecord(body)) return []; diff --git a/server/src/modules/kobo/services/kobo-sync.service.ts b/server/src/modules/kobo/services/kobo-sync.service.ts index c1fcaa0aa..8919bcd30 100644 --- a/server/src/modules/kobo/services/kobo-sync.service.ts +++ b/server/src/modules/kobo/services/kobo-sync.service.ts @@ -206,6 +206,25 @@ export class KoboSyncService { return [this.buildBookMetadata(book, deviceToken, baseUrl)]; } + /** Of the given books, those this device holds: in its library snapshot, and not removed or being removed. */ + async findBookIdsOnDevice(userId: number, deviceId: number, bookIds: number[]): Promise { + if (bookIds.length === 0) return []; + const rows = await this.db + .select({ bookId: schema.koboSnapshotBooks.bookId }) + .from(schema.koboSnapshotBooks) + .innerJoin(schema.koboLibrarySnapshots, eq(schema.koboLibrarySnapshots.id, schema.koboSnapshotBooks.snapshotId)) + .where( + and( + eq(schema.koboLibrarySnapshots.userId, userId), + eq(schema.koboLibrarySnapshots.deviceId, deviceId), + inArray(schema.koboSnapshotBooks.bookId, bookIds), + eq(schema.koboSnapshotBooks.pendingDelete, false), + eq(schema.koboSnapshotBooks.removedByDevice, false), + ), + ); + return rows.map((row) => row.bookId); + } + async removeBookFromSync(userId: number, deviceId: number, bookId: number): Promise { const snapshot = await this.findDeviceSnapshot(userId, deviceId); if (!snapshot) return; diff --git a/server/test/kobo-annotation-sync.e2e-spec.ts b/server/test/kobo-annotation-sync.e2e-spec.ts index 7995bce6d..632ce36ca 100644 --- a/server/test/kobo-annotation-sync.e2e-spec.ts +++ b/server/test/kobo-annotation-sync.e2e-spec.ts @@ -40,6 +40,19 @@ const KOBO_LOCATION = { }, }; +const DOGEAR_LOCATION = { + span: { + startPath: 'span#kobo\\.1\\.1', + startChar: 0, + endPath: 'span#kobo\\.1\\.1', + endChar: 0, + chapterFilename: 'OPS/chapter.xhtml', + chapterProgress: 0, + chapterTitle: 'Chapter 1', + }, +}; +const DOGEAR_CFI = 'epubcfi(/6/2!/4/2/1:0)'; + const KOREADER_USERNAME = `kobo-mesh-device-${randomUUID().slice(0, 8)}`; const KOREADER_PASSWORD = 'KoboMeshDevicePass123'; @@ -523,4 +536,112 @@ describe.each(['epub', 'kepub'])('Kobo annotation sync with %s (e2e)', { timeout expect(served?.location).toMatchObject({ span: { startChar: 0, endChar: 7, chapterFilename: 'OPS/chapter.xhtml' } }); expect((await findPositions(annotation.id)).get('cfi')).toMatchObject({ status: 'exact', pos0: FIXTURE_CFI }); }); + + async function findBookmarks() { + return ctx.db + .select() + .from(schema.bookmarks) + .where(and(eq(schema.bookmarks.userId, adminUserId), eq(schema.bookmarks.bookId, epub.bookId))); + } + + async function findBookmarkLinks() { + return ctx.db.select().from(schema.koboBookmarkLinks).where(eq(schema.koboBookmarkLinks.deviceId, deviceDbId)); + } + + function dogear(id: string) { + return { id, type: 'dogear', highlightedText: '', location: DOGEAR_LOCATION, clientLastModifiedUtc: '2026-06-13T08:00:00Z' }; + } + + it('lands a device dogear as a bookmark and serves it back as a dogear', async () => { + const dogearId = randomUUID(); + await patchAnnotations({ updatedAnnotations: [dogear(dogearId)] }); + + const live = (await findBookmarks()).filter((row) => row.deletedAt === null); + expect(live).toHaveLength(1); + expect(live[0]).toMatchObject({ origin: 'kobo', title: 'Chapter 1', cfi: DOGEAR_CFI }); + expect((await findCanonicalAnnotations()).filter((row) => row.text === '' && row.deletedAt === null)).toEqual([]); + + const served = (await getAnnotations()).annotations.find((row) => row.id === dogearId); + expect(served).toMatchObject({ type: 'dogear', highlightedText: '' }); + expect(served).not.toHaveProperty('highlightColor'); + expect(served?.location).toEqual(DOGEAR_LOCATION); + }); + + it('reports a bookmark deleted in BookOrbit and leaves it out of the next pull', async () => { + const [bookmark] = (await findBookmarks()).filter((row) => row.deletedAt === null); + const removed = await ctx.app.inject({ + method: 'DELETE', + url: `/api/v1/books/${epub.bookId}/bookmarks/${bookmark!.id}`, + headers: authHeader(ctx.adminToken), + }); + expect(removed.statusCode).toBe(204); + + expect(await checkForChanges()).toContain(contentId); + expect((await getAnnotations()).annotations.some((row) => row.type === 'dogear')).toBe(false); + expect(await findBookmarkLinks()).toEqual([]); + expect(await checkForChanges()).not.toContain(contentId); + }); + + it('deletes the bookmark when the device deletes its dogear', async () => { + const dogearId = randomUUID(); + await patchAnnotations({ updatedAnnotations: [dogear(dogearId)] }); + expect((await findBookmarks()).filter((row) => row.deletedAt === null)).toHaveLength(1); + + await patchAnnotations({ deletedAnnotationIds: [dogearId] }); + + expect((await findBookmarks()).filter((row) => row.deletedAt === null)).toEqual([]); + expect(await findBookmarkLinks()).toEqual([]); + }); + + it('moves a dogear stored as an empty highlight to a bookmark under the same id', async () => { + const legacyId = randomUUID(); + await patchAnnotations({ + updatedAnnotations: [{ ...dogear(legacyId), type: 'highlight', highlightColor: '#C6E09E' }], + }); + const legacy = (await findCanonicalAnnotations()).find((row) => row.text === '' && row.deletedAt === null); + expect(legacy).toBeDefined(); + + const served = (await getAnnotations()).annotations.find((row) => row.id === legacyId); + + expect(served).toMatchObject({ type: 'dogear' }); + expect((await findCanonicalAnnotations()).find((row) => row.id === legacy!.id)?.deletedAt).not.toBeNull(); + expect((await findBookmarks()).filter((row) => row.deletedAt === null)).toEqual([expect.objectContaining({ origin: 'kobo', cfi: DOGEAR_CFI })]); + expect(await findBookmarkLinks()).toEqual([expect.objectContaining({ externalKey: legacyId })]); + }); + + it('serves a known annotation at the location the device reported when this copy cannot place it', async () => { + const highlightId = randomUUID(); + await patchAnnotations({ + updatedAnnotations: [ + { + id: highlightId, + type: 'highlight', + highlightColor: '#C6E09E', + highlightedText: 'fixture', + location: KOBO_LOCATION, + clientLastModifiedUtc: '2026-06-14T08:00:00Z', + }, + ], + }); + await getAnnotations(); + const annotation = (await findCanonicalAnnotations()).find((row) => row.deletedAt === null && row.deviceCreatedAt === '2026-06-14 08:00:00')!; + // Placement fails for every annotation below, so the others leave first; this one is the subject. + for (const other of (await findCanonicalAnnotations()).filter((row) => row.deletedAt === null && row.id !== annotation.id)) { + await ctx.db.update(schema.annotations).set({ deletedAt: new Date() }).where(eq(schema.annotations.id, other.id)); + } + await ctx.db + .update(schema.annotationPositions) + .set({ extras: { koboLocation: KOBO_LOCATION, renditionHash: 'an-earlier-copy' } }) + .where(and(eq(schema.annotationPositions.annotationId, annotation.id), eq(schema.annotationPositions.format, 'kobo_span'))); + const placement = vi + .spyOn(ctx.app.get(KoboSpanConverterService), 'canonicalToKoboSpan') + .mockResolvedValue({ status: 'failed', reason: 'span_not_found', renditionHash: 'this-copy' }); + try { + const served = (await getAnnotations()).annotations.find((row) => row.id === highlightId); + + expect(served?.location).toEqual(KOBO_LOCATION); + } finally { + placement.mockRestore(); + } + }); });