Initial portfolio CMS: Fastify API + Vite SPA
Single-container app with Postgres catalog, Typesense search, Authelia Remote-User admin, portfolio views, resume/PDF, media uploads, and ArtStation import tooling.
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user