This commit is contained in:
kikootwo
2026-03-05 11:31:49 -05:00
41 changed files with 3654 additions and 710 deletions
@@ -0,0 +1,49 @@
-- CreateTable
CREATE TABLE "hardcover_shelves" (
"id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"list_id" TEXT NOT NULL,
"api_token" TEXT NOT NULL,
"last_sync_at" TIMESTAMP(3),
"book_count" INTEGER,
"cover_urls" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "hardcover_shelves_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "hardcover_book_mappings" (
"id" TEXT NOT NULL,
"hardcover_book_id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"author" TEXT NOT NULL,
"audible_asin" TEXT,
"cover_url" TEXT,
"no_match" BOOLEAN NOT NULL DEFAULT false,
"last_search_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "hardcover_book_mappings_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "hardcover_shelves_user_id_idx" ON "hardcover_shelves"("user_id");
-- CreateIndex
CREATE UNIQUE INDEX "hardcover_shelves_user_id_list_id_key" ON "hardcover_shelves"("user_id", "list_id");
-- CreateIndex
CREATE UNIQUE INDEX "hardcover_book_mappings_hardcover_book_id_key" ON "hardcover_book_mappings"("hardcover_book_id");
-- CreateIndex
CREATE INDEX "hardcover_book_mappings_hardcover_book_id_idx" ON "hardcover_book_mappings"("hardcover_book_id");
-- CreateIndex
CREATE INDEX "hardcover_book_mappings_audible_asin_idx" ON "hardcover_book_mappings"("audible_asin");
-- AddForeignKey
ALTER TABLE "hardcover_shelves" ADD CONSTRAINT "hardcover_shelves_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,41 @@
-- CreateTable
CREATE TABLE "book_mappings" (
"id" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"external_book_id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"author" TEXT NOT NULL,
"audible_asin" TEXT,
"cover_url" TEXT,
"no_match" BOOLEAN NOT NULL DEFAULT false,
"last_search_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "book_mappings_pkey" PRIMARY KEY ("id")
);
-- Migrate data from goodreads_book_mappings
INSERT INTO "book_mappings" ("id", "provider", "external_book_id", "title", "author", "audible_asin", "cover_url", "no_match", "last_search_at", "created_at", "updated_at")
SELECT "id", 'goodreads', "goodreads_book_id", "title", "author", "audible_asin", "cover_url", "no_match", "last_search_at", "created_at", "updated_at"
FROM "goodreads_book_mappings";
-- Migrate data from hardcover_book_mappings
INSERT INTO "book_mappings" ("id", "provider", "external_book_id", "title", "author", "audible_asin", "cover_url", "no_match", "last_search_at", "created_at", "updated_at")
SELECT "id", 'hardcover', "hardcover_book_id", "title", "author", "audible_asin", "cover_url", "no_match", "last_search_at", "created_at", "updated_at"
FROM "hardcover_book_mappings";
-- DropTable
DROP TABLE "goodreads_book_mappings";
-- DropTable
DROP TABLE "hardcover_book_mappings";
-- CreateIndex
CREATE UNIQUE INDEX "book_mappings_provider_external_book_id_key" ON "book_mappings"("provider", "external_book_id");
-- CreateIndex
CREATE INDEX "book_mappings_provider_external_book_id_idx" ON "book_mappings"("provider", "external_book_id");
-- CreateIndex
CREATE INDEX "book_mappings_audible_asin_idx" ON "book_mappings"("audible_asin");
+47 -13
View File
@@ -66,6 +66,7 @@ model User {
bookDateRecommendations BookDateRecommendation[]
bookDateSwipes BookDateSwipe[]
goodreadsShelves GoodreadsShelf[]
hardcoverShelves HardcoverShelf[]
reportedIssues ReportedIssue[] @relation("Reporter")
resolvedIssues ReportedIssue[] @relation("Resolver")
createdApiTokens ApiToken[] @relation("CreatedApiTokens")
@@ -538,21 +539,54 @@ model GoodreadsShelf {
@@map("goodreads_shelves")
}
model GoodreadsBookMapping {
id String @id @default(uuid())
goodreadsBookId String @unique @map("goodreads_book_id")
title String
author String
audibleAsin String? @map("audible_asin")
coverUrl String? @map("cover_url") @db.Text
noMatch Boolean @default(false) @map("no_match")
lastSearchAt DateTime? @map("last_search_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// ============================================================================
// UNIFIED BOOK MAPPING TABLE
// Global book-to-ASIN mapping cache shared across all shelf providers.
// Uses provider + externalBookId composite key for cross-provider dedup.
// ============================================================================
@@index([goodreadsBookId])
model BookMapping {
id String @id @default(uuid())
provider String // "goodreads", "hardcover", etc.
externalBookId String @map("external_book_id")
title String
author String
audibleAsin String? @map("audible_asin")
coverUrl String? @map("cover_url") @db.Text
noMatch Boolean @default(false) @map("no_match")
lastSearchAt DateTime? @map("last_search_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([provider, externalBookId])
@@index([provider, externalBookId])
@@index([audibleAsin])
@@map("goodreads_book_mappings")
@@map("book_mappings")
}
// ============================================================================
// HARDCOVER SYNC TABLES
// Per-user Hardcover list subscriptions
// ============================================================================
model HardcoverShelf {
id String @id @default(uuid())
userId String @map("user_id")
name String // Extracted from Hardcover API list name or status
listId String @map("list_id") // Hardcover List ID or Status ID
apiToken String @map("api_token") @db.Text // User's personal access token for hardcover api
lastSyncAt DateTime? @map("last_sync_at")
bookCount Int? @map("book_count")
coverUrls String? @map("cover_urls") @db.Text // JSON array of cover image URLs
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, listId])
@@index([userId])
@@map("hardcover_shelves")
}
// ============================================================================