reading: owner archive / restore / delete / merge for books (⋯ menu on cards, rows and detail pages), archived filter, tombstones so merged ids redirect and the ingest never recreates deleted works, books-quarantine.py for deleted files
All checks were successful
Deploy production / deploy (push) Successful in 1m11s
All checks were successful
Deploy production / deploy (push) Successful in 1m11s
This commit is contained in:
@@ -71,6 +71,9 @@ export const bookWorks = booksSchema.table(
|
||||
url: text('url'),
|
||||
siteName: text('site_name'),
|
||||
publishedAt: timestamptz('published_at'),
|
||||
// Archived works stay in the database (and keep their files, notes and progress) but are
|
||||
// hidden from everyone except the owner, who can restore them.
|
||||
archivedAt: timestamptz('archived_at'),
|
||||
...timestamps
|
||||
},
|
||||
(t) => [
|
||||
@@ -78,6 +81,7 @@ export const bookWorks = booksSchema.table(
|
||||
.on(t.url)
|
||||
.where(sql`${t.url} is not null`),
|
||||
index('works_kind_idx').on(t.kind),
|
||||
index('works_archived_at_idx').on(t.archivedAt),
|
||||
uniqueIndex('works_isbn13_key')
|
||||
.on(t.isbn13)
|
||||
.where(sql`${t.isbn13} is not null`),
|
||||
@@ -222,3 +226,24 @@ export type BookNote = typeof bookNotes.$inferSelect;
|
||||
export type BookSession = typeof bookSessions.$inferSelect;
|
||||
export type BookKind = (typeof bookKind.enumValues)[number];
|
||||
export type BookReadingStatus = (typeof bookReadingStatus.enumValues)[number];
|
||||
|
||||
// Tombstones for hard-deleted (or merged-away) works. Keeps what the ingest matches on so it
|
||||
// never recreates them, the file paths left on disk (for books-quarantine.py), and, for a
|
||||
// merge, the surviving work so the old URL can redirect there. `id` is the old works.id.
|
||||
export const bookDeletedWorks = booksSchema.table(
|
||||
'deleted_works',
|
||||
{
|
||||
id: integer('id').primaryKey(),
|
||||
title: text('title').notNull(),
|
||||
authors: text('authors').array().notNull().default([]),
|
||||
isbn13: text('isbn13'),
|
||||
annasMd5: text('annas_md5').array().notNull().default([]),
|
||||
fileSha256: text('file_sha256').array().notNull().default([]),
|
||||
filePaths: text('file_paths').array().notNull().default([]),
|
||||
sourceRefs: jsonb('source_refs').$type<Record<string, unknown>>().notNull().default({}),
|
||||
mergedInto: integer('merged_into').references(() => bookWorks.id, { onDelete: 'set null' }),
|
||||
deletedAt: timestamptz('deleted_at').notNull().defaultNow(),
|
||||
quarantinedAt: timestamptz('quarantined_at') // files moved to $BOOKS_ROOT/quarantine/deleted
|
||||
},
|
||||
(t) => [index('deleted_works_merged_into_idx').on(t.mergedInto)]
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user