465 lines
13 KiB
Plaintext
465 lines
13 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
output = "../src/generated/prisma"
|
|
}
|
|
|
|
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])
|
|
}
|
|
|