import { pgTable, text, serial, integer, boolean, timestamp, pgEnum, primaryKey, json, unique } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; // Enums export const roleEnum = pgEnum('role', ['user', 'contributor', 'reviewer', 'editor', 'admin']); export const workTypeEnum = pgEnum('work_type', ['poem', 'story', 'novel', 'play', 'essay', 'other']); // Users table export const users = pgTable("users", { id: serial("id").primaryKey(), username: text("username").notNull().unique(), password: text("password").notNull(), email: text("email").notNull().unique(), displayName: text("display_name"), bio: text("bio"), avatar: text("avatar"), role: roleEnum("role").default('user').notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), }); // Authors table export const authors = pgTable("authors", { id: serial("id").primaryKey(), name: text("name").notNull(), slug: text("slug").notNull().unique(), birthYear: integer("birth_year"), deathYear: integer("death_year"), country: text("country"), biography: text("biography"), portrait: text("portrait"), createdAt: timestamp("created_at").defaultNow().notNull(), createdBy: integer("created_by").references(() => users.id), }); // Works table (original literary works) export const works = pgTable("works", { id: serial("id").primaryKey(), title: text("title").notNull(), slug: text("slug").notNull().unique(), authorId: integer("author_id").references(() => authors.id).notNull(), year: integer("year"), type: workTypeEnum("type").default('other').notNull(), language: text("language").notNull(), content: text("content").notNull(), description: text("description"), createdAt: timestamp("created_at").defaultNow().notNull(), createdBy: integer("created_by").references(() => users.id), }); // Translations table export const translations = pgTable("translations", { id: serial("id").primaryKey(), workId: integer("work_id").references(() => works.id).notNull(), title: text("title").notNull(), language: text("language").notNull(), content: text("content").notNull(), translatorId: integer("translator_id").references(() => users.id).notNull(), year: integer("year"), notes: text("notes"), createdAt: timestamp("created_at").defaultNow().notNull(), }); // Tags table export const tags = pgTable("tags", { id: serial("id").primaryKey(), name: text("name").notNull().unique(), type: text("type").notNull(), // genre, period, theme, etc. createdAt: timestamp("created_at").defaultNow().notNull(), }); // Work-Tag relations (many-to-many) export const workTags = pgTable("work_tags", { workId: integer("work_id").references(() => works.id).notNull(), tagId: integer("tag_id").references(() => tags.id).notNull(), }, (t) => ({ pk: primaryKey(t.workId, t.tagId), })); // Bookmarks export const bookmarks = pgTable("bookmarks", { id: serial("id").primaryKey(), userId: integer("user_id").references(() => users.id).notNull(), workId: integer("work_id").references(() => works.id).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), }, (t) => ({ unq: unique().on(t.userId, t.workId), })); // Comments export const comments = pgTable("comments", { id: serial("id").primaryKey(), userId: integer("user_id").references(() => users.id).notNull(), workId: integer("work_id").references(() => works.id).notNull(), translationId: integer("translation_id").references(() => translations.id), lineNumber: integer("line_number"), content: text("content").notNull(), parentId: integer("parent_id").references(() => comments.id), createdAt: timestamp("created_at").defaultNow().notNull(), }); // Likes (polymorphic) export const likes = pgTable("likes", { id: serial("id").primaryKey(), userId: integer("user_id").references(() => users.id).notNull(), entityType: text("entity_type").notNull(), // 'work', 'translation', 'comment' entityId: integer("entity_id").notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), }, (t) => ({ unq: unique().on(t.userId, t.entityType, t.entityId), })); // Collections (curated sets of works) export const collections = pgTable("collections", { id: serial("id").primaryKey(), name: text("name").notNull(), slug: text("slug").notNull().unique(), description: text("description"), userId: integer("user_id").references(() => users.id).notNull(), isPublic: boolean("is_public").default(true).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), }); // Collection items export const collectionItems = pgTable("collection_items", { id: serial("id").primaryKey(), collectionId: integer("collection_id").references(() => collections.id).notNull(), workId: integer("work_id").references(() => works.id).notNull(), order: integer("order").default(0).notNull(), addedAt: timestamp("added_at").defaultNow().notNull(), }); // Timeline events for authors export const timelineEvents = pgTable("timeline_events", { id: serial("id").primaryKey(), authorId: integer("author_id").references(() => authors.id).notNull(), year: integer("year").notNull(), title: text("title").notNull(), description: text("description"), createdAt: timestamp("created_at").defaultNow().notNull(), }); // Blog posts export const blogPosts = pgTable("blog_posts", { id: serial("id").primaryKey(), title: text("title").notNull(), slug: text("slug").notNull().unique(), content: text("content").notNull(), authorId: integer("author_id").references(() => users.id).notNull(), excerpt: text("excerpt"), publishedAt: timestamp("published_at"), createdAt: timestamp("created_at").defaultNow().notNull(), }); // Blog post tags export const blogPostTags = pgTable("blog_post_tags", { postId: integer("post_id").references(() => blogPosts.id).notNull(), tagId: integer("tag_id").references(() => tags.id).notNull(), }, (t) => ({ pk: primaryKey(t.postId, t.tagId), })); // Reading progress export const readingProgress = pgTable("reading_progress", { id: serial("id").primaryKey(), userId: integer("user_id").references(() => users.id).notNull(), workId: integer("work_id").references(() => works.id).notNull(), translationId: integer("translation_id").references(() => translations.id), progress: integer("progress").default(0).notNull(), // percentage lastReadAt: timestamp("last_read_at").defaultNow().notNull(), }); // Insert schemas export const insertUserSchema = createInsertSchema(users).omit({ id: true, createdAt: true, role: true }); export const insertAuthorSchema = createInsertSchema(authors).omit({ id: true, createdAt: true }); export const insertWorkSchema = createInsertSchema(works).omit({ id: true, createdAt: true }); export const insertTranslationSchema = createInsertSchema(translations).omit({ id: true, createdAt: true }); export const insertTagSchema = createInsertSchema(tags).omit({ id: true, createdAt: true }); export const insertBookmarkSchema = createInsertSchema(bookmarks).omit({ id: true, createdAt: true }); export const insertCommentSchema = createInsertSchema(comments).omit({ id: true, createdAt: true }); export const insertLikeSchema = createInsertSchema(likes).omit({ id: true, createdAt: true }); export const insertCollectionSchema = createInsertSchema(collections).omit({ id: true, createdAt: true }); export const insertCollectionItemSchema = createInsertSchema(collectionItems).omit({ id: true, addedAt: true }); export const insertTimelineEventSchema = createInsertSchema(timelineEvents).omit({ id: true, createdAt: true }); export const insertBlogPostSchema = createInsertSchema(blogPosts).omit({ id: true, createdAt: true }); export const insertReadingProgressSchema = createInsertSchema(readingProgress).omit({ id: true, lastReadAt: true }); // Types export type InsertUser = z.infer; export type User = typeof users.$inferSelect; export type InsertAuthor = z.infer; export type Author = typeof authors.$inferSelect; export type InsertWork = z.infer; export type Work = typeof works.$inferSelect; export type InsertTranslation = z.infer; export type Translation = typeof translations.$inferSelect; export type InsertTag = z.infer; export type Tag = typeof tags.$inferSelect; export type InsertBookmark = z.infer; export type Bookmark = typeof bookmarks.$inferSelect; export type InsertComment = z.infer; export type Comment = typeof comments.$inferSelect; export type InsertLike = z.infer; export type Like = typeof likes.$inferSelect; export type InsertCollection = z.infer; export type Collection = typeof collections.$inferSelect; export type InsertCollectionItem = z.infer; export type CollectionItem = typeof collectionItems.$inferSelect; export type InsertTimelineEvent = z.infer; export type TimelineEvent = typeof timelineEvents.$inferSelect; export type InsertBlogPost = z.infer; export type BlogPost = typeof blogPosts.$inferSelect; export type InsertReadingProgress = z.infer; export type ReadingProgress = typeof readingProgress.$inferSelect;