feat(db): add tournament_admin role and ownership fields to schema

This commit is contained in:
2026-03-29 19:25:02 -07:00
parent 025b195800
commit 8f1f2bf196
4 changed files with 503 additions and 173 deletions
+207 -173
View File
@@ -1,5 +1,3 @@
// EuchreCamp Database Schema
// Generated from existing Ruby/ROM implementation
generator client {
provider = "prisma-client-js"
@@ -10,192 +8,185 @@ datasource db {
url = env("DATABASE_URL")
}
// Player Model - Tracks individual player information
model Player {
id Int @id @default(autoincrement())
name String @unique
rating Int @default(0)
currentElo Int @default(1000)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relationships
users User[]
eventParticipants EventParticipant[]
teamsAsPlayer1 Team[] @relation("TeamPlayer1")
teamsAsPlayer2 Team[] @relation("TeamPlayer2")
matchesAsP1 Match[] @relation("MatchPlayer1")
matchesAsP2 Match[] @relation("MatchPlayer2")
matchesAsP3 Match[] @relation("MatchPlayer3")
matchesAsP4 Match[] @relation("MatchPlayer4")
id Int @id @default(autoincrement())
name String
rating Int @default(0) // Historical rating (used for rankings)
currentElo Int @default(1000) // Current Elo rating
gamesPlayed Int @default(0) // Total games played
wins Int @default(0) // Total wins
losses Int @default(0) // Total losses
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
eloSnapshots EloSnapshot[]
partnershipGames PartnershipGame[] @relation("PartnershipPlayer1")
partnershipGames2 PartnershipGame[] @relation("PartnershipPlayer2")
partnershipStats PartnershipStat[] @relation("StatPlayer1")
partnershipStats2 PartnershipStat[] @relation("StatPlayer2")
eventParticipants EventParticipant[]
matchesAsP4 Match[] @relation("MatchPlayer4")
matchesAsP3 Match[] @relation("MatchPlayer3")
matchesAsP2 Match[] @relation("MatchPlayer2")
matchesAsP1 Match[] @relation("MatchPlayer1")
partnershipGames2 PartnershipGame[] @relation("PartnershipPlayer2")
partnershipGames PartnershipGame[] @relation("PartnershipPlayer1")
partnershipStats2 PartnershipStat[] @relation("StatPlayer2")
partnershipStats PartnershipStat[] @relation("StatPlayer1")
teamsAsPlayer2 Team[] @relation("TeamPlayer2")
teamsAsPlayer1 Team[] @relation("TeamPlayer1")
user User?
@@map("players")
}
// User Model - Authentication and authorization
model User {
id Int @id @default(autoincrement())
playerId Int @unique
email String @unique
passwordDigest String
role String @default("player")
confirmed Boolean @default(false)
confirmationToken String?
confirmationSentAt DateTime?
resetPasswordToken String?
resetPasswordSentAt DateTime?
failedLoginAttempts Int @default(0)
lockedUntil DateTime?
lastLoginAt DateTime?
lastLoginIp String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
player Player @relation(fields: [playerId], references: [id])
id String @id @default(cuid())
email String @unique
emailVerified Boolean @default(false)
name String?
image String?
role String @default("player") // player, tournament_admin, club_admin
// Session and account references for Better Auth
sessions Session[]
accounts Account[]
// Link to EuchreCamp player (optional)
playerId Int? @unique
player Player? @relation(fields: [playerId], references: [id])
// Tournaments owned by this user
ownedTournaments Event[] @relation("TournamentOwner")
// Matches created by this user
createdMatches Match[] @relation("MatchCreator")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
// Event (Tournament) Model
model Event {
id Int @id @default(autoincrement())
event_id Int?
name String
description String?
eventDate DateTime?
eventType String @default("tournament")
format String @default("round_robin")
status String @default("planned")
id Int @id @default(autoincrement())
event_id Int?
name String
description String?
eventDate DateTime?
eventType String @default("tournament")
format String @default("round_robin")
status String @default("planned")
maxParticipants Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relationships
participants EventParticipant[]
teams Team[]
rounds TournamentRound[]
bracketMatchups BracketMatchup[]
matches Match[]
ownerId String? // User ID of the tournament owner (for tournament_admin)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bracketMatchups BracketMatchup[]
participants EventParticipant[]
matches Match[]
teams Team[]
rounds TournamentRound[]
owner User? @relation("TournamentOwner", fields: [ownerId], references: [id])
@@map("events")
}
// EventParticipant Model
model EventParticipant {
id Int @id @default(autoincrement())
eventId Int
playerId Int
teamId Int?
seed Int?
status String @default("registered")
id Int @id @default(autoincrement())
eventId Int
playerId Int
teamId Int?
seed Int?
status String @default("registered")
registrationDate DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event Event @relation(fields: [eventId], references: [id])
player Player @relation(fields: [playerId], references: [id])
team Team? @relation(fields: [teamId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
team Team? @relation(fields: [teamId], references: [id])
player Player @relation(fields: [playerId], references: [id])
event Event @relation(fields: [eventId], references: [id])
@@map("event_participants")
}
// Team Model
model Team {
id Int @id @default(autoincrement())
eventId Int
teamName String?
player1Id Int
player2Id Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relationships
event Event @relation(fields: [eventId], references: [id])
player1 Player @relation("TeamPlayer1", fields: [player1Id], references: [id])
player2 Player @relation("TeamPlayer2", fields: [player2Id], references: [id])
eventParticipants EventParticipant[]
bracketMatchups1 BracketMatchup[] @relation("BracketTeam1")
bracketMatchups2 BracketMatchup[] @relation("BracketTeam2")
id Int @id @default(autoincrement())
eventId Int
teamName String?
player1Id Int
player2Id Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bracketMatchups2 BracketMatchup[] @relation("BracketTeam2")
bracketMatchups1 BracketMatchup[] @relation("BracketTeam1")
eventParticipants EventParticipant[]
player2 Player @relation("TeamPlayer2", fields: [player2Id], references: [id])
player1 Player @relation("TeamPlayer1", fields: [player1Id], references: [id])
event Event @relation(fields: [eventId], references: [id])
@@map("teams")
}
// TournamentRound Model
model TournamentRound {
id Int @id @default(autoincrement())
eventId Int
roundNumber Int
status String @default("pending")
scheduledStart DateTime?
actualStart DateTime?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event Event @relation(fields: [eventId], references: [id])
id Int @id @default(autoincrement())
eventId Int
roundNumber Int
status String @default("pending")
scheduledStart DateTime?
actualStart DateTime?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bracketMatchups BracketMatchup[]
event Event @relation(fields: [eventId], references: [id])
@@map("tournament_rounds")
}
// BracketMatchup Model
model BracketMatchup {
id Int @id @default(autoincrement())
roundId Int
eventId Int
team1Id Int?
team2Id Int?
matchId Int?
tableNumber Int?
id Int @id @default(autoincrement())
roundId Int
eventId Int
team1Id Int?
team2Id Int?
matchId Int?
tableNumber Int?
bracketPosition Int?
winnerTeamId Int?
loserTeamId Int?
status String @default("pending")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
round TournamentRound @relation(fields: [roundId], references: [id])
event Event @relation(fields: [eventId], references: [id])
team1 Team? @relation("BracketTeam1", fields: [team1Id], references: [id])
team2 Team? @relation("BracketTeam2", fields: [team2Id], references: [id])
match Match? @relation(fields: [matchId], references: [id])
winnerTeamId Int?
loserTeamId Int?
status String @default("pending")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
match Match? @relation(fields: [matchId], references: [id])
team2 Team? @relation("BracketTeam2", fields: [team2Id], references: [id])
team1 Team? @relation("BracketTeam1", fields: [team1Id], references: [id])
event Event @relation(fields: [eventId], references: [id])
round TournamentRound @relation(fields: [roundId], references: [id])
@@map("bracket_matchups")
}
// Match Model
model Match {
id Int @id @default(autoincrement())
eventId Int?
playedAt DateTime?
team1P1Id Int
team1P2Id Int
team2P1Id Int
team2P2Id Int
team1Score Int
team2Score Int
status String @default("completed")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event Event? @relation(fields: [eventId], references: [id])
team1P1 Player @relation("MatchPlayer1", fields: [team1P1Id], references: [id])
team1P2 Player @relation("MatchPlayer2", fields: [team1P2Id], references: [id])
team2P1 Player @relation("MatchPlayer3", fields: [team2P1Id], references: [id])
team2P2 Player @relation("MatchPlayer4", fields: [team2P2Id], references: [id])
eloSnapshots EloSnapshot[]
id Int @id @default(autoincrement())
eventId Int?
playedAt DateTime?
team1P1Id Int
team1P2Id Int
team2P1Id Int
team2P2Id Int
team1Score Int
team2Score Int
status String @default("completed")
createdById String? // User ID of the match creator (for tournament_admin)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bracketMatchups BracketMatchup[]
eloSnapshots EloSnapshot[]
team2P2 Player @relation("MatchPlayer4", fields: [team2P2Id], references: [id])
team2P1 Player @relation("MatchPlayer3", fields: [team2P1Id], references: [id])
team1P2 Player @relation("MatchPlayer2", fields: [team1P2Id], references: [id])
team1P1 Player @relation("MatchPlayer1", fields: [team1P1Id], references: [id])
event Event? @relation(fields: [eventId], references: [id])
partnershipGames PartnershipGame[]
bracketMatchups BracketMatchup[]
createdBy User? @relation("MatchCreator", fields: [createdById], references: [id])
@@map("matches")
}
// EloSnapshot Model
model EloSnapshot {
id Int @id @default(autoincrement())
playerId Int
@@ -204,47 +195,90 @@ model EloSnapshot {
ratingAfter Int
ratingChange Int
createdAt DateTime @default(now())
player Player @relation(fields: [playerId], references: [id])
match Match @relation(fields: [matchId], references: [id])
match Match @relation(fields: [matchId], references: [id])
player Player @relation(fields: [playerId], references: [id])
@@map("elo_snapshots")
}
// PartnershipGame Model - Tracks each game played by a partnership
model PartnershipGame {
id Int @id @default(autoincrement())
player1Id Int
player2Id Int
matchId Int
teamNumber Int?
wonMatch Int?
player1EloChange Int?
player2EloChange Int?
createdAt DateTime @default(now())
player1 Player @relation("PartnershipPlayer1", fields: [player1Id], references: [id])
player2 Player @relation("PartnershipPlayer2", fields: [player2Id], references: [id])
match Match @relation(fields: [matchId], references: [id])
id Int @id @default(autoincrement())
player1Id Int
player2Id Int
matchId Int
teamNumber Int?
wonMatch Int?
player1EloChange Int?
player2EloChange Int?
createdAt DateTime @default(now())
match Match @relation(fields: [matchId], references: [id])
player2 Player @relation("PartnershipPlayer2", fields: [player2Id], references: [id])
player1 Player @relation("PartnershipPlayer1", fields: [player1Id], references: [id])
@@map("partnership_games")
}
// PartnershipStat Model - Aggregated partnership statistics
model PartnershipStat {
id Int @id @default(autoincrement())
player1Id Int
player2Id Int
gamesPlayed Int @default(0)
wins Int @default(0)
losses Int @default(0)
totalEloChange Int @default(0)
lastPlayed DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
player1 Player @relation("StatPlayer1", fields: [player1Id], references: [id])
player2 Player @relation("StatPlayer2", fields: [player2Id], references: [id])
id Int @id @default(autoincrement())
player1Id Int
player2Id Int
gamesPlayed Int @default(0)
wins Int @default(0)
losses Int @default(0)
totalEloChange Int @default(0)
lastPlayed DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
player2 Player @relation("StatPlayer2", fields: [player2Id], references: [id])
player1 Player @relation("StatPlayer1", fields: [player1Id], references: [id])
@@map("partnership_stats")
}
model Session {
id String @id
expiresAt DateTime
token String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ipAddress String?
userAgent String?
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([token])
@@index([userId])
@@map("session")
}
model Account {
id String @id
accountId String
providerId String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
accessToken String?
refreshToken String?
idToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
password String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@map("account")
}
model Verification {
id String @id
identifier String
value String
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([identifier])
@@map("verification")
}