import { z } from "zod"; export const RoleSchema = z.enum(["admin", "user"]); export type Role = z.infer; export const UserSchema = z.object({ id: z.number().int().positive(), email: z.string().email(), name: z.string().nullable(), role: RoleSchema, createdAt: z.string() }); export type UserDto = z.infer; export const BootstrapAdminSchema = z.object({ email: z.string().email(), password: z.string().min(8), name: z.string().min(1).max(120).optional() }); export type BootstrapAdminDto = z.infer; export const LoginSchema = z.object({ email: z.string().email(), password: z.string().min(1) }); export type LoginDto = z.infer; export const AuthStatusSchema = z.object({ hasUsers: z.boolean(), initialAdminEmail: z.string().email(), initialAdminPasswordIsDefault: z.boolean() }); export type AuthStatusDto = z.infer; export const UpdateAccountSchema = z .object({ email: z.string().email().optional(), name: z.string().min(1).max(120).nullable().optional(), currentPassword: z.string().min(1), newPassword: z.string().min(8).optional() }) .refine((value) => value.email !== undefined || value.name !== undefined || value.newPassword !== undefined, { message: "At least one account field must be changed" }); export type UpdateAccountDto = z.infer; export const CreateUserSchema = z.object({ email: z.string().email(), password: z.string().min(8), name: z.string().min(1).max(120).optional(), role: RoleSchema.default("user") }); export type CreateUserDto = z.infer; export const UpdateUserSchema = z.object({ email: z.string().email().optional(), password: z.string().min(8).optional(), name: z.string().min(1).max(120).nullable().optional(), role: RoleSchema.optional() }); export type UpdateUserDto = z.infer; export const LibrarySchema = z.object({ id: z.number().int().positive(), name: z.string(), path: z.string(), enabled: z.boolean(), createdAt: z.string(), updatedAt: z.string() }); export type LibraryDto = z.infer; export const CreateLibrarySchema = z.object({ name: z.string().min(1).max(160), path: z.string().min(1), enabled: z.boolean().default(true) }); export type CreateLibraryDto = z.infer; export const UpdateLibrarySchema = z.object({ name: z.string().min(1).max(160).optional(), path: z.string().min(1).optional(), enabled: z.boolean().optional() }); export type UpdateLibraryDto = z.infer; export const SeriesSchema = z.object({ id: z.number().int().positive(), title: z.string(), normalizedTitle: z.string(), description: z.string().nullable(), publisher: z.string().nullable(), createdAt: z.string(), updatedAt: z.string() }); export type SeriesDto = z.infer; export const BookSchema = z.object({ id: z.number().int().positive(), libraryId: z.number().int().positive(), seriesId: z.number().int().positive().nullable().optional(), title: z.string(), author: z.string().nullable(), description: z.string().nullable(), isbn: z.string().nullable(), isbn13: z.string().nullable(), language: z.string().nullable(), publisher: z.string().nullable(), publishedDate: z.string().nullable(), volumeNumber: z.number().int().nullable().optional(), volumeLabel: z.string().nullable().optional(), format: z.enum(["epub", "pdf", "cbz", "cbr"]), filePath: z.string(), coverPath: z.string().nullable(), metadataStatus: z.enum(["enriched", "partial", "none"]).default("none"), metadataProvenance: z.record(z.string(), z.string()).default({}), series: SeriesSchema.nullable().optional(), scanStatus: z.enum(["idle", "running", "succeeded", "failed"]).default("idle"), enrichmentStatus: z.enum(["idle", "running", "succeeded", "failed"]).default("idle"), fileSize: z.number().int().nonnegative(), fileMtime: z.string(), createdAt: z.string(), updatedAt: z.string() }); export type BookDto = z.infer; export const BookQuerySchema = z.object({ q: z.string().optional(), format: z.enum(["epub", "pdf", "cbz", "cbr"]).optional(), libraryId: z.coerce.number().int().positive().optional(), limit: z.coerce.number().int().min(1).max(100).optional(), offset: z.coerce.number().int().min(0).default(0) }); export type BookQueryDto = z.infer; export const ProgressSchema = z.object({ bookId: z.number().int().positive(), locator: z.string().min(1), percent: z.number().min(0).max(100), updatedAt: z.string() }); export type ProgressDto = z.infer; export const UpdateProgressSchema = z.object({ locator: z.string().min(1), percent: z.number().min(0).max(100) }); export type UpdateProgressDto = z.infer; export const ReaderPreferencesSchema = z.object({ mode: z.enum(["paged", "scrolled", "horizontal", "vertical"]).default("paged"), fit: z.enum(["page", "width", "height", "auto"]).nullable().default(null), updatedAt: z.string().optional() }); export type ReaderPreferencesDto = z.infer; export const UpdateReaderPreferencesSchema = z.object({ mode: z.enum(["paged", "scrolled", "horizontal", "vertical"]).optional(), fit: z.enum(["page", "width", "height", "auto"]).nullable().optional() }); export type UpdateReaderPreferencesDto = z.infer; export const JobSchema = z.object({ id: z.number().int().positive(), type: z.string(), status: z.enum(["queued", "running", "succeeded", "failed"]), detail: z.string().nullable(), error: z.string().nullable(), createdAt: z.string(), updatedAt: z.string() }); export type JobDto = z.infer; export const MetadataProviderIdSchema = z.enum(["local", "openlibrary", "googlebooks", "bnf", "mangadex", "comicvine"]); export type MetadataProviderId = z.infer; export const MetadataSourceConfigSchema = z.object({ provider: MetadataProviderIdSchema, enabled: z.boolean(), priority: z.number().int().min(0), hasApiKey: z.boolean().default(false) }); export type MetadataSourceConfigDto = z.infer; export const MetadataSourcesConfigSchema = z.object({ isbnPriorityEnabled: z.boolean(), sources: z.array(MetadataSourceConfigSchema) }); export type MetadataSourcesConfigDto = z.infer; export const UpdateMetadataSourceConfigSchema = z.object({ provider: MetadataProviderIdSchema.exclude(["local"]), enabled: z.boolean(), priority: z.number().int().min(1).max(100), apiKey: z.string().min(1).nullable().optional() }); export type UpdateMetadataSourceConfigDto = z.infer; export const UpdateMetadataSourcesConfigSchema = z.object({ isbnPriorityEnabled: z.boolean().optional(), sources: z.array(UpdateMetadataSourceConfigSchema).optional() }); export type UpdateMetadataSourcesConfigDto = z.infer; export const AutomationFrequencySchema = z.enum(["disabled", "daily", "weekly"]); export type AutomationFrequency = z.infer; export const AutomationScheduleSchema = z.object({ frequency: AutomationFrequencySchema, time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).default("03:00"), dayOfWeek: z.number().int().min(0).max(6).default(1) }); export type AutomationScheduleDto = z.infer; export const AutomationSettingsSchema = z.object({ watchLibraries: z.boolean(), autoEnrichNewBooks: z.boolean(), scanSchedule: AutomationScheduleSchema, enrichSchedule: AutomationScheduleSchema }); export type AutomationSettingsDto = z.infer; export const UpdateAutomationSettingsSchema = AutomationSettingsSchema.partial(); export type UpdateAutomationSettingsDto = z.infer;