generated from nhcarrigan/template
6e884b9ae8
Added full API implementation for admin action buttons in the report management interface: Backend Changes: - Created new /api/comments routes for admin-only comment operations - Added PUT /api/comments/:id for admin comment editing - Added DELETE /api/comments/:id for admin comment deletion - Added POST /api/users/:id/make-private for admin profile privacy control - All endpoints protected with adminGuard and csrfProtection - Proper audit logging for all admin actions - Added onDelete: Cascade to CommentReport relation for safe comment deletion Frontend Changes: - Added adminUpdateComment() and adminDeleteComment() to CommentsService - Added makeProfilePrivate() to UserService - Integrated API calls into admin-reports component methods - editComment() now updates comment via API and refreshes report list - deleteComment() now deletes comment via API and refreshes report list - makeProfilePrivate() now updates profile privacy via API - editProfile() navigates using ObjectId instead of Discord username - All actions show success/error toasts and close modals on completion The admin interface now has full working moderation capabilities for both comment and profile reports.
395 lines
10 KiB
Plaintext
395 lines
10 KiB
Plaintext
// This is your Prisma schema file,
|
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
|
|
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "mongodb"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
type Link {
|
|
title String
|
|
url String
|
|
}
|
|
|
|
model Game {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
title String
|
|
platform String?
|
|
status GameStatus
|
|
dateAdded DateTime @default(now())
|
|
dateStarted DateTime?
|
|
dateCompleted DateTime?
|
|
dateFinished DateTime?
|
|
rating Int? @db.Int @default(0)
|
|
notes String?
|
|
coverImage String?
|
|
tags String[]
|
|
links Link[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
comments Comment[]
|
|
}
|
|
|
|
enum GameStatus {
|
|
PLAYING
|
|
COMPLETED
|
|
BACKLOG
|
|
RETIRED
|
|
}
|
|
|
|
model Book {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
title String
|
|
author String
|
|
isbn String?
|
|
status BookStatus
|
|
dateAdded DateTime @default(now())
|
|
dateStarted DateTime?
|
|
dateFinished DateTime?
|
|
rating Int? @db.Int @default(0)
|
|
notes String?
|
|
coverImage String?
|
|
tags String[]
|
|
links Link[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
comments Comment[]
|
|
}
|
|
|
|
enum BookStatus {
|
|
READING
|
|
FINISHED
|
|
TO_READ
|
|
RETIRED
|
|
}
|
|
|
|
model Music {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
title String
|
|
artist String
|
|
type MusicType
|
|
status MusicStatus
|
|
dateAdded DateTime @default(now())
|
|
dateStarted DateTime?
|
|
dateCompleted DateTime?
|
|
dateFinished DateTime?
|
|
rating Int? @db.Int @default(0)
|
|
notes String?
|
|
coverArt String?
|
|
tags String[]
|
|
links Link[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
comments Comment[]
|
|
}
|
|
|
|
enum MusicType {
|
|
ALBUM
|
|
SINGLE
|
|
EP
|
|
}
|
|
|
|
enum MusicStatus {
|
|
LISTENING
|
|
COMPLETED
|
|
WANT_TO_LISTEN
|
|
RETIRED
|
|
}
|
|
|
|
model Art {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
title String
|
|
artist String
|
|
description String?
|
|
imageUrl String
|
|
tags String[]
|
|
links Link[]
|
|
dateAdded DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
comments Comment[]
|
|
}
|
|
|
|
model Show {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
title String
|
|
type ShowType
|
|
status ShowStatus
|
|
dateAdded DateTime @default(now())
|
|
dateStarted DateTime?
|
|
dateCompleted DateTime?
|
|
dateFinished DateTime?
|
|
rating Int? @db.Int @default(0)
|
|
notes String?
|
|
coverImage String?
|
|
tags String[]
|
|
links Link[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
comments Comment[]
|
|
}
|
|
|
|
enum ShowType {
|
|
TV_SERIES
|
|
ANIME
|
|
FILM
|
|
DOCUMENTARY
|
|
}
|
|
|
|
enum ShowStatus {
|
|
WATCHING
|
|
COMPLETED
|
|
WANT_TO_WATCH
|
|
RETIRED
|
|
}
|
|
|
|
model Manga {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
title String
|
|
author String
|
|
status MangaStatus
|
|
dateAdded DateTime @default(now())
|
|
dateStarted DateTime?
|
|
dateCompleted DateTime?
|
|
dateFinished DateTime?
|
|
rating Int? @db.Int @default(0)
|
|
notes String?
|
|
coverImage String?
|
|
tags String[]
|
|
links Link[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
comments Comment[]
|
|
}
|
|
|
|
enum MangaStatus {
|
|
READING
|
|
COMPLETED
|
|
WANT_TO_READ
|
|
RETIRED
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
discordId String @unique
|
|
username String
|
|
email String @unique
|
|
avatar String?
|
|
slug String?
|
|
displayName String?
|
|
bio String?
|
|
profilePublic Boolean @default(true)
|
|
website String?
|
|
discordServer String?
|
|
bluesky String?
|
|
github String?
|
|
linkedin String?
|
|
twitch String?
|
|
youtube String?
|
|
isAdmin Boolean @default(false)
|
|
isBanned Boolean @default(false)
|
|
inDiscord Boolean @default(false)
|
|
isVip Boolean @default(false)
|
|
isMod Boolean @default(false)
|
|
isStaff Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
comments Comment[]
|
|
suggestions Suggestion[]
|
|
likes Like[]
|
|
refreshTokens RefreshToken[]
|
|
reportsMade ProfileReport[] @relation("Reporter")
|
|
reportsReceived ProfileReport[] @relation("ReportedUser")
|
|
reportsReviewed ProfileReport[] @relation("Reviewer")
|
|
commentReportsMade CommentReport[] @relation("CommentReporter")
|
|
commentReportsReviewed CommentReport[] @relation("CommentReviewer")
|
|
|
|
@@index([slug], map: "User_slug_key")
|
|
}
|
|
|
|
model Comment {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
content String
|
|
rawContent String?
|
|
userId String @db.ObjectId
|
|
user User @relation(fields: [userId], references: [id])
|
|
gameId String? @db.ObjectId
|
|
game Game? @relation(fields: [gameId], references: [id])
|
|
bookId String? @db.ObjectId
|
|
book Book? @relation(fields: [bookId], references: [id])
|
|
musicId String? @db.ObjectId
|
|
music Music? @relation(fields: [musicId], references: [id])
|
|
artId String? @db.ObjectId
|
|
art Art? @relation(fields: [artId], references: [id])
|
|
showId String? @db.ObjectId
|
|
show Show? @relation(fields: [showId], references: [id])
|
|
mangaId String? @db.ObjectId
|
|
manga Manga? @relation(fields: [mangaId], references: [id])
|
|
reports CommentReport[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model AuditLog {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
action AuditAction
|
|
category AuditCategory
|
|
userId String? @db.ObjectId
|
|
targetUserId String? @db.ObjectId
|
|
resourceType String?
|
|
resourceId String?
|
|
details String?
|
|
userAgent String?
|
|
success Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
enum AuditAction {
|
|
LOGIN
|
|
LOGOUT
|
|
LOGIN_FAILED
|
|
COMMENT_CREATE
|
|
COMMENT_UPDATE
|
|
COMMENT_DELETE
|
|
ENTRY_CREATE
|
|
ENTRY_UPDATE
|
|
ENTRY_DELETE
|
|
LIKE
|
|
UNLIKE
|
|
USER_BAN
|
|
USER_UNBAN
|
|
RATE_LIMIT_EXCEEDED
|
|
CSRF_VALIDATION_FAILED
|
|
UNAUTHORIZED_ACCESS
|
|
}
|
|
|
|
enum AuditCategory {
|
|
AUTH
|
|
CONTENT
|
|
ADMIN
|
|
SECURITY
|
|
}
|
|
|
|
model Suggestion {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
userId String @db.ObjectId
|
|
user User @relation(fields: [userId], references: [id])
|
|
entityType SuggestionEntity
|
|
status SuggestionStatus @default(UNREVIEWED)
|
|
declineReason String?
|
|
|
|
// Data for the suggested item (stored as JSON)
|
|
title String
|
|
gameData Json?
|
|
bookData Json?
|
|
musicData Json?
|
|
artData Json?
|
|
showData Json?
|
|
mangaData Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
enum SuggestionEntity {
|
|
GAME
|
|
BOOK
|
|
MUSIC
|
|
ART
|
|
SHOW
|
|
MANGA
|
|
}
|
|
|
|
enum SuggestionStatus {
|
|
UNREVIEWED
|
|
ACCEPTED
|
|
DECLINED
|
|
}
|
|
|
|
model Like {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
userId String @db.ObjectId
|
|
user User @relation(fields: [userId], references: [id])
|
|
entityType String // 'book', 'game', 'show', 'manga', 'music', 'art'
|
|
entityId String @db.ObjectId
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([userId, entityType, entityId])
|
|
}
|
|
|
|
model RefreshToken {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
token String @unique
|
|
userId String @db.ObjectId
|
|
user User @relation(fields: [userId], references: [id])
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([expiresAt])
|
|
}
|
|
|
|
enum ReportReason {
|
|
INAPPROPRIATE_CONTENT
|
|
HARASSMENT
|
|
SPAM
|
|
IMPERSONATION
|
|
OFFENSIVE_NAME
|
|
MALICIOUS_LINKS
|
|
OTHER
|
|
}
|
|
|
|
enum ReportStatus {
|
|
PENDING
|
|
REVIEWED
|
|
DISMISSED
|
|
ACTION_TAKEN
|
|
}
|
|
|
|
model ProfileReport {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
reportedUserId String @db.ObjectId
|
|
reportedUser User @relation("ReportedUser", fields: [reportedUserId], references: [id])
|
|
reporterId String @db.ObjectId
|
|
reporter User @relation("Reporter", fields: [reporterId], references: [id])
|
|
reason ReportReason
|
|
details String
|
|
status ReportStatus @default(PENDING)
|
|
reviewedBy String? @db.ObjectId
|
|
reviewer User? @relation("Reviewer", fields: [reviewedBy], references: [id])
|
|
reviewNotes String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([reportedUserId])
|
|
@@index([reporterId])
|
|
@@index([status])
|
|
}
|
|
|
|
model CommentReport {
|
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
reportedCommentId String @db.ObjectId
|
|
reportedComment Comment @relation(fields: [reportedCommentId], references: [id], onDelete: Cascade)
|
|
reporterId String @db.ObjectId
|
|
reporter User @relation("CommentReporter", fields: [reporterId], references: [id])
|
|
reason ReportReason
|
|
details String
|
|
status ReportStatus @default(PENDING)
|
|
reviewedBy String? @db.ObjectId
|
|
reviewer User? @relation("CommentReviewer", fields: [reviewedBy], references: [id])
|
|
reviewNotes String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([reportedCommentId])
|
|
@@index([reporterId])
|
|
@@index([status])
|
|
}
|