// Prisma Schema - Complete and Aligned with PostgreSQL // Database schema definition and ORM configuration // Last updated: January 3, 2026 generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = "postgresql://skyartapp:SkyArt2025Pass@localhost:5432/skyartshop?schema=public" } // ===================================================== // ADMIN & AUTH MODELS // ===================================================== model AdminUser { id String @id @default(uuid()) username String @unique email String @unique password String name String? role String @default("admin") isActive Boolean @default(true) @map("isactive") createdAt DateTime @default(now()) @map("createdat") updatedAt DateTime @updatedAt @map("updatedat") @@map("adminusers") } model Role { id Int @id @default(autoincrement()) name String @unique description String? permissions String[] createdAt DateTime @default(now()) @map("createdat") @@map("roles") } // ===================================================== // PRODUCT MODELS // ===================================================== model Product { id String @id @default(uuid()) name String slug String? @unique shortDescription String? @map("shortdescription") @db.Text description String? @db.Text price Decimal @db.Decimal(10, 2) stockQuantity Int @default(0) @map("stockquantity") category String? sku String? weight Decimal? @db.Decimal(10, 2) dimensions String? material String? isActive Boolean @default(true) @map("isactive") isFeatured Boolean @default(false) @map("isfeatured") isBestseller Boolean @default(false) @map("isbestseller") metaKeywords String? @map("metakeywords") @db.Text createdAt DateTime @default(now()) @map("createdat") updatedAt DateTime @updatedAt @map("updatedat") images ProductImage[] @@index([isActive]) @@index([isFeatured, isActive]) @@index([slug]) @@index([category]) @@index([createdAt(sort: Desc)]) @@map("products") } model ProductImage { id String @id @default(uuid()) productId String @map("product_id") imageUrl String @map("image_url") colorVariant String? @map("color_variant") colorCode String? @map("color_code") altText String? @map("alt_text") displayOrder Int @default(0) @map("display_order") isPrimary Boolean @default(false) @map("is_primary") variantPrice Decimal? @map("variant_price") @db.Decimal(10, 2) variantStock Int @default(0) @map("variant_stock") createdAt DateTime @default(now()) @map("created_at") product Product @relation(fields: [productId], references: [id], onDelete: Cascade) @@index([productId]) @@index([productId, isPrimary]) @@index([productId, displayOrder, createdAt]) @@index([colorVariant]) @@map("product_images") } // ===================================================== // PORTFOLIO MODELS // ===================================================== model PortfolioProject { id String @id @default(uuid()) title String description String? @db.Text featuredImage String? @map("featuredimage") imageUrl String? @map("imageurl") images Json? @db.JsonB category String? categoryId Int? @map("categoryid") isActive Boolean @default(true) @map("isactive") displayOrder Int @default(0) @map("displayorder") createdAt DateTime @default(now()) @map("createdat") updatedAt DateTime @updatedAt @map("updatedat") @@index([isActive]) @@index([displayOrder, createdAt(sort: Desc)]) @@map("portfolioprojects") } // ===================================================== // BLOG MODELS // ===================================================== model BlogPost { id String @id @default(uuid()) title String slug String @unique excerpt String? @db.Text content String @db.Text imageUrl String? @map("imageurl") isPublished Boolean @default(true) @map("ispublished") createdAt DateTime @default(now()) @map("createdat") updatedAt DateTime @updatedAt @map("updatedat") @@index([isPublished]) @@index([slug]) @@index([createdAt(sort: Desc)]) @@map("blogposts") } // ===================================================== // PAGE MODELS // ===================================================== model Page { id String @id @default(uuid()) title String slug String @unique pageContent String? @map("pagecontent") @db.Text metaTitle String? @map("metatitle") metaDescription String? @map("metadescription") @db.Text isActive Boolean @default(true) @map("isactive") isPublished Boolean @default(true) @map("ispublished") createdAt DateTime @default(now()) @map("createdat") updatedAt DateTime @updatedAt @map("updatedat") @@index([isActive]) @@index([slug]) @@map("pages") } model HomepageSection { id Int @id @default(autoincrement()) sectionType String @map("sectiontype") title String? content Json? @db.JsonB displayOrder Int @default(0) @map("displayorder") isActive Boolean @default(true) @map("isactive") createdAt DateTime @default(now()) @map("createdat") updatedAt DateTime @updatedAt @map("updatedat") @@index([displayOrder]) @@map("homepagesections") } // ===================================================== // MEDIA LIBRARY MODELS // ===================================================== model Upload { id Int @id @default(autoincrement()) filename String @unique originalName String @map("original_name") filePath String @map("file_path") fileSize Int @map("file_size") mimeType String @map("mime_type") uploadedBy String? @map("uploaded_by") folderId Int? @map("folder_id") usedInType String? @map("used_in_type") usedInId String? @map("used_in_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") folder MediaFolder? @relation(fields: [folderId], references: [id], onDelete: SetNull) @@index([filename]) @@index([createdAt(sort: Desc)]) @@index([folderId]) @@index([usedInType, usedInId]) @@map("uploads") } model MediaFolder { id Int @id @default(autoincrement()) name String parentId Int? @map("parent_id") path String createdBy String? @map("created_by") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") parent MediaFolder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade) children MediaFolder[] @relation("FolderHierarchy") uploads Upload[] @@unique([parentId, name]) @@index([parentId]) @@index([path]) @@map("media_folders") } // ===================================================== // SITE SETTINGS MODELS // ===================================================== model SiteSetting { id Int @id @default(autoincrement()) key String @unique settings Json @default("{}") @db.JsonB createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("site_settings") } model TeamMember { id Int @id @default(autoincrement()) name String position String bio String? @db.Text imageUrl String? @map("image_url") displayOrder Int @default(0) @map("display_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@index([displayOrder, createdAt(sort: Desc)]) @@map("team_members") } // ===================================================== // SESSION MODEL (for express-session) // ===================================================== model Session { sid String @id sess Json @db.JsonB expire DateTime @@index([expire]) @@map("session") }