This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 49 kB View raw
1generator goprisma { 2 provider = "go run github.com/steebchen/prisma-client-go" 3 previewFeatures = ["fullTextSearchPostgres", "postgresqlExtensions"] 4} 5 6// See https://pothos-graphql.dev/docs/plugins/prisma#setup 7 8datasource db { 9 provider = "postgresql" 10 url = env("DATABASE_URL") 11 extensions = [fuzzystrmatch, pgcrypto, unaccent, pg_trgm] 12} 13 14/// Users are the people who use the app 15model User { 16 id String @id @default(dbgenerated("nanoid('u:'::text)")) 17 uid String @unique @db.VarChar(255) 18 createdAt DateTime @default(now()) 19 lastSeenAt DateTime @default(now()) 20 21 // School details 22 schoolServer String? @db.VarChar(255) 23 schoolUid String? @db.VarChar(255) 24 // email of the user from the school's LDAP 25 schoolEmail String? @db.VarChar(255) 26 27 email String @unique @db.VarChar(255) 28 otherEmails String[] @db.VarChar(255) 29 firstName String @db.VarChar(255) 30 lastName String @db.VarChar(255) 31 majorId String? 32 major Major? @relation(fields: [majorId], references: [id], onUpdate: Cascade, onDelete: Restrict) 33 minor Minor? @relation(fields: [minorId], references: [id]) 34 minorId String? 35 graduationYear Int 36 apprentice Boolean @default(false) 37 address String @default("") @db.VarChar(255) 38 birthday DateTime? 39 description String @default("") @db.Text 40 nickname String @default("") @db.VarChar(255) 41 pronouns String @default("") @db.VarChar(255) 42 phone String @default("") @db.VarChar(255) 43 pictureFile String @default("") @db.VarChar(255) 44 links Link[] 45 godparentId String? 46 cededImageRightsToTVn7 Boolean @default(false) 47 enabledNotificationChannels NotificationChannel[] @default([Articles, Shotguns, Permissions, GroupBoard, GodparentRequests, Other]) 48 latestVersionSeenInChangelog String @default("0.0.0") 49 bot Boolean @default(false) 50 // Not shown on profile, used to autofill Lydia payment request forms 51 lydiaPhone String @default("") @db.VarChar(255) 52 // Prevent these themes from being auto-deployed to the user 53 blockedThemes Theme[] 54 55 // Permissions 56 admin Boolean @default(false) 57 canEditGroups StudentAssociation[] @relation("studentAssociationGroupsEditor") 58 canAccessDocuments Boolean @default(false) 59 60 // Relationships 61 articles Article[] 62 groups GroupMember[] 63 credentials Credential[] 64 Reservation Registration[] @relation("author") 65 managedEvents EventManager[] 66 logs LogEntry[] 67 events Event[] 68 notificationSubscriptions NotificationSubscription[] 69 notifications Notification[] 70 godparent User? @relation("mentorship", fields: [godparentId], references: [id], onDelete: SetNull) // Le parrain ou la marraine 71 godchildren User[] @relation("mentorship") // Les filleul(e)s 72 incomingGodparentRequests GodparentRequest[] @relation("godparent") 73 outgoingGodparentRequests GodparentRequest[] @relation("godchild") 74 passwordResets PasswordReset[] 75 emailChanges EmailChange[] 76 Announcement Announcement[] 77 contributions Contribution[] 78 verifications Registration[] @relation("verifiedBy") 79 oppositions Registration[] @relation("opposedBy") 80 cancellations Registration[] @relation("cancelledBy") 81 receivedBookings Registration[] @relation("beneficiaryOf") 82 documents Document[] 83 bannedFromEvents Event[] @relation("bannedFromEvents") 84 reactions Reaction[] 85 formAnswers Answer[] 86 createdForms Form[] 87 completedForms Form[] @relation("completedForms") 88 partiallyCompletedForms Form[] @relation("partiallyCompletedForms") 89 adminOfStudentAssociations StudentAssociation[] @relation("studentAssociationAdmins") 90 bookmarks Bookmark[] 91 sharedPosts Article[] @relation("articleShares") 92 sharedEvents Event[] @relation("eventShares") 93 seenBookings Registration[] @relation("seenBy") 94 eventManagerInvites EventManagerInvite[] @relation("eventManagerInviteUses") 95 invitedToTickets Ticket[] @relation("invitedTo") 96 97 // For full-text search 98 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 99 claimedPromotions PromotionCode[] 100 formsWithMarkedCheckboxes Form[] @relation("formsWithMarkedCheckbox") 101 createdPages Page[] 102 pointOfContactFor Registration[] @relation("pointOfContactFor") 103 104 @@unique([schoolServer, schoolUid]) 105 @@index([search], type: Gin) 106} 107 108/// A bookmarked page, used to make personal quick access links 109model Bookmark { 110 id String @id @default(dbgenerated("nanoid('bookmark:'::text)")) 111 createdAt DateTime @default(now()) 112 updatedAt DateTime @updatedAt 113 user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) 114 userId String 115 // The path to the bookmarked page. Up to the frontend's interpretation 116 path String 117 118 @@unique([userId, path]) 119} 120 121/// Requests to become someone's godchild. Gets deleted once the request has been accepted (or denied). godchild is the requester, godparent is the requested. 122model GodparentRequest { 123 id String @id @default(dbgenerated("nanoid('godparentreq:'::text)")) 124 createdAt DateTime @default(now()) 125 updatedAt DateTime @updatedAt 126 127 godchild User @relation("godchild", fields: [godchildId], references: [id], onDelete: Cascade) 128 godchildId String 129 godparent User @relation("godparent", fields: [godparentId], references: [id], onDelete: Cascade) 130 godparentId String 131 132 @@unique([godchildId, godparentId]) 133} 134 135/// UserCandidates are users in the registration process 136model UserCandidate { 137 id String @id @default(dbgenerated("nanoid('candidate:'::text)")) 138 token String @unique 139 createdAt DateTime @default(now()) 140 141 email String @unique @db.VarChar(255) 142 emailValidated Boolean @default(false) 143 uid String @default("") @db.VarChar(255) 144 firstName String @default("") @db.VarChar(255) 145 lastName String @default("") @db.VarChar(255) 146 147 churrosPassword String @db.VarChar(255) 148 ldapPassword String @db.VarChar(255) 149 150 majorId String? 151 major Major? @relation(fields: [majorId], references: [id], onUpdate: Cascade, onDelete: Restrict) 152 graduationYear Int? 153 apprentice Boolean @default(false) 154 155 birthday DateTime? 156 cededImageRightsToTVn7 Boolean @default(false) 157 158 usingQuickSignup QuickSignup? @relation(fields: [quickSignupId], references: [id]) 159 quickSignupId String? 160} 161 162/// A password reset token 163model PasswordReset { 164 id String @id @default(dbgenerated("nanoid('passreset:'::text)")) 165 createdAt DateTime @default(now()) 166 updatedAt DateTime @updatedAt 167 user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) 168 userId String 169 expiresAt DateTime? 170} 171 172/// A email validation request 173model EmailChange { 174 id String @id @default(dbgenerated("nanoid('emailchange:'::text)")) 175 createdAt DateTime @default(now()) 176 updatedAt DateTime @updatedAt 177 email String 178 expiresAt DateTime? 179 user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) 180 userId String 181 pending Boolean @default(true) 182 // token is not exposed to the API, whereas id has to be 183 token String @unique @default(dbgenerated("nanoid(''::text, 30)")) 184} 185 186model QuickSignup { 187 id String @id @default(dbgenerated("nanoid('quicksignup:'::text, 6)")) 188 createdAt DateTime @default(now()) 189 updatedAt DateTime @updatedAt 190 validUntil DateTime 191 school School @relation(fields: [schoolId], references: [id], onDelete: Cascade, onUpdate: Cascade) 192 schoolId String 193 candidates UserCandidate[] 194} 195 196// Enum for the different kinds of logos 197enum LogoSourceType { 198 InternalLink 199 ExternalLink 200 GroupLogo 201 Icon 202} 203 204/// A service 205model Service { 206 id String @id @default(dbgenerated("nanoid('service:'::text)")) 207 name String @db.VarChar(255) 208 url String @default("") @db.VarChar(255) 209 description String @default("") @db.VarChar(255) 210 logo String @db.VarChar(255) 211 logoSourceType LogoSourceType 212 schoolId String? 213 school School? @relation(fields: [schoolId], references: [id], onUpdate: Cascade, onDelete: SetNull) 214 studentAssociationId String? 215 studentAssociation StudentAssociation? @relation(fields: [studentAssociationId], references: [id], onUpdate: Cascade, onDelete: SetNull) 216 groupId String? 217 group Group? @relation(fields: [groupId], references: [id], onUpdate: Cascade, onDelete: SetNull) 218 importance Int @default(0) 219 hidden Boolean @default(false) 220 221 @@unique([schoolId, studentAssociationId, groupId, url]) 222 @@unique([schoolId, studentAssociationId, groupId, name]) 223} 224 225/// A single external link 226model Link { 227 id String @id @default(dbgenerated("nanoid('link:'::text)")) 228 name String @db.VarChar(255) 229 value String @db.VarChar(255) 230 createdAt DateTime @default(now()) 231 232 // All resources that can have links 233 User User? @relation(fields: [userId], references: [id], onDelete: SetNull) 234 userId String? 235 StudentAssociation StudentAssociation? @relation(fields: [studentAssociationId], references: [id]) 236 studentAssociationId String? 237 Group Group? @relation(fields: [groupId], references: [id]) 238 groupId String? 239 Article Article? @relation(fields: [articleId], references: [id]) 240 articleId String? 241 Event Event? @relation(fields: [eventId], references: [id]) 242 eventId String? 243 Ticket Ticket? @relation(fields: [ticketId], references: [id]) 244 ticketId String? 245 Notification Notification? @relation(fields: [notificationId], references: [id]) 246 notificationId String? 247 Subject Subject? @relation(fields: [subjectId], references: [id]) 248 subjectId String? 249 250 @@unique([name, userId, studentAssociationId, groupId, articleId, eventId, ticketId, notificationId, subjectId]) 251 @@unique([userId, value]) 252 @@unique([studentAssociationId, value]) 253 @@unique([groupId, value]) 254 @@unique([articleId, value]) 255 @@unique([eventId, value]) 256 @@unique([ticketId, value]) 257 @@unique([notificationId, value]) 258 @@unique([subjectId, value]) 259} 260 261/// A school syllabus 262model Major { 263 id String @id @default(dbgenerated("nanoid('major:'::text)")) 264 uid String @unique @db.VarChar(255) 265 name String @db.VarChar(255) 266 shortName String @default("") @db.VarChar(255) 267 ldapSchoolUid String? @db.VarChar(255) 268 pictureFile String @default("") @db.VarChar(255) 269 createdAt DateTime @default(now()) 270 updatedAt DateTime @default(now()) @updatedAt 271 discontinued Boolean @default(false) 272 273 schools School[] @relation("MajorToSchool") 274 ldapSchool School? @relation("ldapSchool", fields: [ldapSchoolUid], references: [uid], onUpdate: Cascade, onDelete: SetNull) 275 students User[] 276 userCandidates UserCandidate[] 277 accessibleTickets Ticket[] 278 subjects Subject[] 279 minors Minor[] 280} 281 282model Minor { 283 id String @id @default(dbgenerated("nanoid('minor:'::text)")) 284 name String 285 shortName String @default("") 286 slug String 287 majors Major[] 288 yearTier Int 289 subjects Subject[] 290 users User[] 291 292 @@unique([slug, yearTier]) 293} 294 295model School { 296 id String @id @default(dbgenerated("nanoid('school:'::text)")) 297 uid String @unique @db.VarChar(255) 298 name String @db.VarChar(255) 299 color String @db.VarChar(7) 300 studentMailDomain String @default("") @db.VarChar(255) 301 aliasMailDomains String[] @default([]) 302 description String @default("") @db.Text 303 address String @default("") @db.VarChar(255) 304 pictureFile String @default("") @db.VarChar(255) 305 306 majors Major[] @relation("MajorToSchool") 307 majorsLdapSchool Major[] @relation("ldapSchool") 308 studentAssociations StudentAssociation[] 309 accessibleTickets Ticket[] 310 contributionOptions ContributionOption[] 311 services Service[] 312 quickSignups QuickSignup[] 313} 314 315enum CredentialType { 316 Password 317 Token 318 GroupAccessToken 319 Google // External token, used for google sheets integration (and possibly more in the future) 320} 321 322/// A credential is a way to authenticate a user 323model Credential { 324 id String @id @default(dbgenerated("nanoid('credential:'::text)")) 325 userId String? 326 groupId String? 327 name String @default("") @db.VarChar(255) 328 type CredentialType 329 value String @db.VarChar(255) 330 userAgent String @default("") @db.VarChar(255) 331 createdAt DateTime @default(now()) 332 expiresAt DateTime? 333 refresh String? @db.VarChar(255) 334 335 user User? @relation(fields: [userId], references: [id], onUpdate: Cascade, onDelete: Cascade) 336 group Group? @relation(fields: [groupId], references: [id], onUpdate: Cascade, onDelete: Cascade) 337} 338 339/// There is one student association per school 340model StudentAssociation { 341 id String @id @default(dbgenerated("nanoid('ae:'::text)")) 342 uid String @unique @db.VarChar(255) 343 description String @default("") @db.Text 344 schoolId String 345 name String @unique @db.VarChar(255) 346 links Link[] 347 pictureFile String @default("") @db.VarChar(255) 348 heroBackgroundFile String @default("") @db.VarChar(255) /// Used for the student association's homepage 349 allPrezMailingList String @default("") @db.VarChar(255) 350 allTrezMailingList String @default("") @db.VarChar(255) 351 allBoardMailingList String @default("") @db.VarChar(255) 352 internalMailDomain String @default("") @db.VarChar(255) 353 354 createdAt DateTime @default(now()) 355 updatedAt DateTime @updatedAt 356 357 school School @relation(fields: [schoolId], references: [id], onUpdate: Cascade, onDelete: Restrict) 358 groups Group[] 359 board Group? @relation("studentAssociationBoard", fields: [boardId], references: [id], onDelete: SetNull, onUpdate: Cascade) 360 boardId String? @unique 361 lydiaAccounts LydiaAccount[] 362 contributionOptions ContributionOption[] 363 services Service[] 364 admins User[] @relation("studentAssociationAdmins") 365 groupsEditors User[] @relation("studentAssociationGroupsEditor") 366 pages Page[] 367} 368 369model Contribution { 370 id String @id @default(dbgenerated("nanoid('contribution:'::text)")) 371 option ContributionOption @relation(fields: [optionId], references: [id], onDelete: Cascade, onUpdate: Cascade) 372 optionId String 373 transaction LydiaTransaction? 374 transactionId String? 375 user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) 376 paid Boolean @default(false) 377 userId String 378 379 @@unique([optionId, userId]) 380} 381 382model ContributionOption { 383 id String @id @default(dbgenerated("nanoid('contributionoption:'::text)")) 384 offeredIn School @relation(fields: [offeredInId], references: [id], onDelete: Cascade, onUpdate: Cascade) 385 offeredInId String 386 paysFor StudentAssociation[] 387 beneficiary LydiaAccount? @relation(fields: [lydiaAccountId], references: [id], onDelete: SetNull, onUpdate: Cascade) 388 lydiaAccountId String? 389 name String 390 price Float 391 description String @default("") @db.Text 392 contributions Contribution[] 393} 394 395/// The different kinds of groups 396enum GroupType { 397 StudentAssociationSection 398 Association 399 Club 400 List 401 Integration 402 Group 403} 404 405/// A group is a collection of users 406model Group { 407 id String @id @default(dbgenerated("nanoid('g:'::text)")) 408 uid String @unique @db.VarChar(255) 409 parentId String? 410 /// Helper field to get a whole tree without processing all groups 411 /// To be set to the group's id itself for root groups. 412 familyId String? 413 studentAssociationId String 414 pictureFile String @default("") @db.VarChar(255) 415 pictureFileDark String @default("") @db.VarChar(255) 416 name String @db.VarChar(255) 417 type GroupType 418 color String @db.VarChar(7) 419 selfJoinable Boolean @default(false) 420 roomIsOpen Boolean @default(false) 421 createdAt DateTime @default(now()) 422 updatedAt DateTime @default(now()) @updatedAt 423 unlisted Boolean @default(false) 424 425 address String @default("") @db.VarChar(255) 426 description String @default("") @db.VarChar(255) 427 email String @default("") @db.VarChar(255) 428 mailingList String @default("") @db.VarChar(255) 429 longDescription String @default("") 430 website String @default("") @db.VarChar(255) 431 ldapUid String @default("") @db.VarChar(255) 432 links Link[] 433 pages Page[] 434 435 /// Parent group, from which this group inherits its permissions 436 parent Group? @relation("parent", fields: [parentId], references: [id], onUpdate: Cascade, onDelete: Restrict) 437 children Group[] @relation("parent") 438 439 /// Family root, only created for performance reasons 440 familyRoot Group? @relation("root", fields: [familyId], references: [id], onUpdate: Cascade, onDelete: Restrict) 441 familyChildren Group[] @relation("root") 442 443 /// Related clubs 444 related Group[] @relation("related") 445 relatedTo Group[] @relation("related") 446 447 articles Article[] 448 members GroupMember[] 449 studentAssociation StudentAssociation @relation(fields: [studentAssociationId], references: [id], onUpdate: Cascade, onDelete: Restrict) 450 events Event[] 451 coOrganizedEvents Event[] @relation("coOrganizer") 452 lyiaAccounts LydiaAccount[] 453 tickets Ticket[] @relation("openTo") 454 services Service[] 455 restrictedFormSections FormSection[] @relation("restrictedTo") 456 457 notifications Notification[] 458 groupId String? 459 ticketGroupId String? 460 joinedByBookingTickets Ticket[] @relation("autojoin") 461 themes Theme[] 462 defaultApplicableOffers Promotion[] @relation("defaultApplicableOffers") 463 464 // For ldap 465 ldapGidNumber Int @unique @default(autoincrement()) 466 467 // For full-text search 468 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 469 forms Form[] 470 boardOf StudentAssociation? @relation("studentAssociationBoard") 471 boardOfId String? 472 tokens Credential[] 473 474 @@index([search], type: Gin) 475} 476 477/// The intermediate model between users 478model GroupMember { 479 groupId String 480 memberId String 481 title String @default("") @db.VarChar(255) 482 president Boolean @default(false) 483 treasurer Boolean @default(false) 484 vicePresident Boolean @default(false) 485 secretary Boolean @default(false) 486 canEditMembers Boolean @default(false) 487 canEditArticles Boolean @default(false) 488 canScanEvents Boolean @default(false) 489 isDeveloper Boolean @default(false) 490 createdAt DateTime @default(now()) 491 492 group Group @relation(fields: [groupId], references: [id], onUpdate: Cascade, onDelete: Cascade) 493 member User @relation(fields: [memberId], references: [id], onUpdate: Cascade, onDelete: Cascade) 494 495 @@id([groupId, memberId]) 496} 497 498/// An article is a post in a group 499model Article { 500 id String @id @default(dbgenerated("nanoid('a:'::text)")) 501 authorId String? 502 groupId String 503 slug String @db.VarChar(255) // TODO remove 504 title String @db.VarChar(255) 505 body String @db.Text 506 published Boolean @default(false) 507 visibility Visibility @default(Private) 508 createdAt DateTime @default(now()) 509 notifiedAt DateTime? @default(now()) // to prevent old notifications before this was added. Another migration will remove this default value. 510 publishedAt DateTime @default(now()) 511 pictureFile String @default("") @db.VarChar(255) 512 links Link[] 513 514 author User? @relation(fields: [authorId], references: [id], onUpdate: Cascade, onDelete: SetNull) 515 group Group @relation(fields: [groupId], references: [id], onUpdate: Cascade, onDelete: Cascade) 516 event Event? @relation(fields: [eventId], references: [id]) 517 eventId String? 518 reactions Reaction[] 519 sharedBy User[] @relation("articleShares") 520 521 // For full-text search 522 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 523 524 @@index([search], type: Gin) 525} 526 527enum EventFrequency { 528 Once 529 Weekly 530 Monthly 531 Biweekly 532} 533 534/// An event is a date, time and place, as well as an optional ticket 535model Event { 536 id String @id @default(dbgenerated("nanoid('e:'::text)")) 537 createdAt DateTime @default(now()) 538 updatedAt DateTime @default(now()) @updatedAt 539 authorId String? 540 groupId String 541 contactMail String 542 beneficiary LydiaAccount? @relation(fields: [lydiaAccountId], references: [id]) 543 description String @db.Text 544 slug String @db.VarChar(255) 545 title String @db.VarChar(255) 546 // events without dates are useful when they're "drafts", but they' can't appear in the calendar - thus they're only visible by link 547 startsAt DateTime? 548 endsAt DateTime? 549 globalCapacity Int? 550 notifiedAt DateTime? @default(now()) // to prevent old notifications before this was added. Another migration will remove this default value. 551 location String @default("") @db.VarChar(255) 552 visibility Visibility 553 frequency EventFrequency @default(Once) 554 recurringUntil DateTime? 555 pictureFile String @default("") @db.VarChar(255) 556 showPlacesLeft Boolean @default(true) 557 showCapacity Boolean @default(true) 558 managers EventManager[] 559 author User? @relation(fields: [authorId], references: [id], onUpdate: Cascade, onDelete: SetNull) 560 includeInKiosk Boolean @default(false) 561 group Group @relation(fields: [groupId], references: [id]) 562 coOrganizers Group[] @relation("coOrganizer") 563 tickets Ticket[] 564 ticketGroups TicketGroup[] 565 articles Article[] 566 lydiaAccountId String? 567 links Link[] 568 bannedUsers User[] @relation("bannedFromEvents") 569 reactions Reaction[] 570 forms Form[] 571 sharedBy User[] @relation("eventShares") 572 applicableOffers Promotion[] 573 managerInvites EventManagerInvite[] 574 // enfore providing a point of contact for external bookings 575 enforcePointOfContact Boolean @default(false) 576 577 // For full-text search 578 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 579 580 @@index([search], type: Gin) 581} 582 583enum Visibility { 584 Private 585 Unlisted 586 GroupRestricted 587 SchoolRestricted 588 Public 589} 590 591/// Used to make invite links to invite people as managers of an event 592model EventManagerInvite { 593 id String @id @default(dbgenerated("nanoid('eminvite:'::text)")) 594 createdAt DateTime @default(now()) 595 updatedAt DateTime @updatedAt 596 597 /// Maximum number of uses 598 capacity Int? 599 code String @unique 600 /// If null, should expire at the event's end date, not never expire 601 expiresAt DateTime? 602 603 eventId String 604 event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) 605 606 canVerifyRegistrations Boolean @default(true) // Can scan tickets 607 canEdit Boolean @default(false) 608 canEditPermissions Boolean @default(false) 609 610 usedBy User[] @relation("eventManagerInviteUses") 611} 612 613/// An event manager is a user that can scan tickets, and may be able to manage the event 614model EventManager { 615 id String @id @default(dbgenerated("nanoid('em:'::text)")) 616 eventId String 617 userId String 618 canVerifyRegistrations Boolean @default(true) // Can scan tickets 619 canEdit Boolean @default(false) 620 canEditPermissions Boolean @default(false) 621 622 event Event @relation(fields: [eventId], references: [id], onUpdate: Cascade, onDelete: Cascade) 623 user User @relation(fields: [userId], references: [id], onUpdate: Cascade, onDelete: Cascade) 624 625 @@unique([eventId, userId]) 626} 627 628/// A ticket group allows for conditions on multiple tickets, such as an upper limit on the sum of registrations in the sub-tickets 629model TicketGroup { 630 id String @id @default(dbgenerated("nanoid('tg:'::text)")) 631 eventId String 632 name String @db.VarChar(255) 633 634 capacity Int @default(0) // 0 means unlimited, capacity is on the sum of sub-tickets registrations 635 tickets Ticket[] 636 event Event @relation(fields: [eventId], references: [id]) 637 638 @@unique([eventId, name]) 639} 640 641/// Decide when to count a ticket as counting towards the capacity 642enum TicketCountingPolicy { 643 OnBooked // Count when the ticket is booked 644 OnPaid // Count when the ticket is paid 645} 646 647/// A ticket is a way to register for an event. May include a price and conditions. 648model Ticket { 649 id String @id @default(dbgenerated("nanoid('t:'::text)")) 650 slug String 651 eventId String 652 ticketGroupId String? 653 name String @db.VarChar(255) 654 description String @db.VarChar(255) 655 opensAt DateTime? 656 closesAt DateTime? 657 minimumPrice Float 658 maximumPrice Float 659 capacity Int? 660 registrations Registration[] 661 links Link[] 662 allowedPaymentMethods PaymentMethod[] @default([]) // empty means all 663 countingPolicy TicketCountingPolicy @default(OnBooked) 664 inviteCode String? @unique 665 666 // Conditions for that ticket. 667 openToPromotions Int[] @default([]) 668 openToAlumni Boolean? @default(false) // false means only non-alumni, true means only alumni, null means both 669 openToExternal Boolean? @default(false) // same thing 670 openToContributors Boolean? // same thing 671 openToApprentices Boolean? // same thing 672 openToSchools School[] 673 openToMajors Major[] 674 openToGroups Group[] @relation("openTo") 675 godsonLimit Int @default(0) // 0 means unlimited 676 autojoinGroups Group[] @relation("autojoin") 677 678 onlyManagersCanProvide Boolean @default(false) 679 680 event Event @relation(fields: [eventId], references: [id], onUpdate: Cascade, onDelete: Cascade) 681 group TicketGroup? @relation(fields: [ticketGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade) 682 Promotion Promotion? @relation(fields: [promotionId], references: [id]) 683 promotionId String? 684 invited User[] @relation("invitedTo") 685} 686 687/// A reservation is a user's registration for a ticket 688model Registration { 689 id String @id @default(dbgenerated("nanoid('r:'::text)")) 690 ticketId String 691 externalBeneficiary String? 692 internalBeneficiary User? @relation(fields: [internalBeneficiaryId], references: [id], onUpdate: Cascade, onDelete: SetNull, name: "beneficiaryOf") 693 internalBeneficiaryId String? 694 authorId String? 695 authorEmail String @default("") 696 authorName String @default("") 697 verifiedById String? 698 opposedById String? 699 cancelledById String? 700 createdAt DateTime @default(now()) 701 updatedAt DateTime @updatedAt 702 paymentMethod PaymentMethod? 703 paid Boolean @default(false) 704 wantsToPay Float? 705 // Person to refer to, especially for external beneficiaries, to handle misconducts at the event 706 pointOfContactId String? 707 pointOfContact User? @relation(fields: [pointOfContactId], references: [id], onDelete: SetNull, name: "pointOfContactFor") 708 709 lydiaTransaction LydiaTransaction? 710 paypalTransaction PaypalTransaction? 711 ticket Ticket @relation(fields: [ticketId], references: [id], onUpdate: Cascade, onDelete: Cascade) 712 author User? @relation(name: "author", fields: [authorId], references: [id], onUpdate: Cascade, onDelete: SetNull) 713 verifiedBy User? @relation(name: "verifiedBy", fields: [verifiedById], references: [id], onUpdate: Cascade, onDelete: SetNull) 714 verifiedAt DateTime? 715 opposedBy User? @relation(name: "opposedBy", fields: [opposedById], references: [id], onUpdate: Cascade, onDelete: SetNull) 716 opposedAt DateTime? 717 cancelledBy User? @relation(name: "cancelledBy", fields: [cancelledById], references: [id], onUpdate: Cascade, onDelete: SetNull) 718 cancelledAt DateTime? 719 seenBy User[] @relation(name: "seenBy") 720 formAnswer Answer? 721 722 // Those are kept in sync by a trigger, and are used for fuzzy-matching in a full text search 723 authorFullName String? @default("") @db.VarChar(255) 724 internalBeneficiaryFullName String? @default("") @db.VarChar(255) 725 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 726 727 @@index([search], type: Gin) 728} 729 730enum PaymentMethod { 731 Lydia 732 PayPal 733 Card 734 Transfer 735 Check 736 Cash 737 External 738 Other 739} 740 741/// A log entry is a log of an action that happened on the website 742model LogEntry { 743 id String @id @default(dbgenerated("nanoid('log:'::text)")) 744 happenedAt DateTime @default(now()) 745 userId String? 746 area String @db.VarChar(255) // billeterie, gestion clubs, etc. à typer, mais pas dans la DB (pour être plus flexible) 747 action String @db.VarChar(255) 748 target String? @db.VarChar(255) 749 message String @db.Text 750 751 user User? @relation(fields: [userId], references: [id], onUpdate: Cascade, onDelete: SetNull) 752} 753 754/// A Lydia account 755model LydiaAccount { 756 id String @id @default(dbgenerated("nanoid('lydia:'::text)")) 757 groupId String? 758 group Group? @relation(fields: [groupId], references: [id], onUpdate: Cascade, onDelete: Cascade) 759 name String @default("") @db.VarChar(255) 760 privateToken String @default("") @db.VarChar(255) 761 vendorToken String @default("") @db.VarChar(255) 762 studentAssociationId String? 763 events Event[] 764 studentAssociation StudentAssociation? @relation(fields: [studentAssociationId], references: [id]) 765 ContributionOption ContributionOption[] 766 767 @@unique([privateToken, vendorToken, groupId]) 768} 769 770// Lydia payment 771model LydiaTransaction { 772 id String @id @default(dbgenerated("nanoid('lydiapayment:'::text)")) 773 phoneNumber String @default("") @db.VarChar(255) 774 registrationId String? @unique 775 registration Registration? @relation(fields: [registrationId], references: [id], onUpdate: Cascade, onDelete: Cascade) 776 shopPaymentId String? @unique 777 requestId String? 778 requestUuid String? 779 transactionId String? 780 createdAt DateTime @default(now()) 781 updatedAt DateTime @updatedAt 782 contribution Contribution? @relation(fields: [studentAssociationContributionId], references: [id], onDelete: Cascade, onUpdate: Cascade) 783 studentAssociationContributionId String? @unique 784 paidCallback String? 785} 786 787enum PayPalTransactionStatus { 788 Created 789 Saved 790 Approved 791 Voided 792 Completed 793 PayerActionRequired 794} 795 796/// Paypal payment 797model PaypalTransaction { 798 id String @id @default(dbgenerated("nanoid('paypalpayment:'::text)")) 799 emailAddress String @default("") @db.VarChar(255) 800 registrationId String? @unique 801 registration Registration? @relation(fields: [registrationId], references: [id], onUpdate: Cascade, onDelete: Cascade) 802 status PayPalTransactionStatus? 803 804 /// PayPal's order ID. 805 orderId String? 806 createdAt DateTime @default(now()) 807 updatedAt DateTime @updatedAt 808} 809 810/// A NotificationSubscription stores a user's subscription to push notifications on a user agent 811model NotificationSubscription { 812 id String @id @default(dbgenerated("nanoid('notifsub:'::text)")) 813 name String @default("") @db.VarChar(255) 814 createdAt DateTime @default(now()) 815 updatedAt DateTime @updatedAt 816 owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade, onUpdate: Cascade) 817 ownerId String 818 endpoint String @unique 819 expiresAt DateTime? 820 authKey String 821 p256dhKey String 822 notifications Notification[] 823} 824 825/// A notification is a push notification that was sent to a user 826model Notification { 827 id String @id @default(dbgenerated("nanoid('notif:'::text)")) 828 createdAt DateTime @default(now()) 829 updatedAt DateTime @updatedAt 830 timestamp DateTime? 831 subscription NotificationSubscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade, onUpdate: Cascade) 832 subscriptionId String 833 image String @default("") 834 actions Link[] 835 title String @db.VarChar(255) 836 imageFile String @default("") 837 body String @db.Text 838 vibrate Int[] @default([]) 839 group Group? @relation(fields: [groupId], references: [id], onDelete: SetNull, onUpdate: Cascade) 840 groupId String? 841 channel NotificationChannel @default(Other) 842 user User? @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) 843 userId String? 844 goto String @default("") 845} 846 847/// A NotificationChannel represents the different kinds of reasons why you might receive a notification: a shotgun just opened, etc. 848enum NotificationChannel { 849 Articles 850 Shotguns 851 Permissions 852 GroupBoard 853 GodparentRequests 854 Mandatory 855 Other // should't be used too much 856} 857 858/// Announcement is a way to get a message accross the entire site, such as for maintenance announcements. 859model Announcement { 860 id String @id @default(dbgenerated("nanoid('ann:'::text)")) 861 by User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade) 862 userId String? 863 title String @db.VarChar(255) 864 body String @db.Text 865 warning Boolean 866 createdAt DateTime @default(now()) 867 updatedAt DateTime @updatedAt 868 startsAt DateTime 869 endsAt DateTime 870} 871 872model TeachingUnit { 873 id String @id @default(dbgenerated("nanoid('ue:'::text)")) 874 name String @db.VarChar(255) 875 shortName String @default("") @db.VarChar(255) 876 apogeeCode String? @db.VarChar(255) 877 subjects Subject[] 878} 879 880model Subject { 881 id String @id @default(dbgenerated("nanoid('subj:'::text)")) 882 name String 883 slug String 884 shortName String @default("") @db.VarChar(255) 885 yearTier Int? 886 forApprentices Boolean @default(false) 887 links Link[] 888 apogeeCode String? @db.VarChar(255) 889 unitId String? 890 unit TeachingUnit? @relation(fields: [unitId], references: [id], onDelete: SetNull, onUpdate: Cascade) 891 nextExamAt DateTime? 892 majors Major[] 893 minors Minor[] 894 documents Document[] 895 // 1 for the first semester, 2 for the second one. Null means both 896 semester Int? 897 emoji String @default("") @db.VarChar(4) 898 899 @@unique([slug, yearTier, forApprentices]) 900} 901 902enum DocumentType { 903 // Graded 904 Exam // Partiel 905 PracticalExam // BE 906 GradedExercises // DM 907 908 // Ungraded 909 Exercises // TD 910 Practical // TP 911 CourseNotes // CM 912 CourseSlides // Diapos 913 Summary // Fiche de rev, Fiche 914 915 Miscellaneous // Divers 916} 917 918model Document { 919 id String @id @default(dbgenerated("nanoid('doc:'::text)")) 920 slug String 921 createdAt DateTime @default(now()) 922 updatedAt DateTime @updatedAt 923 schoolYear Int // stored using the school year start's year 924 925 title String 926 description String @db.Text 927 // Null subject means the document needs to be sorted 928 subject Subject? @relation(fields: [subjectId], references: [id], onDelete: Cascade, onUpdate: Cascade) 929 subjectId String? 930 type DocumentType 931 paperPaths String[] // le sujet 932 solutionPaths String[] // la correction 933 934 uploader User? @relation(fields: [uploaderId], references: [id], onDelete: SetNull, onUpdate: Cascade) 935 uploaderId String? 936 937 reactions Reaction[] 938 939 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 940 941 @@unique([subjectId, slug]) 942 @@index([search], type: Gin) 943} 944 945model Reaction { 946 id String @id @default(dbgenerated("nanoid('reac:'::text)")) 947 emoji String 948 949 createdAt DateTime @default(now()) 950 updatedAt DateTime @updatedAt 951 author User? @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade) 952 authorId String? 953 954 document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade, onUpdate: Cascade) 955 documentId String? 956 article Article? @relation(fields: [articleId], references: [id], onDelete: Cascade, onUpdate: Cascade) 957 articleId String? 958 event Event? @relation(fields: [eventId], references: [id], onDelete: Cascade, onUpdate: Cascade) 959 eventId String? 960 961 @@unique([emoji, authorId, documentId]) 962 @@unique([emoji, authorId, articleId]) 963 @@unique([emoji, authorId, eventId]) 964} 965 966enum PromotionType { 967 SIMPPS 968 // More to come... 969} 970 971model PromotionCode { 972 id String @id @default(dbgenerated("nanoid('promocode:'::text)")) 973 createdAt DateTime @default(now()) 974 updatedAt DateTime @updatedAt 975 claimedAt DateTime? 976 code String @unique 977 claimedBy User? @relation(fields: [claimedById], references: [id], onDelete: Cascade) 978 claimedById String? 979 promotion Promotion @relation(fields: [promotionId], references: [id], onDelete: Cascade) 980 promotionId String 981} 982 983model Promotion { 984 id String @id @default(dbgenerated("nanoid('promo:'::text)")) 985 createdAt DateTime @default(now()) 986 updatedAt DateTime @updatedAt 987 validUntil DateTime? 988 type PromotionType 989 codes PromotionCode[] 990 validOn Ticket[] 991 validByDefaultOn Group[] @relation("defaultApplicableOffers") 992 993 priceOverride Int 994 events Event[] 995} 996 997model Form { 998 id String @id @default(dbgenerated("nanoid('form:'::text)")) 999 createdAt DateTime @default(now()) 1000 updatedAt DateTime @updatedAt 1001 createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull, onUpdate: Cascade) 1002 createdById String? 1003 visibility Visibility 1004 groupId String? 1005 group Group? @relation(fields: [groupId], references: [id], onDelete: Cascade, onUpdate: Cascade) 1006 1007 event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull, onUpdate: Cascade) 1008 eventId String? 1009 1010 opensAt DateTime? 1011 closesAt DateTime? 1012 title String @db.VarChar(255) 1013 description String @db.Text 1014 sections FormSection[] 1015 answeredBy User[] @relation("completedForms") 1016 partiallyAnsweredBy User[] @relation("partiallyCompletedForms") 1017 allowEditingAnswers Boolean @default(true) 1018 // Pretty specific use-case: manually marking or un-marking answers from the answer list for each user. Used for votes that can be both online and offline. 1019 enableAnswersCompletionCheckbox Boolean @default(false) 1020 markedCheckboxes User[] @relation("formsWithMarkedCheckbox") 1021 restrictToPromotions Int[] 1022 contributorsOnly Boolean @default(false) 1023 1024 // For full-text search 1025 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 1026 linkedGoogleSheetId String? 1027 1028 @@index([search], type: Gin) 1029} 1030 1031model FormSection { 1032 id String @id @default(dbgenerated("nanoid('formsection:'::text)")) 1033 order Int 1034 form Form @relation(fields: [formId], references: [id], onDelete: Cascade) 1035 formId String 1036 title String @db.VarChar(255) 1037 description String @db.Text 1038 questions Question[] 1039 1040 /// Conditions 1041 1042 restrictedToGroups Group[] @relation("restrictedTo") 1043 jumps FormJump[] 1044 1045 @@unique([formId, order]) 1046} 1047 1048/// Represent a jump condition: answering a certain value of a certain question changes what section of the form is shown next 1049model FormJump { 1050 id String @id @default(dbgenerated("nanoid('formjump:'::text)")) 1051 question Question @relation(fields: [questionId], references: [id], onDelete: Cascade) 1052 questionId String 1053 value String 1054 target FormSection @relation(fields: [targetId], references: [id], onDelete: Cascade, onUpdate: Cascade) 1055 targetId String 1056} 1057 1058model Question { 1059 id String @id @default(dbgenerated("nanoid('question:'::text)")) 1060 section FormSection @relation(fields: [sectionId], references: [id], onDelete: Cascade, onUpdate: Cascade) 1061 sectionId String 1062 order Int 1063 1064 title String @db.VarChar(255) 1065 description String @db.Text 1066 type QuestionKind 1067 mandatory Boolean @default(false) 1068 anonymous Boolean @default(false) 1069 1070 options String[] @default([]) 1071 scaleStart Int? 1072 scaleEnd Int? 1073 allowOptionOther Boolean @default(false) 1074 allowedFiletypes String[] @default([]) // empty means all 1075 1076 // See Answer 1077 defaultAnswer String[] 1078 1079 answers Answer[] 1080 jumps FormJump[] 1081 1082 @@unique([sectionId, order]) 1083} 1084 1085enum QuestionKind { 1086 Text 1087 LongText 1088 SelectOne 1089 SelectMultiple 1090 FileUpload 1091 // In scale, options has two values, the labels of the start and end of the scale 1092 Scale 1093 Number 1094 Date 1095 Time 1096} 1097 1098model Answer { 1099 id String @id @default(dbgenerated("nanoid('answer:'::text)")) 1100 question Question @relation(fields: [questionId], references: [id], onDelete: Cascade) 1101 questionId String 1102 1103 createdAt DateTime @default(now()) 1104 updatedAt DateTime @updatedAt 1105 1106 createdBy User? @relation(fields: [createdById], references: [id], onDelete: Cascade, onUpdate: Cascade) 1107 createdById String? 1108 1109 /// Only the first value is considered for all types except SelectMultiple. 1110 /// Can be empty if the user didn't answer the question (when it wasn't mandatory, for example) 1111 answer String[] 1112 1113 /// Data to link back to an event booking 1114 booking Registration? @relation(fields: [bookingId], references: [id], onDelete: SetNull, onUpdate: Cascade) 1115 bookingId String? @unique 1116 1117 // For full-text search 1118 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 1119 1120 // For now, users can answer only once to every question 1121 @@unique([questionId, createdById]) 1122 @@index([search], type: Gin) 1123} 1124 1125/// A user-defined markdown page, useful for custom pages, only liked to student associations for now. 1126model Page { 1127 id String @id @default(dbgenerated("nanoid('page:'::text)")) 1128 1129 // Paths are unique per linked resource, final URL of the page should therefore be namespaced 1130 path String @db.VarChar(255) 1131 title String @db.VarChar(255) 1132 body String @db.Text 1133 createdAt DateTime @default(now()) 1134 updatedAt DateTime @updatedAt 1135 lastAuthor User? @relation(fields: [lastAuthorId], references: [id], onDelete: SetNull, onUpdate: Cascade) 1136 lastAuthorId String? 1137 studentAssociation StudentAssociation? @relation(fields: [studentAssociationId], references: [id], onDelete: Cascade, onUpdate: Cascade) 1138 studentAssociationId String? 1139 group Group? @relation(fields: [groupId], references: [id], onDelete: SetNull, onUpdate: Cascade) 1140 groupId String? 1141 /// Paths to files that are included on that page (relative to the storage root as always) 1142 files String[] 1143 // For full-text search 1144 search Unsupported("tsvector") @default(dbgenerated("''::tsvector")) 1145 1146 @@unique([studentAssociationId, path]) 1147 @@unique([groupId, path]) 1148 @@index([search], type: Gin) 1149} 1150 1151// UIDs that should never be considered free 1152model BlockedUid { 1153 uid String @id 1154} 1155 1156model Theme { 1157 id String @id @default(dbgenerated("nanoid('theme:'::text)")) 1158 name String @db.VarChar(255) 1159 values ThemeValue[] 1160 createdAt DateTime @default(now()) 1161 updatedAt DateTime @updatedAt 1162 visibility Visibility @default(Private) 1163 startsAt DateTime? @default(now()) 1164 endsAt DateTime? 1165 author Group? @relation(fields: [authorId], references: [id], onDelete: SetNull, onUpdate: Cascade) 1166 authorId String? 1167 1168 // Auto-change people's theme to this one when it's visible to them 1169 autodeploy Boolean @default(false) 1170 blockedBy User[] 1171} 1172 1173enum ThemeVariant { 1174 Light 1175 Dark 1176 // Might add more stuff, like "High contrast light, high contrast dark", etc 1177} 1178 1179enum ThemeVariable { 1180 ColorBackground 1181 ColorBackground2 1182 ColorBackground3 1183 ColorBackground4 1184 ColorShy 1185 ColorMuted 1186 ColorForeground 1187 ColorPrimary 1188 ColorSuccess 1189 ColorDanger 1190 ColorWarning 1191 ColorPrimaryBackground 1192 ColorSuccessBackground 1193 ColorDangerBackground 1194 ColorWarningBackground 1195 ImageLogoNavbarTop 1196 ImageLogoNavbarSide 1197 ImageBackgroundNavbarBottom 1198 ImageBackgroundNavbarTop 1199 PatternBackground 1200} 1201 1202model ThemeValue { 1203 id String @id @default(dbgenerated("nanoid('themeval:'::text)")) 1204 variable ThemeVariable 1205 value String @db.VarChar(500) 1206 theme Theme @relation(fields: [themeId], references: [id], onDelete: Cascade, onUpdate: Cascade) 1207 themeId String 1208 variant ThemeVariant @default(Light) 1209 1210 @@unique([themeId, variant, variable]) 1211}