Files
gruperly/apps/api/prisma/schema.prisma
2026-09-07 09:33:07 -03:00

257 lines
6.3 KiB
Plaintext

// Gruperly - Prisma Schema (PostgreSQL)
generator client {
provider = "prisma-client"
output = "./generated/prisma"
}
datasource db {
provider = "postgresql"
}
// Auth (Better Auth) - required tables
model User {
id String @id @default(cuid())
name String
email String @unique
emailVerified Boolean @default(false)
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
sessions Session[]
passkeys Passkey[]
groupsMember GroupMember[]
groupsOwner Group[] @relation("OwnerGroups")
orgMemberships Member[]
orgInvitations Invitation[] @relation("InvitedBy")
waitlist WaitlistEntry[]
@@map("users")
}
model Account {
id String @id @default(cuid())
userId String
providerId String
accountId String
issuer String
accessToken String?
refreshToken String?
idToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
password String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([issuer, accountId])
@@index([userId])
@@map("accounts")
}
model Session {
id String @id @default(cuid())
userId String
token String @unique
expiresAt DateTime
ipAddress String?
userAgent String?
activeOrganizationId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("sessions")
}
model Verification {
id String @id @default(cuid())
identifier String
value String
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([identifier, value])
@@map("verifications")
}
// Better Auth - Organization plugin
model Organization {
id String @id @default(cuid())
name String
slug String @unique
logo String?
metadata Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
members Member[]
invitations Invitation[]
@@map("organizations")
}
model Member {
id String @id @default(cuid())
organizationId String
userId String
role String @default("member")
createdAt DateTime @default(now())
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([organizationId, userId])
@@map("members")
}
model Invitation {
id String @id @default(cuid())
organizationId String
email String
role String
status String @default("pending")
inviterId String
expiresAt DateTime
createdAt DateTime @default(now())
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
inviter User @relation("InvitedBy", fields: [inviterId], references: [id], onDelete: Cascade)
@@index([organizationId])
@@map("invitations")
}
// Better Auth - Passkey plugin
model Passkey {
id String @id @default(cuid())
name String?
publicKey String
credentialID String @unique
userId String
counter Int
deviceType String
backedUp Boolean
transports String?
createdAt DateTime @default(now())
aaguid String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("passkeys")
}
// Domain models
model Group {
id String @id @default(cuid())
name String
description String?
createdById String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
owner User @relation("OwnerGroups", fields: [createdById], references: [id], onDelete: Cascade)
members GroupMember[]
students Student[]
payments Payment[]
@@map("groups")
}
model GroupMember {
id String @id @default(cuid())
groupId String
userId String
role Role @default(MEMBER) // OWNER, ADMIN, MEMBER
joinedAt DateTime @default(now())
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([groupId, userId])
@@map("group_members")
}
enum Role {
OWNER
ADMIN
MEMBER
}
model Student {
id String @id @default(cuid())
groupId String
fullName String
email String?
phone String?
guardianName String?
guardianPhone String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
payments Payment[]
@@index([groupId])
@@map("students")
}
model Payment {
id String @id @default(cuid())
groupId String
studentId String
amount Decimal @db.Decimal(10, 2)
currency String @default("MXN")
status PaymentStatus @default(PENDING) // PENDING, PAID, OVERDUE, CANCELLED
dueDate DateTime
paidAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
student Student @relation(fields: [studentId], references: [id], onDelete: Cascade)
@@index([groupId])
@@index([studentId])
@@map("payments")
}
enum PaymentStatus {
PENDING
PAID
OVERDUE
CANCELLED
}
model WaitlistEntry {
id String @id @default(cuid())
email String
name String?
status WaitlistStatus @default(PENDING) // PENDING, INVITED, JOINED, DECLINED
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String? @unique
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
@@unique([email])
@@map("waitlist_entries")
}
enum WaitlistStatus {
PENDING
INVITED
JOINED
DECLINED
}