ironie-nextjs/prisma/schema.prisma
2025-10-16 17:12:58 +02:00

467 lines
13 KiB
Plaintext

generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
binaryTargets = ["debian-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
//
// ──────────────────────────────────────────────
// 🧑 Benutzer, Teams & Verwaltung
// ──────────────────────────────────────────────
//
model User {
steamId String @id
name String?
avatar String?
location String?
isAdmin Boolean @default(false)
teamId String?
team Team? @relation("UserTeam", fields: [teamId], references: [id])
ledTeam Team? @relation("TeamLeader")
matchesAsTeamA Match[] @relation("TeamAPlayers")
matchesAsTeamB Match[] @relation("TeamBPlayers")
premierRank Int?
authCode String?
lastKnownShareCode String?
lastKnownShareCodeDate DateTime?
createdAt DateTime @default(now())
invites TeamInvite[] @relation("UserInvitations")
notifications Notification[]
matchPlayers MatchPlayer[]
serverRequests ServerRequest[] @relation("MatchRequests")
rankHistory RankHistory[] @relation("UserRankHistory")
demoFiles DemoFile[]
createdSchedules Schedule[] @relation("CreatedSchedules")
confirmedSchedules Schedule[] @relation("ConfirmedSchedules")
mapVoteChoices MapVoteStep[] @relation("VoteStepChooser")
status UserStatus @default(offline) // 👈 neu
lastActiveAt DateTime? // optional: wann zuletzt aktiv
readyAcceptances MatchReady[] @relation("MatchReadyUser")
pterodactylClientApiKey String?
timeZone String? // IANA-TZ, z.B. "Europe/Berlin"
// ✅ Datenschutz: darf eingeladen werden?
canBeInvited Boolean @default(true)
// ⬇️ Dauerhafter Ban-Status (zuletzt bekannter Stand)
vacBanned Boolean? @default(false)
numberOfVACBans Int? @default(0)
numberOfGameBans Int? @default(0)
daysSinceLastBan Int? @default(0)
communityBanned Boolean? @default(false)
economyBan String?
lastBanCheck DateTime?
// FaceIt Account
faceitId String? @unique
faceitNickname String?
faceitAvatar String?
faceitCountry String?
faceitUrl String?
faceitVerified Boolean? @default(false)
faceitSteamId64 String?
faceitGames FaceitGameStat[]
@@index([vacBanned])
@@index([numberOfVACBans])
@@index([numberOfGameBans])
}
enum UserStatus {
online
away
offline
}
enum FaceitGameId {
csgo
cs2
}
model FaceitGameStat {
id String @id @default(cuid())
user User @relation(fields: [userSteamId], references: [steamId], onDelete: Cascade)
userSteamId String
game FaceitGameId
region String? // "EU"
gamePlayerId String? // "76561198000414190"
gamePlayerName String? // "Army"
skillLevel Int? // 4
elo Int? // 1045
skillLabel String?
gameProfileId String?
updatedAt DateTime @updatedAt
@@unique([userSteamId, game])
@@index([game, elo])
}
enum TeamJoinPolicy {
REQUEST
INVITE_ONLY
}
model Team {
id String @id @default(uuid())
name String @unique
logo String?
logoUpdatedAt DateTime? @default(now())
leaderId String? @unique
createdAt DateTime @default(now())
activePlayers String[]
inactivePlayers String[]
leader User? @relation("TeamLeader", fields: [leaderId], references: [steamId])
members User[] @relation("UserTeam")
invites TeamInvite[]
matchPlayers MatchPlayer[]
matchesAsTeamA Match[] @relation("MatchTeamA")
matchesAsTeamB Match[] @relation("MatchTeamB")
schedulesAsTeamA Schedule[] @relation("ScheduleTeamA")
schedulesAsTeamB Schedule[] @relation("ScheduleTeamB")
mapVoteSteps MapVoteStep[] @relation("VoteStepTeam")
// Default bleibt REQUEST
joinPolicy TeamJoinPolicy @default(REQUEST)
}
model TeamInvite {
id String @id @default(uuid())
steamId String
teamId String
type String
createdAt DateTime @default(now())
user User @relation("UserInvitations", fields: [steamId], references: [steamId])
team Team @relation(fields: [teamId], references: [id])
}
model Notification {
id String @id @default(uuid())
steamId String
title String?
message String
read Boolean @default(false)
persistent Boolean @default(false)
actionType String?
actionData String?
createdAt DateTime @default(now())
user User @relation(fields: [steamId], references: [steamId])
}
//
// ──────────────────────────────────────────────
// 🎮 Matches & Spieler
// ──────────────────────────────────────────────
//
// ──────────────────────────────────────────────
// 🎮 Matches
// ──────────────────────────────────────────────
model Match {
id String @id @default(uuid())
title String
matchType String @default("community")
map String?
description String?
scoreA Int?
scoreB Int?
teamAId String?
teamA Team? @relation("MatchTeamA", fields: [teamAId], references: [id])
teamBId String?
teamB Team? @relation("MatchTeamB", fields: [teamBId], references: [id])
teamAUsers User[] @relation("TeamAPlayers")
teamBUsers User[] @relation("TeamBPlayers")
filePath String?
demoFile DemoFile?
demoDate DateTime?
demoData Json?
players MatchPlayer[]
rankUpdates RankHistory[] @relation("MatchRankHistory")
roundCount Int?
roundHistory Json?
winnerTeam String?
matchDate DateTime? // geplante Startzeit (separat von demoDate)
mapVote MapVote?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
schedule Schedule?
readyAcceptances MatchReady[] @relation("MatchReadyMatch")
cs2MatchId BigInt? @unique // <— wichtig (Postgres lässt mehrere NULLs zu)
exportedAt DateTime? // wann die JSON exportiert wurde
}
model MatchPlayer {
id String @id @default(uuid())
steamId String
matchId String
teamId String?
team Team? @relation(fields: [teamId], references: [id])
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
user User @relation(fields: [steamId], references: [steamId])
stats PlayerStats?
createdAt DateTime @default(now())
@@unique([matchId, steamId])
}
model PlayerStats {
id String @id @default(uuid())
matchId String
steamId String
kills Int
assists Int
deaths Int
headshotPct Float
totalDamage Float @default(0)
utilityDamage Int @default(0)
flashAssists Int @default(0)
mvps Int @default(0)
mvpEliminations Int @default(0)
mvpDefuse Int @default(0)
mvpPlant Int @default(0)
knifeKills Int @default(0)
zeusKills Int @default(0)
wallbangKills Int @default(0)
smokeKills Int @default(0)
headshots Int @default(0)
noScopes Int @default(0)
blindKills Int @default(0)
aim Int @default(0)
oneK Int @default(0)
twoK Int @default(0)
threeK Int @default(0)
fourK Int @default(0)
fiveK Int @default(0)
rankOld Int?
rankNew Int?
rankChange Int?
winCount Int?
matchPlayer MatchPlayer @relation(fields: [matchId, steamId], references: [matchId, steamId])
@@unique([matchId, steamId])
}
model RankHistory {
id String @id @default(uuid())
steamId String
matchId String?
rankOld Int
rankNew Int
delta Int
winCount Int
createdAt DateTime @default(now())
user User @relation("UserRankHistory", fields: [steamId], references: [steamId])
match Match? @relation("MatchRankHistory", fields: [matchId], references: [id], onDelete: Cascade)
}
model Schedule {
id String @id @default(uuid())
title String
description String?
map String?
date DateTime
status ScheduleStatus @default(PENDING)
teamAId String?
teamA Team? @relation("ScheduleTeamA", fields: [teamAId], references: [id])
teamBId String?
teamB Team? @relation("ScheduleTeamB", fields: [teamBId], references: [id])
createdById String
createdBy User @relation("CreatedSchedules", fields: [createdById], references: [steamId])
confirmedById String?
confirmedBy User? @relation("ConfirmedSchedules", fields: [confirmedById], references: [steamId])
linkedMatchId String? @unique
linkedMatch Match? @relation(fields: [linkedMatchId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum ScheduleStatus {
PENDING
CONFIRMED
DECLINED
CANCELLED
COMPLETED
}
//
// ──────────────────────────────────────────────
// 📦 Demo-Dateien & CS2 Requests
// ──────────────────────────────────────────────
//
model DemoFile {
id String @id @default(uuid())
matchId String @unique
steamId String
fileName String @unique
filePath String
parsed Boolean @default(false)
createdAt DateTime @default(now())
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
user User @relation(fields: [steamId], references: [steamId])
}
model ServerRequest {
id String @id @default(uuid())
steamId String
matchId String
reservationId BigInt
tvPort BigInt
processed Boolean @default(false)
failed Boolean @default(false)
createdAt DateTime @default(now())
user User @relation("MatchRequests", fields: [steamId], references: [steamId])
@@unique([steamId, matchId])
}
// ──────────────────────────────────────────────
// 🗺️ Map-Vote
// ──────────────────────────────────────────────
enum MapVoteAction {
BAN
PICK
DECIDER
}
model MapVote {
id String @id @default(uuid())
matchId String @unique
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
bestOf Int @default(3)
mapPool String[]
currentIdx Int @default(0)
locked Boolean @default(false)
opensAt DateTime?
leadMinutes Int @default(60)
adminEditingBy String?
adminEditingSince DateTime?
steps MapVoteStep[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model MapVoteStep {
id String @id @default(uuid())
voteId String
order Int
action MapVoteAction
teamId String?
team Team? @relation("VoteStepTeam", fields: [teamId], references: [id])
map String?
chosenAt DateTime?
chosenBy String?
chooser User? @relation("VoteStepChooser", fields: [chosenBy], references: [steamId])
vote MapVote @relation(fields: [voteId], references: [id], onDelete: Cascade)
@@unique([voteId, order])
@@index([teamId])
@@index([chosenBy])
}
model MatchReady {
matchId String
steamId String
acceptedAt DateTime @default(now())
match Match @relation("MatchReadyMatch", fields: [matchId], references: [id], onDelete: Cascade)
user User @relation("MatchReadyUser", fields: [steamId], references: [steamId])
@@id([matchId, steamId])
@@index([steamId])
}
// ──────────────────────────────────────────────
// 🛠️ Server-Konfiguration & Pterodactyl
// ──────────────────────────────────────────────
model ServerConfig {
id String @id
serverIp String
serverPassword String?
pterodactylServerId String
pterodactylServerApiKey String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// ───── Live / GameBanner ─────
activeMatchId String?
activeMapKey String?
activeMapLabel String?
activeMapBg String?
activeParticipants String[] // steamIds
activeSince DateTime?
bannerExpiresAt DateTime?
@@index([activeMatchId])
}