Single-container app with Postgres catalog, Typesense search, Authelia Remote-User admin, portfolio views, resume/PDF, media uploads, and ArtStation import tooling.
193 lines
5.6 KiB
Plaintext
193 lines
5.6 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum Visibility {
|
|
published
|
|
draft
|
|
private
|
|
}
|
|
|
|
enum MediaType {
|
|
image
|
|
video
|
|
external
|
|
}
|
|
|
|
enum VideoSource {
|
|
youtube
|
|
vimeo
|
|
self_hosted
|
|
external
|
|
}
|
|
|
|
model Category {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
slug String @unique
|
|
description String?
|
|
sortOrder Int @default(0) @map("sort_order")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
projects ProjectCategory[]
|
|
viewPriorities PortfolioViewCategory[]
|
|
|
|
@@map("categories")
|
|
}
|
|
|
|
model Tag {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
slug String @unique
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
projects ProjectTag[]
|
|
|
|
@@map("tags")
|
|
}
|
|
|
|
model Project {
|
|
id String @id @default(cuid())
|
|
title String
|
|
slug String @unique
|
|
description String @default("")
|
|
shortDescription String? @map("short_description")
|
|
date DateTime? @db.Date
|
|
software String[] @default([])
|
|
externalLinks Json @default("[]") @map("external_links")
|
|
featured Boolean @default(false)
|
|
displayPriority Int @default(0) @map("display_priority")
|
|
visibility Visibility @default(draft)
|
|
thumbnailId String? @map("thumbnail_id")
|
|
sourceUrl String? @map("source_url")
|
|
sourcePlatform String? @map("source_platform")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
publishedAt DateTime? @map("published_at")
|
|
|
|
thumbnail Media? @relation("ProjectThumbnail", fields: [thumbnailId], references: [id], onDelete: SetNull)
|
|
media Media[] @relation("ProjectMedia")
|
|
categories ProjectCategory[]
|
|
tags ProjectTag[]
|
|
|
|
@@index([visibility, displayPriority])
|
|
@@index([featured])
|
|
@@index([date])
|
|
@@map("projects")
|
|
}
|
|
|
|
model ProjectCategory {
|
|
projectId String @map("project_id")
|
|
categoryId String @map("category_id")
|
|
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([projectId, categoryId])
|
|
@@map("project_categories")
|
|
}
|
|
|
|
model ProjectTag {
|
|
projectId String @map("project_id")
|
|
tagId String @map("tag_id")
|
|
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([projectId, tagId])
|
|
@@map("project_tags")
|
|
}
|
|
|
|
model Media {
|
|
id String @id @default(cuid())
|
|
projectId String? @map("project_id")
|
|
type MediaType @default(image)
|
|
url String
|
|
thumbnailUrl String? @map("thumbnail_url")
|
|
filename String?
|
|
mimeType String? @map("mime_type")
|
|
width Int?
|
|
height Int?
|
|
sizeBytes Int? @map("size_bytes")
|
|
alt String?
|
|
caption String?
|
|
sortOrder Int @default(0) @map("sort_order")
|
|
videoSource VideoSource? @map("video_source")
|
|
videoId String? @map("video_id")
|
|
externalUrl String? @map("external_url")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
project Project? @relation("ProjectMedia", fields: [projectId], references: [id], onDelete: Cascade)
|
|
thumbnailFor Project[] @relation("ProjectThumbnail")
|
|
|
|
@@index([projectId, sortOrder])
|
|
@@map("media")
|
|
}
|
|
|
|
/// Named portfolio landing experiences e.g. game-dev, engineering
|
|
model PortfolioView {
|
|
id String @id @default(cuid())
|
|
name String
|
|
slug String @unique
|
|
description String?
|
|
/// Default sort when no category match: priority | date | title
|
|
defaultSort String @default("priority") @map("default_sort")
|
|
/// Show projects outside configured categories after prioritized ones
|
|
showOthers Boolean @default(true) @map("show_others")
|
|
isActive Boolean @default(true) @map("is_active")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
categoryPriorities PortfolioViewCategory[]
|
|
|
|
@@map("portfolio_views")
|
|
}
|
|
|
|
model PortfolioViewCategory {
|
|
viewId String @map("view_id")
|
|
categoryId String @map("category_id")
|
|
priority Int @default(0)
|
|
|
|
view PortfolioView @relation(fields: [viewId], references: [id], onDelete: Cascade)
|
|
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([viewId, categoryId])
|
|
@@map("portfolio_view_categories")
|
|
}
|
|
|
|
model Resume {
|
|
id String @id @default(cuid())
|
|
/// Single active resume — only one should be isActive=true
|
|
isActive Boolean @default(true) @map("is_active")
|
|
fullName String @map("full_name")
|
|
title String @default("")
|
|
email String?
|
|
phone String?
|
|
location String?
|
|
website String?
|
|
summary String @default("")
|
|
/// Structured sections as JSON: experience, education, skills, etc.
|
|
sections Json @default("[]")
|
|
/// Optional raw HTML override for public render
|
|
htmlContent String? @map("html_content") @db.Text
|
|
theme String @default("dark")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@map("resumes")
|
|
}
|
|
|
|
model SiteSetting {
|
|
key String @id
|
|
value Json
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@map("site_settings")
|
|
}
|