- api: cbz utilitaire commun, scanner/métadonnées (+ tests), books, schéma et migrations - web: CbzReader, ReaderPage/locators (+ tests), types et client API - shared: types formats Refs: #16
78 lines
2.7 KiB
TypeScript
78 lines
2.7 KiB
TypeScript
import { integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
|
|
export const users = sqliteTable(
|
|
"users",
|
|
{
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
email: text("email").notNull(),
|
|
name: text("name"),
|
|
passwordHash: text("password_hash").notNull(),
|
|
role: text("role", { enum: ["admin", "user"] }).notNull().default("user"),
|
|
createdAt: text("created_at").notNull(),
|
|
updatedAt: text("updated_at").notNull()
|
|
},
|
|
(table) => ({ emailIdx: uniqueIndex("users_email_unique").on(table.email) })
|
|
);
|
|
|
|
export const libraries = sqliteTable("libraries", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
name: text("name").notNull(),
|
|
path: text("path").notNull(),
|
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
createdAt: text("created_at").notNull(),
|
|
updatedAt: text("updated_at").notNull()
|
|
});
|
|
|
|
export const books = sqliteTable(
|
|
"books",
|
|
{
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
libraryId: integer("library_id")
|
|
.notNull()
|
|
.references(() => libraries.id, { onDelete: "cascade" }),
|
|
title: text("title").notNull(),
|
|
author: text("author"),
|
|
description: text("description"),
|
|
isbn: text("isbn"),
|
|
language: text("language"),
|
|
publisher: text("publisher"),
|
|
publishedDate: text("published_date"),
|
|
format: text("format", { enum: ["epub", "pdf", "cbz"] }).notNull(),
|
|
filePath: text("file_path").notNull(),
|
|
coverPath: text("cover_path"),
|
|
fileSize: integer("file_size").notNull(),
|
|
fileMtime: text("file_mtime").notNull(),
|
|
createdAt: text("created_at").notNull(),
|
|
updatedAt: text("updated_at").notNull()
|
|
},
|
|
(table) => ({ filePathIdx: uniqueIndex("books_file_path_unique").on(table.filePath) })
|
|
);
|
|
|
|
export const progress = sqliteTable(
|
|
"progress",
|
|
{
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
userId: integer("user_id")
|
|
.notNull()
|
|
.references(() => users.id, { onDelete: "cascade" }),
|
|
bookId: integer("book_id")
|
|
.notNull()
|
|
.references(() => books.id, { onDelete: "cascade" }),
|
|
locator: text("locator").notNull(),
|
|
percent: integer("percent").notNull(),
|
|
createdAt: text("created_at").notNull(),
|
|
updatedAt: text("updated_at").notNull()
|
|
},
|
|
(table) => ({ userBookIdx: uniqueIndex("progress_user_book_unique").on(table.userId, table.bookId) })
|
|
);
|
|
|
|
export const jobs = sqliteTable("jobs", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
type: text("type").notNull(),
|
|
status: text("status", { enum: ["queued", "running", "succeeded", "failed"] }).notNull(),
|
|
detail: text("detail"),
|
|
error: text("error"),
|
|
createdAt: text("created_at").notNull(),
|
|
updatedAt: text("updated_at").notNull()
|
|
});
|