diff --git a/.forge/features/judging-discord-comms/spec.md b/.forge/features/judging-discord-comms/spec.md
new file mode 100644
index 000000000..5348482d2
--- /dev/null
+++ b/.forge/features/judging-discord-comms/spec.md
@@ -0,0 +1,222 @@
+# Judging Discord comms spec
+
+Status: Approved from the 2026-09-05 implementation request
+
+> This file owns the user and product behavior. Technical design belongs in `srd.md`.
+
+## User-facing purpose
+
+Judging rooms need a quiet way to reach organizers while pitches are underway.
+Walkie-talkies interrupt presenters, and authenticated organizers already have
+Discord on their phones. Officers should be able to connect the judging room
+workspace to one Discord channel, then use a room-specific thread for updates
+and guest QR delivery. Officers also need a fast way to place an operational
+notice over the judging workspace without interrupting a pitch.
+
+## Users and actors
+
+- **Officer:** chooses or removes the hackathon's judging communications
+ channel and manages room QR access.
+- **Authenticated judge:** a Blade user with judge or officer access who may
+ select a room. Discord mentions let this judge find that room's thread.
+- **Guest judge:** enters through the existing room QR flow. Completing the
+ full-name dialog announces the guest to that room's authenticated judges. The
+ guest does not need Discord and is never mentioned as a Discord user.
+
+## User-visible interface
+
+### Communications channel
+
+The Rooms tab in Command Center has a `Judging communications` panel. An
+officer can search the active environment's Knight Hacks guild text channels,
+select one, save it, or disconnect Discord communications.
+
+The setting is optional. Rooms, QR generation, room assignment, and revocation
+continue to work when no channel is selected or Discord is unavailable.
+
+Saving a channel creates a thread named after each active room. A room created
+later receives its thread automatically. Changing the channel creates new room
+threads under the new channel. Existing Discord history stays in the old
+channel.
+
+The panel shows the selected channel, connection state, and any rooms whose
+thread could not be provisioned. Officers can retry provisioning without
+changing the channel.
+
+### Room thread access and arrivals
+
+When an authenticated judge newly selects or switches into a room, Blade posts
+a short notice in that room's thread and mentions that judge. The mention makes
+the thread visible in Discord. Reopening the page and presence heartbeats do
+not send another notice.
+
+Authenticated judges who enter a room by scanning its QR receive the same
+mention.
+
+When a guest completes the required name dialog, Blade posts a guest-arrival
+notice with the entered name and mentions the authenticated judges currently
+assigned to the room. QR activation alone stays quiet because the guest has not
+identified themselves yet. Guest heartbeats and page reloads do not post.
+
+Every Blade view that identifies an authenticated judge uses the current full
+name from that judge's Member profile. This includes room rosters, feedback,
+scores, evaluation history, and deliberation. The linked Discord account label
+is the fallback only when no Member profile name is available. Guest judges
+keep the full name they entered during QR access.
+
+Each connected room includes an `Open thread` link in Command Center.
+
+### QR delivery
+
+Creating a room's first active QR posts the QR image and activation link in the
+room thread. It mentions the authenticated judges currently assigned to the
+room.
+
+An active room has a separate `Send QR` action. It posts the current QR again
+without rotating or revoking it. Opening the QR preview does not post to
+Discord.
+
+Rotating a QR revokes the old access, creates the replacement, and posts the
+new QR image and link. The notice mentions assigned authenticated judges.
+
+### Revocation notices
+
+Revoking one guest session posts the guest's display name and the officer who
+revoked access. Revoking a whole room QR identifies the affected guest judges,
+when any are named, and explains that the room QR is no longer valid. Both
+notices mention authenticated judges currently assigned to the room.
+
+Discord delivery never controls the security result. A join, guest sign-in,
+revocation, or rotation still succeeds if the message cannot be sent. Blade
+reports the Discord failure where an initiating user is present. The live room
+roster remains the source of truth for guest presence.
+
+### Judging announcements
+
+The Rooms tab lets an officer publish either a hackathon-wide announcement or
+an announcement for one room. Each scope has one current announcement.
+Publishing another message in the same scope replaces the current one, and an
+officer can clear either scope independently.
+
+Announcements target authenticated judges by default. An explicit `Include
+guest judges` switch also shows the announcement to QR guests. A guest never
+sees a room announcement for another room.
+
+Current announcements appear over the judging workspace. A standard notice is
+a persistent banner with an X. It never disappears on a timer. An urgent notice
+opens as a blocking dialog that the judge must acknowledge. Both forms remain
+available on Projects, Submissions, Deliberation, project details, and while a
+judge fills out feedback. Their labels identify whether they apply to all
+judging rooms or the selected room.
+
+Publishing also posts to Discord. A global announcement posts in the root
+judging communications channel and mentions every authenticated judge for the
+hackathon who has a linked Discord account. A room announcement posts in that
+room's thread and mentions the authenticated judges currently assigned there.
+Guest judges can see eligible Blade notices but are never Discord recipients.
+The announcement remains published in Blade if Discord delivery fails.
+
+The server renders the current notices on first load. While the judging page
+stays open, Blade checks every 30 seconds for replacements and cleared notices,
+including while the tab is hidden. Announcements do not depend on Discord or a
+webhook.
+
+## Scope
+
+### In scope
+
+- Optional communications channel per hackathon judging configuration.
+- Searchable guild text-channel selection through the existing combobox pattern.
+- One durable Discord thread reference per active judging room.
+- Thread provisioning when the channel is saved and when rooms are created.
+- Authenticated judge room-entry mentions.
+- Named guest arrival notices with authenticated room-judge mentions.
+- QR image and link delivery on first generation, explicit resend, and rotation.
+- Guest-session and room-link revocation notices.
+- Links from Command Center to room threads.
+- Safe partial-failure feedback and retry.
+- Hackathon-wide and room-scoped judging announcements.
+- Authenticated-only delivery by default, with optional guest delivery.
+- Server-rendered notices followed by 30-second polling.
+- Persistent banners, urgent acknowledgement dialogs, and Discord delivery.
+
+### Out of scope
+
+- Discord accounts or mentions for guest judges.
+- General judging chat outside room threads.
+- Mirroring messages between Blade and Discord.
+- Reading Discord thread messages inside Blade.
+- WebSockets or a new Discord bot command.
+- Deleting old threads when an officer changes or removes the channel.
+- Scheduling presentations or notifying hackers.
+- Push notifications and WebSockets for announcements.
+
+## Vocabulary
+
+- **Root communications channel:** the optional Discord text channel that owns
+ all judging room threads for one hackathon.
+- **Room thread:** the Discord thread tied to one durable judging room.
+- **Assigned authenticated judge:** a member judge with current presence in
+ the room. Guest sessions do not count as Discord recipients.
+- **Guest arrival:** the one-time notice sent after a guest successfully saves
+ their display name.
+- **Send QR:** repost the current active room QR without changing its access
+ credential.
+- **Judging announcement:** the current operational notice for either the
+ hackathon or one room.
+
+## Acceptance criteria
+
+- An officer can search and select a text channel from the configured
+ development or production guild.
+- Saving is allowed with no channel selected, and all non-Discord judging
+ behavior remains available.
+- Saving a channel creates or reuses one thread for every active room.
+- A room created after channel setup receives a thread.
+- Changing the channel creates replacement room threads without deleting old
+ Discord history.
+- A newly assigned authenticated judge is mentioned once in the room thread.
+- Authenticated QR entry sends the same room-entry mention.
+- A guest's completed name step posts one arrival notice and mentions current
+ authenticated room judges.
+- The guest identity field is labeled `Full name`.
+- Authenticated judge names shown in judging views come from current Member
+ profiles, with the Discord account label as fallback.
+- Guest QR activation, page reloads, and heartbeat polling send no message.
+- First QR generation posts its image and link in the room thread.
+- Viewing an existing QR does not post it again.
+- `Send QR` posts the current QR without changing its URL or link ID.
+- QR rotation posts only the replacement QR and leaves the security change in
+ effect if Discord delivery fails.
+- Guest revocation names the revoked guest and actor in the room thread.
+- Room-link revocation names affected guests when available.
+- QR, guest-arrival, and revocation messages mention only current
+ authenticated room judges.
+- Discord messages cannot produce `@everyone`, `@here`, role, or arbitrary
+ user mentions from room names or guest names.
+- Command Center shows the configured channel and provides room-thread links.
+- A Discord outage never rolls back a room join, guest sign-in, QR creation,
+ rotation, or revocation.
+- An officer can publish, replace, and clear a global announcement or one room's
+ announcement.
+- Publishing a global announcement posts it in the root communications channel
+ and mentions authenticated hackathon judges. Publishing a room announcement
+ posts it in the room thread and mentions assigned authenticated judges.
+- Announcements default to authenticated judges. Guests see only announcements
+ whose officer enabled guest delivery.
+- Authenticated judges see the global announcement and the announcement for
+ their selected room. Guests see the eligible global announcement and the
+ eligible announcement for their QR room.
+- Room announcements never cross room boundaries.
+- Current notices render on the first server response and update within 30
+ seconds while the judging page remains open.
+- Standard notices stay above the judging workspace until the judge dismisses
+ them with an X. Urgent notices block interaction until acknowledged. No notice
+ disappears on a timer.
+- A Discord failure is reported to the officer but does not undo the Blade
+ announcement.
+
+## Open questions
+
+None blocking. Message wording may tighten during visual and live Discord
+review without changing this contract.
diff --git a/.forge/features/judging-discord-comms/srd.md b/.forge/features/judging-discord-comms/srd.md
new file mode 100644
index 000000000..01fde2630
--- /dev/null
+++ b/.forge/features/judging-discord-comms/srd.md
@@ -0,0 +1,238 @@
+# Judging Discord comms SRD
+
+Status: Approved from the 2026-09-05 implementation request
+
+## Technical purpose
+
+Connect judging rooms and QR access to optional, hackathon-scoped Discord
+communications. Forge stores the selected root channel and each room's current
+thread. Discord writes happen after judging state commits and return a separate
+delivery result. Forge also stores current judging announcements and serves
+them to Blade independently of Discord delivery.
+
+## Relevant principles
+
+- Keep Blade pages server-first and business logic in `@forge/api`.
+- Use officer-managed data instead of hard-coded yearly Discord IDs.
+- Resolve the guild through the existing environment-aware Discord config.
+- Keep Discord calls outside database transactions.
+- Follow `apps/blade/DESIGN_SYSTEM.md` for the Command Center UI.
+
+## Access policy
+
+- Guests and unauthenticated users cannot list channels, configure Discord, or
+ send QR messages.
+- Guest QR activation stays quiet. Successful name completion may announce the
+ guest to the room thread.
+- Authenticated judges may trigger a self-mention only when newly assigned to a
+ configured room.
+- Existing officer project-management permission gates protect every
+ configuration, provisioning, resend, rotation, and revocation procedure.
+- The same officer permission protects announcement publication and clearing.
+- `judgeProcedure` resolves announcement visibility from the principal. Clients
+ cannot select another room or opt themselves into guest-hidden notices.
+
+## Architecture and data flow
+
+- `@forge/db` owns additive nullable channel and thread columns.
+- `@forge/validators` owns the nullable channel input.
+- `@forge/api` owns channel validation, thread lifecycle, payload safety,
+ recipient resolution, QR attachments, and delivery status.
+- Blade owns the responsive channel combobox, thread links, actions, and toasts.
+- `@forge/utils/discord` remains the REST client.
+
+### Stored state
+
+`HackathonJudgingConfiguration` gains nullable
+`judgingCommsChannelId varchar(20)`. Null disables Discord communications.
+`JudgingRoom` gains nullable `discordThreadId varchar(20)`.
+
+`JudgingAnnouncement` stores a hackathon, optional room, message, urgency,
+guest audience flag, publishing officer, and clear metadata. A null room means
+the announcement is hackathon-wide. Partial unique indexes permit at most one
+uncleared global announcement per hackathon and one uncleared announcement per
+room.
+
+Changing or clearing the channel nulls active-room thread IDs. Saving a new
+channel provisions replacement threads but does not delete old Discord
+history. Archived rooms do not receive replacement threads.
+
+### Thread provisioning
+
+Saving a channel validates that it belongs to the resolved guild and supports
+text messages and threads. The channel commits first. The API then creates one
+starter message and public thread per active room, saving each returned thread
+ID. New rooms provision after their database insert. Room renames sync the
+current thread name after commit.
+
+The selected root channel is the Discord access boundary and must be restricted
+to Knight Hacks organizers. Public room threads intentionally inherit that
+channel's readers so officers can inspect every room without being assigned.
+Mentions are notifications, not an access-control list. Command Center states
+this requirement beside the channel selector because Blade cannot make an
+otherwise public Discord channel private.
+
+Provisioning is retryable and keeps successful room IDs when another room
+fails. A missing, forbidden, or invalid stored thread is replaced. Rate limits,
+server errors, and transport failures preserve the saved thread for retry.
+Thread names are single-line, mention-neutralized, and limited to Discord's
+100-character maximum. Discord network work runs outside database
+transactions; per-room serialization prevents duplicate local provisioning.
+
+### Room arrivals
+
+Member joins and authenticated QR activation distinguish existing same-room
+presence from a new entry. Existing presence only refreshes `lastSeenAt`.
+New presence commits, then sends a self-mention using the authenticated user's
+stored Discord snowflake.
+
+Guest QR activation sends nothing. `completeGuestJudge` commits the identity
+and presence, then sends one arrival notice with the guest's inert display
+name. It mentions authenticated judges with current presence in the room.
+Guest reloads and heartbeats send nothing.
+
+Judge-facing API reads batch-resolve authenticated judge names through the
+linked Member record before returning room rosters, score feedback, or
+evaluation history. They retain the stored Judge label as the Discord fallback.
+Guest labels never pass through Member-name resolution. The guest introduction
+field says `Full Name`, but the signed guest session remains the authorization
+identity.
+
+### QR and revocation delivery
+
+First QR generation posts the new PNG and signed link. Reading an existing QR
+does not post. `sendRoomQr` regenerates the PNG for the active link without
+changing its ID or sessions. It resolves and mentions current authenticated
+room judges at send time.
+
+Rotation commits revocation and replacement before posting the replacement QR.
+Guest revocation notices identify the guest and actor. Room-link revocation
+notices identify affected named guests when available. Both resolve current
+authenticated room judges after the security change.
+
+Payloads disable parsed mentions and allowlist only validated member Discord
+IDs. Room, actor, and guest names cannot create mentions. Guest identities are
+never treated as Discord users.
+
+Delivery status is `not_configured`, `delivered`, or `failed`. A Discord
+failure never rolls back room entry, guest completion, QR creation, rotation,
+or revocation.
+
+### Announcement reads and lifecycle
+
+Publishing runs in a transaction. It locks the scope, clears its current
+announcement, inserts the replacement, and records both actor and scope in the
+audit log. Clearing timestamps the current row instead of deleting it. The
+history remains available for incident review.
+
+Member visibility uses the member judge's current room presence for the chosen
+hackathon. Members receive the active global announcement and, when assigned,
+their room announcement. Guest visibility derives the hackathon and room from
+the signed guest principal and adds `includeGuests = true`. The API never trusts
+a client-provided room ID for announcement reads.
+
+`getContext` includes the initial visible announcements for server rendering.
+`listAnnouncements` uses the same resolver for client polling every 30 seconds.
+Polling continues while the document is hidden so a clear or urgent replacement
+arrives without waiting for focus.
+
+A normal announcement renders as a persistent banner. The judge can dismiss it
+with an X, and Blade remembers that announcement ID in the browser. A newly
+published replacement has a new ID and appears again. An urgent announcement
+renders as a blocking dialog and requires an acknowledgement for that ID.
+
+After the transaction commits, global publication posts to the configured root
+channel and allowlists linked Discord IDs for all member judges in the
+hackathon. Room publication posts to the room thread and allowlists linked IDs
+for member judges with current room presence. The mutation returns delivery
+status. Discord failure never rolls back the stored Blade announcement.
+
+## tRPC and API behavior
+
+Extend `judgingRouter` with:
+
+- `listDiscordChannels` for officer-only channel discovery.
+- `setCommsChannel` to save a nullable channel and provision active rooms.
+- `provisionRoomThreads` to retry missing threads.
+- `sendRoomQr` to repost the current active QR.
+- `publishAnnouncement` to replace the current global or room announcement.
+- `clearAnnouncement` to clear one current announcement.
+- `listAnnouncements` for principal-scoped polling.
+
+`listAdmin` returns the channel ID, resolved guild ID, room thread IDs, and
+missing-thread state. Current join, guest completion, QR, and revocation
+procedures return delivery status beside their domain result.
+
+Use `BAD_REQUEST` for an unsupported channel and `NOT_FOUND` for a missing
+room or active QR. Do not return raw Discord errors.
+
+## Validation
+
+- Optional channel IDs are 17 through 20 digit snowflakes.
+- Existing room and hackathon IDs remain UUIDs.
+- Live validation checks guild and channel type.
+- Message and filename builders enforce Discord limits and neutralize hostile
+ display names.
+- Announcement messages trim surrounding whitespace, reject control
+ characters, and allow 1 through 1,000 characters.
+
+## Data, migration, and compatibility
+
+- Generate additive migrations for the Discord columns and announcement table.
+- Existing hackathons remain disconnected. No migration backfill calls Discord.
+- Existing QR URLs, guest cookies, scoring, imports, and presence stay
+ compatible.
+- Rollback removes the columns but cannot delete Discord threads already
+ created.
+
+## Discord integration
+
+Add a focused judging gateway under `packages/api/src/utils/judging` with
+testable payload builders. Use existing Discord REST v10 dependencies and
+`getKnightHacksGuildId()`. Do not add a bot command, role, environment
+variable, dependency, or hard-coded guild/channel ID.
+
+## Configurability review
+
+No developer change is needed next year. Officers choose the channel for each
+hackathon and the existing Discord config chooses the environment's guild.
+
+## React and frontend constraints
+
+- Add one top-level communications panel inside the Rooms tab.
+- Use `ResponsiveComboBox` with channel name and ID search plus an explicit
+ disconnected action.
+- Show disconnected, connected, loading, and partial-failure states with text.
+- Add `Open thread` and `Send QR` beside current room actions.
+- Put a global announcement action beside the room controls and an announcement
+ action on every active room.
+- Use one composed dialog for global and room publication. Show the scope,
+ current message, authenticated default, guest switch, urgency switch, replace
+ action, and clear action.
+- Render standard notices above the judge workspace. Render urgent notices in a
+ blocking dialog above the feedback flow. Stack the global notice before the
+ room notice and label both scopes.
+- Server-render initial notices. Poll with React Query every 30 seconds and keep
+ background polling enabled.
+- Keep 44px targets, mobile wrapping, skeleton parity, and no horizontal
+ document overflow.
+- Warn about Discord delivery failure without claiming the judging action
+ failed.
+- Label the guest identity field `Full Name`.
+- Render authenticated judge identities with the current Member full name and
+ use the stored Discord label only when no Member profile is linked.
+- Put screenshots only in PR discussion, never in the repository.
+
+## Testing and verification strategy
+
+Cover validators, payload safety, thread reuse and recovery, current-recipient
+selection, member and guest arrivals, QR lifecycle rules, revocation failure
+isolation, permissions, Blade states, migrations, desktop, mobile, and live
+development-guild delivery.
+
+Run targeted tests, `pnpm format`, `pnpm lint`, `pnpm typecheck`,
+`pnpm analyze:react:changed`, migration checks, and the Blade build.
+
+## Open questions
+
+None.
diff --git a/.forge/features/judging-discord-comms/status.md b/.forge/features/judging-discord-comms/status.md
new file mode 100644
index 000000000..33c9f90b6
--- /dev/null
+++ b/.forge/features/judging-discord-comms/status.md
@@ -0,0 +1,110 @@
+# Judging Discord comms status
+
+Current phase: Review and delivery
+
+## Decision log
+
+- 2026-09-05: The human approved a separate PR for optional, per-hackathon
+ judging Discord communications.
+- 2026-09-05: Forge uses the existing environment-aware Knight Hacks guild.
+ No guild or channel ID is hard-coded.
+- 2026-09-05: Active rooms receive one current thread. Channel changes create
+ replacements and keep old Discord history.
+- 2026-09-05: Newly assigned authenticated judges receive a self-mention.
+ Heartbeats and same-room activity remain quiet.
+- 2026-09-05: Guest name completion posts one arrival notice and mentions the
+ authenticated judges currently assigned to the room.
+- 2026-09-05: First QR generation, explicit resend, and rotation post the QR.
+ Every explicit resend mentions the authenticated judges assigned at that
+ moment. Viewing an existing QR does not post.
+- 2026-09-05: Database security and presence actions commit independently of
+ Discord delivery.
+- 2026-09-05: Screenshots belong in PR discussion only.
+- 2026-09-05: Discord copy uses the linked member profile's full name. The
+ Discord account label remains a fallback for members without a profile.
+- 2026-09-05: The same full-name rule applies to every judging view that names
+ an authenticated judge. Guest access now labels its identity field `Full
+Name`.
+- 2026-09-05: The slice now includes one current global announcement and one
+ current announcement per room. Authenticated judges are the default audience;
+ officers may include QR guests.
+- 2026-09-05: Judging pages server-render visible notices and poll every 30
+ seconds, including in hidden tabs.
+- 2026-09-05: Every announcement also posts to Discord. Global notices mention
+ all authenticated hackathon judges in the root channel. Room notices mention
+ assigned authenticated judges in the room thread. Discord failure does not
+ roll back Blade publication.
+- 2026-09-05: Standard notices require an explicit X dismissal. Urgent notices
+ block the workspace until the judge acknowledges them. Neither uses a timer.
+
+## Open questions
+
+None blocking.
+
+## Task list
+
+- [x] Create a worktree and branch from merged `origin/main`.
+- [x] Review repository, database, design-system, Discord, and judging guidance.
+- [x] Complete the product spec, SRD, and test plan.
+- [x] Add schema, migration, validators, and Discord comms gateway.
+- [x] Add API configuration, thread, arrival, QR, and revocation behavior.
+- [x] Add Command Center channel and room-thread controls.
+- [x] Add automated tests and run required checks.
+- [x] Run desktop and mobile visual QA against KH VIII data.
+- [x] Test delivery in the development guild bot-testing channel.
+- [x] Create and assign the GitHub issue.
+- [x] Commit, push, open the PR, and attach screenshots outside the repo.
+- [x] Add announcement persistence, validation, principal-scoped reads, and
+ audit events.
+- [x] Add global and room announcement controls to Command Center.
+- [x] Add server-rendered announcement banners and urgent dialogs.
+- [x] Extend automated tests and visual evidence for announcements.
+- [x] Address and resolve CodeRabbit review threads until approved.
+
+## Validation and commands
+
+- `git fetch origin main` and `git rebase origin/main`: passed. The branch is
+ based on `bd97fccb` after PR #533 merged.
+- `pnpm forge:feature judging-discord-comms "Judging Discord Comms"`: passed
+ after linking the existing dependency directory.
+- `pnpm db:generate`: passed; created additive migrations
+ `0048_little_tarot` and `0049_real_redwing`.
+- `pnpm db:migrate`: passed against the pulled local database.
+- `pnpm format`: passed.
+- `pnpm lint`: passed after resolving two feature errors; repository warning
+ baseline remains.
+- `pnpm typecheck`: passed, 33 tasks.
+- `pnpm verify:precommit`: passed, including formatting, lint, type checks, and
+ repository policy checks across 33 tasks.
+- `pnpm --filter=@forge/validators test -- judging audit`: passed, 17 tests.
+- `pnpm --filter=@forge/api test -- judging`: passed, 25 tests.
+- `pnpm --filter=@forge/api test`: passed, 112 files and 797 tests.
+- `pnpm --filter=@forge/db test -- migration`: passed, 65 tests.
+- `pnpm --filter=@forge/db test -- judging-schema`: passed, 11 tests.
+- `pnpm --filter=@forge/db test`: passed, 148 tests, including development
+ backup policy classification for announcements.
+- `pnpm --filter=@forge/blade test -- authenticated-shell
+project-judge-privacy guest-name-gate judging-announcements`: passed, 29
+ tests.
+- `pnpm analyze:react:changed`: passed, 11 components and zero failures.
+- `pnpm --filter=@forge/blade build`: passed; 58 static pages generated.
+- Desktop Command Center, QR dialog, authenticated room assignment, live
+ roster, and 390px guest-dialog visual checks: passed.
+- Live Discord delivery through T.K in Dev@KnightHacks `#bot-testing`: passed.
+ The check covered thread provisioning, member arrival, guest arrival,
+ current-QR delivery, rotation, member mentions, and guest revocation.
+- Live testing caught Discord's 25-character nonce limit. QR and thread
+ messages now use a 22-character base64url nonce, backed by a regression test.
+- Live copy check: passed. Member arrivals and organizer actions show the
+ linked member profile name, such as `Dylan Vidal`, instead of a Discord
+ username.
+- Announcement delivery review: passed for replacement ordering, current-role
+ recipients, Discord mention batching, room-thread provisioning races,
+ complete long-message delivery, server-rendered urgent notices, background
+ polling, and mobile overflow.
+
+## Links
+
+- PRs: https://github.com/KnightHacks/forge/pull/535
+- Issues: https://github.com/KnightHacks/forge/issues/534
+- Discord/thread context: Dev@KnightHacks `#bot-testing`, Sponsor Suite A
diff --git a/.forge/features/judging-discord-comms/test-cases.md b/.forge/features/judging-discord-comms/test-cases.md
new file mode 100644
index 000000000..9d0345d20
--- /dev/null
+++ b/.forge/features/judging-discord-comms/test-cases.md
@@ -0,0 +1,232 @@
+# Judging Discord comms test cases
+
+Status: Approved from the 2026-09-05 implementation request
+
+## Scope and placement
+
+These cases cover channel configuration, room threads, arrival notices, QR
+delivery, revocation, permissions, message safety, failure isolation, and the
+Command Center UI. Validators, API utilities and router tests, migration tests,
+Blade tests, and manual desktop/mobile checks own the proof.
+
+## Test cases
+
+### TC-001: connect judging communications
+
+Saving a valid guild text channel persists it and provisions one named thread
+for every active room. Command Center shows the connected channel and links.
+
+### TC-002: keep Discord optional
+
+With no channel, room assignment and every QR action still work. Delivery
+returns `not_configured` and the UI never claims a message was sent.
+
+### TC-003: change or clear the channel
+
+Changing channels creates replacement active-room threads and leaves old
+history intact. Clearing the channel clears current thread references without
+disabling judging.
+
+### TC-004: provision new rooms
+
+Creating a room after channel setup commits the room and creates its sanitized
+thread.
+
+### TC-005: retry partial provisioning
+
+If one room fails, successful thread IDs stay saved, failed rooms are named,
+and retry provisions only missing threads.
+
+### TC-006: mention a member on new entry
+
+Selecting a room or entering through an authenticated QR commits presence and
+sends one notice that allowlists and mentions that member.
+
+### TC-007: keep repeated member activity quiet
+
+Same-room selection, reloads, and heartbeats refresh presence without another
+Discord post.
+
+### TC-008: announce a named guest
+
+Successful guest name completion commits identity and presence, posts the inert
+guest name once, and mentions authenticated judges currently in the room.
+
+### TC-009: keep other guest activity quiet
+
+Guest QR activation, reloads, and heartbeats do not post.
+
+### TC-010: post only a newly generated QR
+
+First generation posts the PNG and link with current member mentions. Viewing
+an existing QR sends nothing.
+
+### TC-011: resend the current QR
+
+`Send QR` posts the unchanged active link and a fresh PNG. It mentions every
+authenticated judge assigned at send time and does not change guest sessions.
+
+### TC-012: rotate and deliver the replacement
+
+Rotation revokes old access, commits a new link, and posts only the replacement
+QR with current member mentions.
+
+### TC-013: announce guest revocation
+
+Individual revocation commits, identifies the guest and actor in the thread,
+and mentions current member judges without treating the guest as a Discord user.
+
+### TC-014: announce room-link revocation
+
+Room-link revocation commits, identifies affected named guests within Discord
+limits, and mentions current member judges.
+
+### TC-015: render thread links safely
+
+An active room thread links to
+`https://discord.com/channels/{resolvedGuildId}/{threadId}` in a new tab.
+
+### TC-016: support desktop and mobile channel search
+
+Channel name and ID search works with long and numerous channels at desktop and
+390px widths. Controls retain 44px targets and no document overflow appears.
+
+### TC-017: publish a global announcement
+
+An officer publishes a message with the default audience. Every authenticated
+judge for the hackathon sees an `All judging rooms` notice. QR guests do not.
+The root Discord channel receives the message and linked member mentions.
+
+### TC-018: include guests in a global announcement
+
+Enabling `Include guest judges` exposes the global Blade notice to authenticated
+judges and active QR guests for that hackathon. Guests are not Discord
+recipients.
+
+### TC-019: publish a room announcement
+
+An authenticated judge assigned to the room sees its notice. A member in
+another room and a member without a room do not. A QR guest in the room sees it
+only when guest delivery is enabled. The room thread receives the message and
+mentions assigned authenticated judges.
+
+### TC-020: replace and clear an announcement
+
+Publishing again in the same scope clears the old row and makes only the new
+message current. Clearing removes the notice from the next read while retaining
+the stored history.
+
+### TC-021: render notices before hydration
+
+The first server response includes every announcement visible to the principal.
+There is no empty announcement flash while the client hydrates.
+
+### TC-022: poll every 30 seconds
+
+An open judging page picks up publications, replacements, and clears within 30
+seconds. Polling continues while the document is hidden.
+
+### TC-023: dismiss standard notices explicitly
+
+A standard notice never expires on a timer. Selecting its X hides that exact
+announcement in the browser. A replacement in the same scope appears because
+it has a new ID.
+
+### TC-024: acknowledge urgent notices
+
+An urgent global or room announcement opens above the judging workspace and an
+open feedback form. Judging interaction remains blocked until the judge
+acknowledges that announcement ID.
+
+### TC-025: expose current state in Command Center
+
+The Rooms tab shows the current global notice and each room's current notice,
+including guest visibility and urgency. Officers can replace or clear either
+one.
+
+### TC-026: show human names for every judge
+
+Room rosters, score feedback, and officer evaluation history show an
+authenticated judge's current Member full name even when the stored Judge label
+contains a Discord username. An authenticated judge without a linked Member
+profile keeps the stored Discord label. Guest judges keep the full name entered
+through the field labeled `Full Name`.
+
+## Negative and regression cases
+
+### TC-NEG-001: reject a foreign or unsupported channel
+
+The API returns `BAD_REQUEST`, preserves prior configuration, and creates no
+threads.
+
+### TC-NEG-002: enforce officer access
+
+Guests and non-officer judges cannot discover channels, save configuration,
+retry provisioning, or send a QR.
+
+### TC-NEG-003: isolate Discord failures
+
+Room entry, guest completion, QR creation, rotation, and revocation remain
+committed when Discord fails. Delivery reports `failed`.
+
+### TC-NEG-004: contain hostile names
+
+Markdown, `@everyone`, `@here`, and mention syntax in names stay inert.
+Allowed mentions contain only validated current member IDs.
+
+### TC-NEG-005: skip invalid Discord IDs
+
+Malformed member Discord IDs are omitted without affecting other recipients.
+
+### TC-NEG-006: recover a missing room thread
+
+A deleted or unusable stored thread is replaced and saved before delivery.
+
+### TC-NEG-007: require an active QR for resend
+
+`sendRoomQr` returns `NOT_FOUND` and sends nothing when no active link exists.
+
+### TC-NEG-008: preserve archived-room history
+
+Archived rooms receive no replacement thread and show no active send controls.
+
+### TC-NEG-009: prevent announcement scope spoofing
+
+Guests and members cannot request announcements for an arbitrary room. The API
+derives room visibility from the signed guest session or current member
+presence.
+
+### TC-NEG-010: enforce announcement permissions
+
+Guests and non-officer judges cannot publish, replace, or clear announcements.
+
+### TC-NEG-011: reject invalid announcement content
+
+Blank text, text over 1,000 characters, and control characters fail validation
+without replacing the current message.
+
+### TC-NEG-012: serialize same-scope publication
+
+Concurrent publications leave exactly one current row in a scope. Global and
+room scopes do not clear one another.
+
+### TC-NEG-013: isolate announcement Discord failure
+
+Blade publication stays current and the officer sees `failed` when Discord
+cannot accept the global or room announcement.
+
+### TC-NEG-014: preserve threads after transient Discord failures
+
+A rate limit, server error, or transport failure does not replace the saved
+room thread. A confirmed missing, forbidden, invalid, or wrong-parent thread is
+eligible for replacement.
+
+### TC-NEG-015: expose the Discord access boundary
+
+Command Center tells officers to select an organizer-only root channel and that
+thread readers inherit channel access. Mentions remain notification routing,
+not authorization.
+
+## Open questions
+
+None.
diff --git a/apps/blade/src/app/_components/judging/guest-name-gate.tsx b/apps/blade/src/app/_components/judging/guest-name-gate.tsx
index 8edb4521b..edfddbf10 100644
--- a/apps/blade/src/app/_components/judging/guest-name-gate.tsx
+++ b/apps/blade/src/app/_components/judging/guest-name-gate.tsx
@@ -59,14 +59,14 @@ export function GuestNameGate() {
-
Name
+
Full Name
setDisplayName(event.target.value)}
- placeholder="Your name"
+ placeholder="Your full name"
required
value={displayName}
/>
diff --git a/apps/blade/src/app/_components/judging/judging-announcements.tsx b/apps/blade/src/app/_components/judging/judging-announcements.tsx
new file mode 100644
index 000000000..e68f92ebb
--- /dev/null
+++ b/apps/blade/src/app/_components/judging/judging-announcements.tsx
@@ -0,0 +1,188 @@
+"use client";
+
+import { useMemo, useState, useSyncExternalStore } from "react";
+import { AlertTriangle, BellRing, X } from "lucide-react";
+import { createPortal } from "react-dom";
+
+import type { RouterOutputs } from "@forge/api";
+import { Badge } from "@forge/ui/badge";
+import { Button } from "@forge/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@forge/ui/dialog";
+
+import { api } from "~/trpc/react";
+
+type Announcement = RouterOutputs["judging"]["listAnnouncements"][number];
+
+const DISMISSED_KEY = "forge-judging-announcements-dismissed";
+const subscribeToNothing = () => () => undefined;
+
+function scopeLabel(announcement: Announcement) {
+ return announcement.roomName ?? "All judging rooms";
+}
+
+function readDismissed() {
+ try {
+ const value = JSON.parse(
+ sessionStorage.getItem(DISMISSED_KEY) ?? "[]",
+ ) as unknown;
+ return Array.isArray(value)
+ ? new Set(value.filter((id): id is string => typeof id === "string"))
+ : new Set
();
+ } catch {
+ return new Set();
+ }
+}
+
+export function JudgingAnnouncements({
+ hackathonId,
+ initialAnnouncements,
+}: {
+ hackathonId?: string;
+ initialAnnouncements: Announcement[];
+}) {
+ const [dismissedThisMount, setDismissedThisMount] = useState>(
+ new Set(),
+ );
+ const mounted = useSyncExternalStore(
+ subscribeToNothing,
+ () => true,
+ () => false,
+ );
+ const query = api.judging.listAnnouncements.useQuery(
+ { hackathonId },
+ {
+ refetchInterval: 30_000,
+ refetchIntervalInBackground: true,
+ },
+ );
+ const announcements = query.data ?? initialAnnouncements;
+ const dismissed = useMemo(() => {
+ if (!mounted) return dismissedThisMount;
+ return new Set([...readDismissed(), ...dismissedThisMount]);
+ }, [dismissedThisMount, mounted]);
+
+ function dismiss(id: string) {
+ const next = new Set([...readDismissed(), ...dismissedThisMount]);
+ next.add(id);
+ const ids = [...next].slice(-100);
+ try {
+ sessionStorage.setItem(DISMISSED_KEY, JSON.stringify(ids));
+ } catch {
+ // Keep this dismissal in memory when browser storage is unavailable.
+ }
+ setDismissedThisMount(new Set(ids));
+ }
+
+ const visible = announcements.filter(
+ (announcement) => !dismissed.has(announcement.id),
+ );
+ const urgent = visible.find((announcement) => announcement.isUrgent);
+ const banners = visible.filter((announcement) => !announcement.isUrgent);
+ const bannerLayer = banners.length ? (
+
+
+ {banners.map((announcement) => (
+
+
+
+
+
+
+
+
+
Judging announcement
+
+ {scopeLabel(announcement)}
+
+
+
+ {announcement.message}
+
+
+
dismiss(announcement.id)}
+ size="icon"
+ type="button"
+ variant="ghost"
+ >
+
+
+
+
+ ))}
+
+
+ ) : null;
+
+ return (
+ <>
+ {mounted && bannerLayer
+ ? createPortal(bannerLayer, document.body)
+ : bannerLayer}
+
+ {
+ if (!open && urgent) dismiss(urgent.id);
+ }}
+ open={!!urgent}
+ >
+ event.preventDefault()}
+ onInteractOutside={(event) => event.preventDefault()}
+ portalled={mounted}
+ showCloseButton={false}
+ >
+ {urgent ? (
+ <>
+
+
+
+
+
+ Urgent judging announcement
+ {scopeLabel(urgent)}
+
+
+ {urgent.message}
+
+
+
+ dismiss(urgent.id)}
+ type="button"
+ variant="destructive"
+ >
+ I understand
+
+
+
+ >
+ ) : null}
+
+
+ >
+ );
+}
diff --git a/apps/blade/src/app/_components/judging/judging-control-panel.tsx b/apps/blade/src/app/_components/judging/judging-control-panel.tsx
index ff9b7ec2a..e32ec6e47 100644
--- a/apps/blade/src/app/_components/judging/judging-control-panel.tsx
+++ b/apps/blade/src/app/_components/judging/judging-control-panel.tsx
@@ -10,12 +10,17 @@ import {
ArrowUp,
Copy,
DoorOpen,
+ ExternalLink,
+ Hash,
KeyRound,
+ Megaphone,
+ MessageCircle,
Pencil,
Plus,
Printer,
QrCode,
RefreshCw,
+ Send,
ShieldAlert,
UserRoundX,
UsersRound,
@@ -35,6 +40,9 @@ import {
} from "@forge/ui/dialog";
import { Input } from "@forge/ui/input";
import { Label } from "@forge/ui/label";
+import { ResponsiveComboBox } from "@forge/ui/responsive-combo-box";
+import { Switch } from "@forge/ui/switch";
+import { Textarea } from "@forge/ui/textarea";
import { toast } from "@forge/ui/toast";
import {
@@ -47,7 +55,11 @@ import { api } from "~/trpc/react";
type ControlData = RouterOutputs["judging"]["listAdmin"];
type Hackathons = RouterOutputs["projects"]["listAdminHackathons"];
type Room = ControlData["rooms"][number];
-type QrResult = RouterOutputs["judging"]["generateRoomLink"];
+type Announcement = NonNullable;
+type QrResult = Pick<
+ RouterOutputs["judging"]["generateRoomLink"],
+ "id" | "qrCodeUrl" | "url"
+>;
const ACTIVE_PRESENCE_WINDOW_MS = 2 * 60 * 1000;
@@ -311,6 +323,198 @@ function RoomQrDialog({
);
}
+interface AnnouncementDialogProps {
+ current: Announcement | null;
+ data: ControlData;
+ onClose: () => void;
+ onSaved: () => void;
+ room: Room | null;
+}
+
+export function AnnouncementDialog(props: AnnouncementDialogProps) {
+ return (
+
+ );
+}
+
+function AnnouncementDialogContent({
+ current,
+ data,
+ onClose,
+ onSaved,
+ room,
+}: AnnouncementDialogProps) {
+ const publish = api.judging.publishAnnouncement.useMutation();
+ const clear = api.judging.clearAnnouncement.useMutation();
+ const [includeGuests, setIncludeGuests] = useState(
+ current?.includeGuests ?? false,
+ );
+ const [isUrgent, setIsUrgent] = useState(current?.isUrgent ?? false);
+ const [message, setMessage] = useState(current?.message ?? "");
+ const scope = room?.name ?? "All judging rooms";
+
+ async function submit(event: FormEvent) {
+ event.preventDefault();
+ try {
+ const result = await publish.mutateAsync({
+ hackathonId: data.hackathon.id,
+ includeGuests,
+ isUrgent,
+ message,
+ roomId: room?.id ?? null,
+ });
+ if (result.discordDelivery === "failed") {
+ toast.error(
+ "Announcement published in Blade, but Discord delivery failed.",
+ );
+ } else if (result.discordDelivery === "not_configured") {
+ toast.success(
+ "Announcement published in Blade. Discord is not connected.",
+ );
+ } else if (result.discordDelivery === "superseded") {
+ toast.success(
+ "Announcement published in Blade, then replaced before Discord delivery.",
+ );
+ } else {
+ toast.success("Announcement published in Blade and Discord.");
+ }
+ onClose();
+ onSaved();
+ } catch (error) {
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Announcement could not be published.",
+ );
+ }
+ }
+
+ async function clearCurrent() {
+ if (!current) return;
+ try {
+ await clear.mutateAsync({ announcementId: current.id });
+ toast.success("Announcement cleared.");
+ onClose();
+ onSaved();
+ } catch (error) {
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Announcement could not be cleared.",
+ );
+ }
+ }
+
+ return (
+ !open && onClose()}>
+
+
+
+
+ );
+}
+
export function JudgingControlPanel({
initialData,
hackathons,
@@ -329,11 +533,22 @@ export function JudgingControlPanel({
null,
);
const [archiving, setArchiving] = useState(null);
+ const [announcementTarget, setAnnouncementTarget] = useState(
+ null,
+ );
+ const [commsDraft, setCommsDraft] = useState({
+ channelId: initialData.configuration.judgingCommsChannelId,
+ hackathonId: initialData.hackathon.id,
+ });
const query = api.judging.listAdmin.useQuery(
{ hackathonId: initialData.hackathon.id },
{ initialData, refetchInterval: 10_000 },
);
const generate = api.judging.generateRoomLink.useMutation();
+ const channels = api.judging.listDiscordChannels.useQuery();
+ const saveComms = api.judging.setCommsChannel.useMutation();
+ const retryThreads = api.judging.provisionRoomThreads.useMutation();
+ const sendQr = api.judging.sendRoomQr.useMutation();
const revoke = api.judging.revokeRoomLink.useMutation();
const rotate = api.judging.rotateRoomLink.useMutation();
const move = api.judging.moveRoom.useMutation();
@@ -341,6 +556,18 @@ export function JudgingControlPanel({
const revokeGuest = api.judging.revokeGuest.useMutation();
const removeJudge = api.judging.removeJudgeFromRoom.useMutation();
const data = query.data;
+ const announcementRoom =
+ announcementTarget && announcementTarget !== "global"
+ ? (data.rooms.find((room) => room.id === announcementTarget) ?? null)
+ : null;
+ const commsChannelId =
+ commsDraft.hackathonId === data.hackathon.id
+ ? commsDraft.channelId
+ : data.configuration.judgingCommsChannelId;
+
+ function setCommsChannelId(channelId: string | null) {
+ setCommsDraft({ channelId, hackathonId: data.hackathon.id });
+ }
function refresh() {
void query.refetch();
@@ -360,12 +587,47 @@ export function JudgingControlPanel({
? await rotate.mutateAsync({ roomId: room.id })
: await generate.mutateAsync({ roomId: room.id });
setQr({ roomName: room.name, value });
+ if (value.discordDelivery === "failed") {
+ toast.error(
+ mode === "rotate"
+ ? "QR rotated, but the Discord message failed."
+ : "QR generated, but the Discord message failed.",
+ );
+ }
refresh();
} catch (error) {
toast.error(error instanceof Error ? error.message : "QR update failed.");
}
}
+ async function updateCommsChannel(channelId: string | null) {
+ try {
+ const result = await saveComms.mutateAsync({
+ channelId,
+ hackathonId: data.hackathon.id,
+ });
+ setCommsChannelId(channelId);
+ if (result.failedRooms.length) {
+ toast.error(
+ `Channel saved, but ${result.failedRooms.length} room thread${result.failedRooms.length === 1 ? "" : "s"} need a retry.`,
+ );
+ } else {
+ toast.success(
+ channelId
+ ? "Judging communications connected."
+ : "Judging communications disconnected.",
+ );
+ }
+ refresh();
+ } catch (error) {
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Discord communications could not be saved.",
+ );
+ }
+ }
+
async function moveRoom(roomId: string, direction: "up" | "down") {
try {
await move.mutateAsync({ direction, roomId });
@@ -380,6 +642,12 @@ export function JudgingControlPanel({
const activeRoomIds = data.rooms
.filter((room) => room.archivedAt === null)
.map((room) => room.id);
+ const missingThreadCount = data.rooms.filter(
+ (room) =>
+ room.archivedAt === null &&
+ data.configuration.judgingCommsChannelId &&
+ !room.discordThreadId,
+ ).length;
const Root = embedded ? "div" : "main";
@@ -391,13 +659,23 @@ export function JudgingControlPanel({
{!embedded ? (
setEditing("new")}
- >
- Create room
-
+
+
setAnnouncementTarget("global")}
+ variant="outline"
+ >
+ Announce to
+ all rooms
+
+
setEditing("new")}
+ >
+ Create room
+
+
}
description="Provision physical rooms, distribute guest access, and watch the live judge roster."
eyebrow="Officer command center"
@@ -405,7 +683,15 @@ export function JudgingControlPanel({
title="Judging rooms"
/>
) : (
-
+
+
setAnnouncementTarget("global")}
+ variant="outline"
+ >
+ Announce to all
+ rooms
+
+
+
+
+
+
+
+
+
Judging communications
+
+ Give every room a quiet Discord thread for judge arrivals, guest
+ check-ins, QR delivery, and access changes.
+
+
+
+
+ {data.configuration.judgingCommsChannelId
+ ? missingThreadCount
+ ? `${missingThreadCount} thread${missingThreadCount === 1 ? "" : "s"} pending`
+ : "Connected"
+ : "Optional · disconnected"}
+
+
+
+
+
+
Root Discord channel
+
`#${channel.name}`}
+ getItemSearchValue={(channel) => `${channel.name} ${channel.id}`}
+ getItemValue={(channel) => channel.id}
+ inputPlaceholder="Search channels"
+ isDisabled={saveComms.isPending}
+ isLoading={channels.isLoading}
+ items={
+ channels.data ??
+ (commsChannelId
+ ? [{ id: commsChannelId, name: commsChannelId }]
+ : [])
+ }
+ onValueChange={setCommsChannelId}
+ renderItem={(channel) => (
+
+
+ {channel.name}
+
+ )}
+ triggerClassName="h-11 bg-background/70"
+ triggerId="judging-comms-channel"
+ value={commsChannelId}
+ />
+
+ Blade uses the configured{" "}
+ {data.discordGuildId ? "Knight Hacks server" : "Discord server"}{" "}
+ for this environment.
+
+
+ Use an organizer-only channel. Room threads inherit the channel's
+ readers, and mentions do not restrict who can view QR links or
+ guest notices.
+
+ {channels.isError ? (
+
+ Discord channels could not be loaded. Check the bot's access to
+ this server, then retry.
+
+ ) : null}
+
+
+ {data.configuration.judgingCommsChannelId ? (
+ void updateCommsChannel(null)}
+ variant="outline"
+ >
+ Disconnect
+
+ ) : null}
+ {missingThreadCount ? (
+ {
+ try {
+ const result = await retryThreads.mutateAsync({
+ hackathonId: data.hackathon.id,
+ });
+ if (result.failedRooms.length) {
+ toast.error(
+ `${result.failedRooms.length} room thread${result.failedRooms.length === 1 ? "" : "s"} still need a retry.`,
+ );
+ } else {
+ toast.success("Room threads are ready.");
+ }
+ refresh();
+ } catch (error) {
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Room threads could not be provisioned.",
+ );
+ }
+ }}
+ variant="secondary"
+ >
+ Retry {missingThreadCount}{" "}
+ {missingThreadCount === 1 ? "thread" : "threads"}
+
+ ) : null}
+ void updateCommsChannel(commsChannelId)}
+ >
+ {saveComms.isPending ? "Saving…" : "Save channel"}
+
+
+
+
+
+ {data.globalAnnouncement ? (
+
+
+
+
+
Announcement to all rooms
+
+ {data.globalAnnouncement.isUrgent ? "Urgent" : "Banner"}
+
+
+ {data.globalAnnouncement.includeGuests
+ ? "Members and guests"
+ : "Authenticated judges"}
+
+
+
+ {data.globalAnnouncement.message}
+
+
+
setAnnouncementTarget("global")}
+ size="sm"
+ variant="outline"
+ >
+ Manage
+
+
+
+ ) : null}
+
{!data.challenges.length ? (
@@ -509,6 +963,26 @@ export function JudgingControlPanel({
QR live
) : null}
+ {room.announcement ? (
+ <>
+
+ {room.announcement.isUrgent
+ ? "Urgent notice"
+ : "Notice live"}
+
+
+ {room.announcement.includeGuests
+ ? "Members and guests"
+ : "Authenticated judges"}
+
+ >
+ ) : null}
{archived ? (
Archived
) : null}
@@ -548,6 +1022,14 @@ export function JudgingControlPanel({
>
Edit
+
setAnnouncementTarget(room.id)}
+ size="sm"
+ variant="outline"
+ >
+ Announce
+
void showQr(room)}
@@ -557,16 +1039,80 @@ export function JudgingControlPanel({
{room.activeLinkId ? "View QR" : "Generate QR"}
+ {room.discordThreadId && data.discordGuildId ? (
+
+
+
+ Open thread
+
+
+ ) : null}
{room.activeLinkId ? (
<>
{
try {
- await revoke.mutateAsync({ roomId: room.id });
- toast.success(
- "Room QR and guest sessions revoked.",
+ const result = await sendQr.mutateAsync({
+ roomId: room.id,
+ });
+ if (result.discordDelivery === "delivered") {
+ toast.success(
+ "QR sent to current room judges.",
+ );
+ } else if (
+ result.discordDelivery === "failed"
+ ) {
+ toast.error(
+ "The QR is still active, but Discord delivery failed.",
+ );
+ } else {
+ toast.error(
+ "Connect a Discord channel to send this QR.",
+ );
+ }
+ refresh();
+ } catch (error) {
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "QR delivery failed.",
);
+ }
+ }}
+ size="sm"
+ title={
+ data.configuration.judgingCommsChannelId
+ ? "Send the current QR and mention assigned authenticated judges"
+ : "Connect a Discord channel to send this QR"
+ }
+ variant="secondary"
+ >
+ Send QR
+
+
{
+ try {
+ const result = await revoke.mutateAsync({
+ roomId: room.id,
+ });
+ if (result.discordDelivery === "failed") {
+ toast.error(
+ "Room access was revoked, but the Discord notice failed.",
+ );
+ } else {
+ toast.success(
+ "Room QR and guest sessions revoked.",
+ );
+ }
refresh();
} catch (error) {
toast.error(
@@ -673,24 +1219,35 @@ export function JudgingControlPanel({
aria-label={`${judge.kind === "guest" ? "Revoke" : "Remove"} ${judge.displayName}`}
onClick={async () => {
try {
+ let discordNoticeFailed = false;
if (
judge.kind === "guest" &&
judge.guestSessionId
) {
- await revokeGuest.mutateAsync({
- guestSessionId:
- judge.guestSessionId,
- });
+ const result =
+ await revokeGuest.mutateAsync({
+ guestSessionId:
+ judge.guestSessionId,
+ });
+ discordNoticeFailed =
+ result.discordDelivery ===
+ "failed";
} else {
await removeJudge.mutateAsync({
judgeId: judge.judgeId,
});
}
- toast.success(
- judge.kind === "guest"
- ? "Guest access revoked."
- : "Judge removed from room.",
- );
+ if (discordNoticeFailed) {
+ toast.error(
+ "Guest access was revoked, but the Discord notice failed.",
+ );
+ } else {
+ toast.success(
+ judge.kind === "guest"
+ ? "Guest access revoked."
+ : "Judge removed from room.",
+ );
+ }
refresh();
} catch (error) {
toast.error(
@@ -734,6 +1291,19 @@ export function JudgingControlPanel({
)}
+ {announcementTarget === "global" || announcementRoom ? (
+ setAnnouncementTarget(null)}
+ onSaved={refresh}
+ room={announcementRoom}
+ />
+ ) : null}
;
+ hackathonId?: string;
}) {
const pathname = usePathname();
const router = useRouter();
const searchParams = useSearchParams();
+ const utils = api.useUtils();
const joinRoom = api.judging.joinRoom.useMutation();
const leaveRoom = api.judging.leaveRoom.useMutation();
+ const announcementInput = { hackathonId };
async function selectRoom(roomId: string) {
try {
@@ -114,6 +119,10 @@ function MemberRoomSelector({
if (context.activeRoomId) {
await leaveRoom.mutateAsync({ roomId: context.activeRoomId });
}
+ utils.judging.listAnnouncements.setData(announcementInput, []);
+ await utils.judging.listAnnouncements
+ .invalidate(announcementInput)
+ .catch(() => undefined);
const next = new URLSearchParams(searchParams.toString());
next.delete("challenge");
next.delete("page");
@@ -124,10 +133,20 @@ function MemberRoomSelector({
return;
}
const room = await joinRoom.mutateAsync({ roomId });
+ utils.judging.listAnnouncements.setData(announcementInput, []);
+ await utils.judging.listAnnouncements
+ .invalidate(announcementInput)
+ .catch(() => undefined);
const next = new URLSearchParams(searchParams.toString());
next.set("challenge", room.challengeId);
next.delete("page");
- toast.success("Judging room selected.");
+ if (room.discordDelivery === "failed") {
+ toast.error(
+ "Room selected, but Discord could not open the room thread for you.",
+ );
+ } else {
+ toast.success("Judging room selected.");
+ }
router.replace(`${pathname}?${next.toString()}`);
router.refresh();
} catch (error) {
@@ -195,6 +214,7 @@ export function JudgeProjectWorkspace({
const [scoreProject, setScoreProject] = useState(null);
const context = judgingContext ?? {
activeRoomId: null,
+ announcements: [],
displayName: "",
hackathon: null,
isOfficer,
@@ -210,6 +230,8 @@ export function JudgeProjectWorkspace({
);
const heartbeatRoomId =
context.kind === "guest" ? context.roomId : activeRoom?.id;
+ const announcementHackathonId =
+ context.kind === "member" ? context.hackathon?.id : undefined;
function selectHackathon(hackathonId: string) {
const next = new URLSearchParams(searchParams.toString());
@@ -245,11 +267,18 @@ export function JudgeProjectWorkspace({
return (
+
{memberContext?.rooms.length ? (
-
+
) : null}
{isOfficer && hackathons.length ? (
diff --git a/apps/blade/src/app/_components/shared/authenticated-shell.tsx b/apps/blade/src/app/_components/shared/authenticated-shell.tsx
index 83611c65f..4253d4a29 100644
--- a/apps/blade/src/app/_components/shared/authenticated-shell.tsx
+++ b/apps/blade/src/app/_components/shared/authenticated-shell.tsx
@@ -35,12 +35,14 @@ export function AuthenticatedShell({
activeNavigation = "dashboard",
adminNavigation,
children,
+ displayName,
session,
sectionLabel,
}: {
activeNavigation?: NavigationItem;
adminNavigation?: AdminNavigationAccess;
children: ReactNode;
+ displayName?: string;
session: Session;
sectionLabel?: string;
}) {
@@ -112,7 +114,7 @@ export function AuthenticatedShell({
{currentSection}
- {session.user.name}
+ {displayName ?? session.user.name}
diff --git a/apps/blade/src/app/judge/layout.tsx b/apps/blade/src/app/judge/layout.tsx
index b3621b274..adf5ee013 100644
--- a/apps/blade/src/app/judge/layout.tsx
+++ b/apps/blade/src/app/judge/layout.tsx
@@ -32,6 +32,7 @@ export default async function JudgeLayout({
return (
diff --git a/apps/blade/src/tests/admin/authenticated-shell.test.tsx b/apps/blade/src/tests/admin/authenticated-shell.test.tsx
index 40bc12736..b5e12efea 100644
--- a/apps/blade/src/tests/admin/authenticated-shell.test.tsx
+++ b/apps/blade/src/tests/admin/authenticated-shell.test.tsx
@@ -182,4 +182,20 @@ describe("AuthenticatedShell", () => {
expect(html).toContain('aria-label="Blade home"');
expect(html).toContain('href="/"');
});
+
+ it("uses a resolved member name when a judging shell provides one", () => {
+ const html = renderToStaticMarkup(
+ createElement(AuthenticatedShell, {
+ children: createElement("main", null, "Judging workspace"),
+ displayName: "Dylan Vidal",
+ session: {
+ ...session,
+ user: { ...session.user, name: "dvidal1205" },
+ },
+ }),
+ );
+
+ expect(html).toContain("Dylan Vidal");
+ expect(html).not.toContain("dvidal1205");
+ });
});
diff --git a/apps/blade/src/tests/projects/guest-name-gate.test.tsx b/apps/blade/src/tests/projects/guest-name-gate.test.tsx
index 2ba57618f..0f7f2cb08 100644
--- a/apps/blade/src/tests/projects/guest-name-gate.test.tsx
+++ b/apps/blade/src/tests/projects/guest-name-gate.test.tsx
@@ -35,10 +35,10 @@ describe("guest judge name gate", () => {
"Please introduce yourself. Your name will be used for judging deliberation and identity verification. Your responses will NOT be shared.",
);
- await user.type(screen.getByLabelText("Name"), "A");
+ await user.type(screen.getByLabelText("Full Name"), "A");
expect(submit).toBeDisabled();
- await user.clear(screen.getByLabelText("Name"));
- await user.type(screen.getByLabelText("Name"), "Casey Sponsor");
+ await user.clear(screen.getByLabelText("Full Name"));
+ await user.type(screen.getByLabelText("Full Name"), "Casey Sponsor");
expect(submit).toBeEnabled();
await user.click(submit);
expect(completeGuest).toHaveBeenCalledWith({
diff --git a/apps/blade/src/tests/projects/judging-announcement-editor.test.tsx b/apps/blade/src/tests/projects/judging-announcement-editor.test.tsx
new file mode 100644
index 000000000..543234aab
--- /dev/null
+++ b/apps/blade/src/tests/projects/judging-announcement-editor.test.tsx
@@ -0,0 +1,240 @@
+/** @vitest-environment jsdom */
+import { render, screen, waitFor } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, it, vi } from "vitest";
+
+import type { RouterOutputs } from "@forge/api";
+
+import {
+ AnnouncementDialog,
+ JudgingControlPanel,
+} from "~/app/_components/judging/judging-control-panel";
+
+const mutations = vi.hoisted(() => ({
+ adminData: undefined as unknown,
+ clear: vi.fn(),
+ publish: vi.fn(),
+ refetch: vi.fn(),
+ replace: vi.fn(),
+}));
+
+vi.mock("next/navigation", () => ({
+ usePathname: () => "/admin/judging",
+ useSearchParams: () => new URLSearchParams(),
+}));
+
+vi.mock("~/app/_components/shared/route-transition-link", () => ({
+ useNavigationRouter: () => ({
+ refresh: vi.fn(),
+ replace: mutations.replace,
+ }),
+}));
+
+vi.mock("@forge/ui/use-media-query", () => ({
+ useMediaQuery: () => true,
+}));
+
+vi.mock("~/trpc/react", () => {
+ const idleMutation = () => ({ isPending: false, mutateAsync: vi.fn() });
+ return {
+ api: {
+ judging: {
+ archiveRoom: { useMutation: idleMutation },
+ clearAnnouncement: {
+ useMutation: () => ({
+ isPending: false,
+ mutateAsync: mutations.clear,
+ }),
+ },
+ createRoom: { useMutation: idleMutation },
+ generateRoomLink: { useMutation: idleMutation },
+ listAdmin: {
+ useQuery: () => ({
+ data: mutations.adminData,
+ isFetching: false,
+ refetch: mutations.refetch,
+ }),
+ },
+ listDiscordChannels: {
+ useQuery: () => ({ data: [], isError: false, isLoading: false }),
+ },
+ moveRoom: { useMutation: idleMutation },
+ provisionRoomThreads: { useMutation: idleMutation },
+ publishAnnouncement: {
+ useMutation: () => ({
+ isPending: false,
+ mutateAsync: mutations.publish,
+ }),
+ },
+ removeJudgeFromRoom: { useMutation: idleMutation },
+ revokeGuest: { useMutation: idleMutation },
+ revokeRoomLink: { useMutation: idleMutation },
+ rotateRoomLink: { useMutation: idleMutation },
+ sendRoomQr: { useMutation: idleMutation },
+ setCommsChannel: { useMutation: idleMutation },
+ updateRoom: { useMutation: idleMutation },
+ },
+ },
+ };
+});
+
+vi.stubGlobal(
+ "ResizeObserver",
+ class {
+ disconnect() {
+ return undefined;
+ }
+ observe() {
+ return undefined;
+ }
+ unobserve() {
+ return undefined;
+ }
+ },
+);
+
+type ControlData = RouterOutputs["judging"]["listAdmin"];
+type Announcement = NonNullable;
+
+const data = {
+ hackathon: { id: "00000000-0000-4000-8000-000000000001" },
+} as ControlData;
+
+const first = {
+ id: "00000000-0000-4000-8000-000000000002",
+ includeGuests: false,
+ isUrgent: false,
+ message: "Judging pauses at 4:30 PM.",
+ publishedAt: new Date("2026-09-06T14:00:00.000Z"),
+ roomId: null,
+} satisfies Announcement;
+
+const replacement = {
+ ...first,
+ id: "00000000-0000-4000-8000-000000000003",
+ message: "Judging now pauses at 4:45 PM.",
+} satisfies Announcement;
+
+describe("judging announcement editor", () => {
+ it("resets an open draft when polling replaces the announcement", async () => {
+ const user = userEvent.setup();
+ const view = render(
+ ,
+ );
+
+ const message = screen.getByRole("textbox", { name: "Message" });
+ await user.clear(message);
+ await user.type(message, "Unsaved local draft");
+ await user.click(
+ screen.getByRole("switch", { name: /Include guest judges/ }),
+ );
+ await user.click(
+ screen.getByRole("switch", { name: /Urgent announcement/ }),
+ );
+
+ view.rerender(
+ ,
+ );
+
+ await waitFor(() =>
+ expect(screen.getByRole("textbox", { name: "Message" })).toHaveValue(
+ replacement.message,
+ ),
+ );
+ expect(
+ screen.getByRole("switch", { name: /Include guest judges/ }),
+ ).not.toBeChecked();
+ expect(
+ screen.getByRole("switch", { name: /Urgent announcement/ }),
+ ).not.toBeChecked();
+ });
+
+ it("follows polling replacements for an open room editor", async () => {
+ const user = userEvent.setup();
+ const roomId = "00000000-0000-4000-8000-000000000004";
+ const roomAnnouncement = { ...first, roomId } satisfies Announcement;
+ const roomReplacement = {
+ ...replacement,
+ includeGuests: true,
+ isUrgent: true,
+ roomId,
+ } satisfies Announcement;
+ const room = {
+ activeLinkId: null,
+ announcement: roomAnnouncement,
+ archivedAt: null,
+ challengeId: "00000000-0000-4000-8000-000000000005",
+ challengeLabel: "Sponsor challenge",
+ discordThreadId: null,
+ id: roomId,
+ judges: [],
+ name: "Sponsor suite A",
+ } as ControlData["rooms"][number];
+ const initialData = {
+ challenges: [{ id: room.challengeId, label: room.challengeLabel }],
+ configuration: {
+ closedAt: null,
+ displayAllResults: false,
+ judgingCommsChannelId: null,
+ openedAt: null,
+ state: "draft",
+ },
+ discordGuildId: null,
+ globalAnnouncement: null,
+ hackathon: {
+ displayName: "Knight Hacks IX",
+ id: data.hackathon.id,
+ timezone: "America/New_York",
+ },
+ inventoryLockedAt: null,
+ rooms: [room],
+ rubric: [],
+ } as ControlData;
+ mutations.adminData = initialData;
+ const view = render(
+ ,
+ );
+
+ await user.click(screen.getByRole("button", { name: "Announce" }));
+ expect(screen.getByRole("textbox", { name: "Message" })).toHaveValue(
+ roomAnnouncement.message,
+ );
+ await user.clear(screen.getByRole("textbox", { name: "Message" }));
+ await user.type(
+ screen.getByRole("textbox", { name: "Message" }),
+ "Stale room draft",
+ );
+
+ mutations.adminData = {
+ ...initialData,
+ rooms: [{ ...room, announcement: roomReplacement }],
+ };
+ view.rerender(
+ ,
+ );
+
+ await waitFor(() =>
+ expect(screen.getByRole("textbox", { name: "Message" })).toHaveValue(
+ roomReplacement.message,
+ ),
+ );
+ expect(
+ screen.getByRole("switch", { name: /Include guest judges/ }),
+ ).toBeChecked();
+ expect(
+ screen.getByRole("switch", { name: /Urgent announcement/ }),
+ ).toBeChecked();
+ });
+});
diff --git a/apps/blade/src/tests/projects/judging-announcements.test.tsx b/apps/blade/src/tests/projects/judging-announcements.test.tsx
new file mode 100644
index 000000000..ef602a00f
--- /dev/null
+++ b/apps/blade/src/tests/projects/judging-announcements.test.tsx
@@ -0,0 +1,181 @@
+/** @vitest-environment jsdom */
+import { fireEvent, render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { renderToStaticMarkup } from "react-dom/server";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+import type { RouterOutputs } from "@forge/api";
+
+import { JudgingAnnouncements } from "~/app/_components/judging/judging-announcements";
+
+type Announcement = RouterOutputs["judging"]["listAnnouncements"][number];
+
+const query = vi.hoisted(() => ({
+ data: undefined as Announcement[] | undefined,
+ input: undefined as unknown,
+ options: undefined as unknown,
+}));
+
+vi.mock("~/trpc/react", () => ({
+ api: {
+ judging: {
+ listAnnouncements: {
+ useQuery: (input: unknown, options: unknown) => {
+ query.input = input;
+ query.options = options;
+ return { data: query.data };
+ },
+ },
+ },
+ },
+}));
+
+const standard = {
+ id: "00000000-0000-4000-8000-000000000001",
+ includeGuests: false,
+ isUrgent: false,
+ message: "Deliberation begins at 4:30 PM.",
+ publishedAt: new Date("2026-09-05T20:00:00.000Z"),
+ roomId: null,
+ roomName: null,
+} satisfies Announcement;
+
+const urgent = {
+ ...standard,
+ id: "00000000-0000-4000-8000-000000000002",
+ isUrgent: true,
+ message: "Pause judging and wait for an organizer.",
+ roomId: "00000000-0000-4000-8000-000000000003",
+ roomName: "Sponsor suite A",
+} satisfies Announcement;
+
+describe("judging announcements", () => {
+ beforeEach(() => {
+ query.data = undefined;
+ query.input = undefined;
+ query.options = undefined;
+ sessionStorage.clear();
+ });
+
+ it("renders a server-provided banner until the judge dismisses it", async () => {
+ const user = userEvent.setup();
+ render( );
+
+ expect(screen.getByText(standard.message)).toBeInTheDocument();
+ expect(screen.getByText("All judging rooms")).toBeInTheDocument();
+ await user.click(
+ screen.getByRole("button", {
+ name: "Dismiss announcement for All judging rooms",
+ }),
+ );
+ expect(screen.queryByText(standard.message)).not.toBeInTheDocument();
+ });
+
+ it("keeps a dismissed announcement hidden after a remount", async () => {
+ const user = userEvent.setup();
+ const first = render(
+ ,
+ );
+
+ await user.click(
+ screen.getByRole("button", {
+ name: "Dismiss announcement for All judging rooms",
+ }),
+ );
+ first.unmount();
+ render( );
+
+ expect(screen.queryByText(standard.message)).not.toBeInTheDocument();
+ });
+
+ it("keeps dismissals in memory when browser storage is unavailable", async () => {
+ const user = userEvent.setup();
+ const setItem = vi
+ .spyOn(Storage.prototype, "setItem")
+ .mockImplementation(() => {
+ throw new DOMException("Storage denied", "SecurityError");
+ });
+ render( );
+
+ await user.click(
+ screen.getByRole("button", {
+ name: "Dismiss announcement for All judging rooms",
+ }),
+ );
+
+ expect(screen.queryByText(standard.message)).not.toBeInTheDocument();
+ setItem.mockRestore();
+ });
+
+ it("polls in the background and renders replacements and clears", async () => {
+ const user = userEvent.setup();
+ const replacement = {
+ ...standard,
+ id: "00000000-0000-4000-8000-000000000004",
+ message: "Deliberation now begins at 4:45 PM.",
+ } satisfies Announcement;
+ const view = render(
+ ,
+ );
+
+ expect(query.input).toEqual({
+ hackathonId: "00000000-0000-4000-8000-000000000005",
+ });
+ expect(query.options).toMatchObject({
+ refetchInterval: 30_000,
+ refetchIntervalInBackground: true,
+ });
+ await user.click(
+ screen.getByRole("button", {
+ name: "Dismiss announcement for All judging rooms",
+ }),
+ );
+ query.data = [replacement];
+ view.rerender(
+ ,
+ );
+ expect(screen.getByText(replacement.message)).toBeInTheDocument();
+
+ query.data = [];
+ view.rerender(
+ ,
+ );
+ expect(screen.queryByText(replacement.message)).not.toBeInTheDocument();
+ });
+
+ it("requires an explicit acknowledgement for an urgent notice", async () => {
+ const user = userEvent.setup();
+ render( );
+
+ expect(
+ screen.getByRole("dialog", { name: "Urgent judging announcement" }),
+ ).toBeInTheDocument();
+ expect(screen.getByText("Sponsor suite A")).toBeInTheDocument();
+ expect(screen.queryByRole("button", { name: "Close" })).toBeNull();
+ await user.keyboard("{Escape}");
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
+ fireEvent.pointerDown(document.body);
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
+ await user.click(screen.getByRole("button", { name: "I understand" }));
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
+ });
+
+ it("includes urgent announcement content in server HTML", () => {
+ const html = renderToStaticMarkup(
+ ,
+ );
+
+ expect(html).toContain("Urgent judging announcement");
+ expect(html).toContain(urgent.message);
+ expect(html).toContain("Sponsor suite A");
+ });
+});
diff --git a/apps/blade/src/tests/projects/project-judge-privacy.test.tsx b/apps/blade/src/tests/projects/project-judge-privacy.test.tsx
index c7fb10f2e..4bc39e634 100644
--- a/apps/blade/src/tests/projects/project-judge-privacy.test.tsx
+++ b/apps/blade/src/tests/projects/project-judge-privacy.test.tsx
@@ -11,8 +11,13 @@ import { ProjectDetailDialog } from "~/app/_components/projects/project-detail-d
import { ProjectDirectory } from "~/app/_components/projects/project-directory";
const navigation = vi.hoisted(() => ({
+ announcementData: [] as RouterOutputs["judging"]["listAnnouncements"],
refresh: vi.fn(),
replace: vi.fn(),
+ setAnnouncements: vi.fn(),
+ invalidateAnnouncements: vi.fn(() => Promise.resolve()),
+ joinRoom: vi.fn(),
+ leaveRoom: vi.fn(),
saveEvaluation: vi.fn(),
}));
@@ -27,7 +32,33 @@ vi.mock("next/navigation", () => ({
vi.mock("~/trpc/react", () => ({
api: {
+ useUtils: () => ({
+ judging: {
+ listAnnouncements: {
+ invalidate: navigation.invalidateAnnouncements,
+ setData: navigation.setAnnouncements,
+ },
+ },
+ }),
judging: {
+ heartbeat: {
+ useMutation: () => ({ mutate: vi.fn() }),
+ },
+ joinRoom: {
+ useMutation: () => ({
+ isPending: false,
+ mutateAsync: navigation.joinRoom,
+ }),
+ },
+ leaveRoom: {
+ useMutation: () => ({
+ isPending: false,
+ mutateAsync: navigation.leaveRoom,
+ }),
+ },
+ listAnnouncements: {
+ useQuery: () => ({ data: navigation.announcementData }),
+ },
getProjectJudgingDetails: {
useQuery: () => ({ data: undefined, error: null, isLoading: false }),
},
@@ -232,6 +263,206 @@ describe("judge project directory", () => {
).toBeNull();
});
+ it("refreshes announcement scope after joining and leaving a room", async () => {
+ const user = userEvent.setup();
+ navigation.setAnnouncements.mockClear();
+ navigation.invalidateAnnouncements.mockClear();
+ navigation.joinRoom.mockResolvedValue({
+ challengeId: "challenge-acme",
+ discordDelivery: "not_configured",
+ });
+ navigation.leaveRoom.mockResolvedValue({ left: true });
+ const data = {
+ challenges: judgeProject.challenges,
+ hackathon: {
+ displayName: "Knight Hacks IX",
+ endDate: new Date("2026-10-01T00:00:00.000Z"),
+ id: judgeProject.hackathonId,
+ startDate: new Date("2026-09-01T00:00:00.000Z"),
+ },
+ page: 1,
+ pageSize: 25,
+ projects: [judgeProject],
+ totalCount: 1,
+ };
+ const room = {
+ challengeId: "challenge-acme",
+ challengeLabel: "Acme Challenge",
+ id: "00000000-0000-4000-8000-000000000006",
+ name: "Sponsor suite A",
+ };
+ const context = {
+ activeRoomId: null,
+ announcements: [],
+ discordUserId: "123456789012345678",
+ displayName: "Morgan Judge",
+ hackathon: data.hackathon,
+ isOfficer: false,
+ kind: "member" as const,
+ rooms: [room],
+ userId: "00000000-0000-4000-8000-000000000007",
+ };
+ const view = render(
+ ,
+ );
+
+ await user.selectOptions(
+ screen.getByRole("combobox", { name: "Judging room" }),
+ room.id,
+ );
+ await waitFor(() =>
+ expect(navigation.invalidateAnnouncements).toHaveBeenCalledTimes(1),
+ );
+ expect(navigation.setAnnouncements).toHaveBeenLastCalledWith(
+ { hackathonId: data.hackathon.id },
+ [],
+ );
+
+ view.rerender(
+ ,
+ );
+ await user.selectOptions(
+ screen.getByRole("combobox", { name: "Judging room" }),
+ "",
+ );
+ await waitFor(() =>
+ expect(navigation.invalidateAnnouncements).toHaveBeenCalledTimes(2),
+ );
+ expect(navigation.setAnnouncements).toHaveBeenCalledTimes(2);
+ });
+
+ it("clears the previous room announcement when its refetch fails", async () => {
+ const user = userEvent.setup();
+ navigation.replace.mockClear();
+ const oldRoomAnnouncement = {
+ id: "00000000-0000-4000-8000-000000000008",
+ includeGuests: false,
+ isUrgent: false,
+ message: "Private instructions for the previous room.",
+ publishedAt: new Date("2026-09-06T14:00:00.000Z"),
+ roomId: "00000000-0000-4000-8000-000000000009",
+ roomName: "Previous room",
+ } satisfies RouterOutputs["judging"]["listAnnouncements"][number];
+ navigation.announcementData = [oldRoomAnnouncement];
+ navigation.setAnnouncements.mockImplementation((_input, value) => {
+ navigation.announcementData = value as typeof navigation.announcementData;
+ });
+ navigation.invalidateAnnouncements.mockRejectedValueOnce(
+ new Error("Refetch failed"),
+ );
+ navigation.joinRoom.mockResolvedValue({
+ challengeId: "challenge-acme",
+ discordDelivery: "not_configured",
+ });
+ const data = {
+ challenges: judgeProject.challenges,
+ hackathon: {
+ displayName: "Knight Hacks IX",
+ endDate: new Date("2026-10-01T00:00:00.000Z"),
+ id: judgeProject.hackathonId,
+ startDate: new Date("2026-09-01T00:00:00.000Z"),
+ },
+ page: 1,
+ pageSize: 25,
+ projects: [judgeProject],
+ totalCount: 1,
+ };
+ const nextRoom = {
+ challengeId: "challenge-acme",
+ challengeLabel: "Acme Challenge",
+ id: "00000000-0000-4000-8000-000000000006",
+ name: "Sponsor suite A",
+ };
+ const context = {
+ activeRoomId: oldRoomAnnouncement.roomId,
+ announcements: [oldRoomAnnouncement],
+ discordUserId: "123456789012345678",
+ displayName: "Morgan Judge",
+ hackathon: data.hackathon,
+ isOfficer: false,
+ kind: "member" as const,
+ rooms: [nextRoom],
+ userId: "00000000-0000-4000-8000-000000000007",
+ };
+ const view = render(
+ ,
+ );
+
+ expect(screen.getByText(oldRoomAnnouncement.message)).toBeInTheDocument();
+ await user.selectOptions(
+ screen.getByRole("combobox", { name: "Judging room" }),
+ nextRoom.id,
+ );
+ await waitFor(() =>
+ expect(navigation.setAnnouncements).toHaveBeenCalledWith(
+ { hackathonId: data.hackathon.id },
+ [],
+ ),
+ );
+ view.rerender(
+ ,
+ );
+
+ expect(
+ screen.queryByText(oldRoomAnnouncement.message),
+ ).not.toBeInTheDocument();
+ expect(navigation.replace).toHaveBeenCalledWith(
+ "/judge/projects?challenge=challenge-acme",
+ );
+ });
+
it("renders member score controls and challenge completion", async () => {
navigation.replace.mockClear();
const user = userEvent.setup();
diff --git a/docs/DATABASE-USAGE.md b/docs/DATABASE-USAGE.md
index 729d4499d..485f07262 100644
--- a/docs/DATABASE-USAGE.md
+++ b/docs/DATABASE-USAGE.md
@@ -83,6 +83,16 @@ Notes:
- Before judging locks the inventory, a Devpost import replaces projects while preserving exact matching challenge records.
- After the first room QR is generated, ordinary imports add projects with unseen normalized Devpost URLs and leave existing records untouched. A separately confirmed replacement revokes active guest access and cannot remove a challenge assigned to an active room.
- Every imported project is associated with the hackathon's `General` challenge. Devpost opt-in prize columns produce the remaining challenge labels.
+- `HackathonJudgingConfiguration.judgingCommsChannelId` is the optional root
+ Discord text channel for room communications. Null keeps Discord delivery
+ off without changing judging behavior.
+- `JudgingRoom.discordThreadId` is the room's current thread under that root
+ channel. Changing or clearing the channel removes active Blade references but
+ leaves old Discord history intact.
+- `JudgingAnnouncement` stores the current and cleared history of operational
+ notices for one hackathon or judging room. A null `roomId` means every room.
+ `includeGuests` controls QR-guest visibility, while `isUrgent` selects the
+ blocking acknowledgement dialog instead of the persistent banner.
- `Project.deletedAt` provides officer-restorable soft deletion between imports; an authoritative re-import removes the prior inventory, including deleted rows.
- A guest session stores only the SHA-256 hash of its random browser credential. Shared development backups drop judging rooms, judge identities, links, guest sessions, and presence rows.
diff --git a/packages/api/src/judging-access.server.ts b/packages/api/src/judging-access.server.ts
index 1e80ccb7a..650f3b5a6 100644
--- a/packages/api/src/judging-access.server.ts
+++ b/packages/api/src/judging-access.server.ts
@@ -23,6 +23,7 @@ import {
import type { WriteDb } from "./utils/db";
import { env } from "./env";
+import { deliverJudgingRoomNotice } from "./utils/judging/discord-comms";
import { resolveJudgeAccess } from "./utils/judging/principal";
function judgingSecret() {
@@ -63,7 +64,12 @@ export function judgingRoomActivationUrl(linkId: string) {
}
export type RoomActivationResult =
- | { challengeId: string; kind: "member"; roomId: string }
+ | {
+ challengeId: string;
+ discordDelivery: "delivered" | "failed" | "not_configured" | "skipped";
+ kind: "member";
+ roomId: string;
+ }
| {
challengeId: string;
credential: string;
@@ -85,7 +91,7 @@ export async function activateJudgingRoom(input: {
session: input.session,
});
- return db.transaction(async (tx) => {
+ const result = await db.transaction(async (tx) => {
const [access] = await tx
.select({
challengeId: JudgingRoom.challengeId,
@@ -114,6 +120,33 @@ export async function activateJudgingRoom(input: {
userId: memberAccess.userId,
});
const now = new Date();
+ const [currentPresence] = await tx
+ .select({
+ id: JudgingRoomPresence.id,
+ roomId: JudgingRoomPresence.roomId,
+ })
+ .from(JudgingRoomPresence)
+ .where(
+ and(
+ eq(JudgingRoomPresence.judgeId, judge.id),
+ isNull(JudgingRoomPresence.leftAt),
+ ),
+ )
+ .limit(1);
+ if (currentPresence?.roomId === access.roomId) {
+ await tx
+ .update(JudgingRoomPresence)
+ .set({ lastSeenAt: now })
+ .where(eq(JudgingRoomPresence.id, currentPresence.id));
+ return {
+ challengeId: access.challengeId,
+ discordUserId: memberAccess.discordUserId,
+ displayName: memberAccess.displayName,
+ kind: "member" as const,
+ newlyJoined: false,
+ roomId: access.roomId,
+ };
+ }
await tx
.update(JudgingRoomPresence)
.set({ leftAt: now, leaveReason: "switched-room" })
@@ -130,7 +163,10 @@ export async function activateJudgingRoom(input: {
});
return {
challengeId: access.challengeId,
- kind: "member",
+ discordUserId: memberAccess.discordUserId,
+ displayName: memberAccess.displayName,
+ kind: "member" as const,
+ newlyJoined: true,
roomId: access.roomId,
};
}
@@ -145,10 +181,24 @@ export async function activateJudgingRoom(input: {
return {
challengeId: access.challengeId,
credential,
- kind: "guest",
+ kind: "guest" as const,
roomId: access.roomId,
};
});
+ if (result.kind === "guest") return result;
+ const discordDelivery = result.newlyJoined
+ ? await deliverJudgingRoomNotice(result.roomId, {
+ discordUserId: result.discordUserId,
+ kind: "member_joined",
+ memberName: result.displayName,
+ })
+ : ("skipped" as const);
+ return {
+ challengeId: result.challengeId,
+ discordDelivery,
+ kind: result.kind,
+ roomId: result.roomId,
+ };
}
export async function completeGuestJudge(input: {
@@ -162,7 +212,7 @@ export async function completeGuestJudge(input: {
if (!credential) throw new TRPCError({ code: "UNAUTHORIZED" });
const tokenHash = hashGuestJudgeCredential(credential);
- return db.transaction(async (tx) => {
+ const result = await db.transaction(async (tx) => {
const [record] = await tx
.select({
completedAt: GuestJudgeSession.completedAt,
@@ -192,7 +242,11 @@ export async function completeGuestJudge(input: {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
if (record.completedAt && record.judgeId) {
- return { judgeId: record.judgeId, roomId: record.roomId };
+ return {
+ judgeId: record.judgeId,
+ newlyCompleted: false,
+ roomId: record.roomId,
+ };
}
const [judge] = await tx
@@ -214,6 +268,21 @@ export async function completeGuestJudge(input: {
judgeId: judge.id,
roomId: record.roomId,
});
- return { judgeId: judge.id, roomId: record.roomId };
+ return {
+ judgeId: judge.id,
+ newlyCompleted: true,
+ roomId: record.roomId,
+ };
});
+ const discordDelivery = result.newlyCompleted
+ ? await deliverJudgingRoomNotice(result.roomId, {
+ guestName: input.displayName,
+ kind: "guest_joined",
+ })
+ : ("skipped" as const);
+ return {
+ discordDelivery,
+ judgeId: result.judgeId,
+ roomId: result.roomId,
+ };
}
diff --git a/packages/api/src/routers/judging-scores.ts b/packages/api/src/routers/judging-scores.ts
index 25de4e626..e066cf4d1 100644
--- a/packages/api/src/routers/judging-scores.ts
+++ b/packages/api/src/routers/judging-scores.ts
@@ -64,6 +64,7 @@ import {
evaluationMean,
resolveResponseVisibility,
} from "../utils/judging/scoring";
+import { resolveCurrentJudgeDisplayNames } from "../utils/member/display-name";
import { assertCanManageProjects } from "../utils/projects/access";
const workspaceInputSchema = judgingEvaluationSaveSchema.pick({
@@ -524,9 +525,11 @@ export const judgingScoresRouter = {
}),
db
.select({
+ displayName: Judge.displayName,
id: ProjectEvaluation.id,
- judgeDisplayName: Judge.displayName,
judgeId: ProjectEvaluation.judgeId,
+ kind: Judge.kind,
+ userId: Judge.userId,
})
.from(ProjectEvaluation)
.innerJoin(Judge, eq(Judge.id, ProjectEvaluation.judgeId))
@@ -538,9 +541,13 @@ export const judgingScoresRouter = {
),
),
]);
+ const currentEvaluations =
+ await resolveCurrentJudgeDisplayNames(evaluations);
const hasOwnEvaluation =
scope.judgeId !== null &&
- evaluations.some((evaluation) => evaluation.judgeId === scope.judgeId);
+ currentEvaluations.some(
+ (evaluation) => evaluation.judgeId === scope.judgeId,
+ );
const canRead = canReadScopedResult({
displayAllResultsToMembers: config?.displayAllResultsToMembers ?? false,
hasOwnEvaluation,
@@ -558,7 +565,9 @@ export const judgingScoresRouter = {
};
}
- const evaluationIds = evaluations.map((evaluation) => evaluation.id);
+ const evaluationIds = currentEvaluations.map(
+ (evaluation) => evaluation.id,
+ );
const [ratings, feedback, feedbackCount] = await Promise.all([
db
.select({
@@ -609,13 +618,15 @@ export const judgingScoresRouter = {
ratingValues.set(rating.evaluationId, values);
}
const judgeByEvaluation = new Map(
- evaluations.map((evaluation) => [
+ currentEvaluations.map((evaluation) => [
evaluation.id,
- evaluation.judgeDisplayName,
+ evaluation.displayName,
]),
);
const aggregate = aggregateEvaluationMeans(
- evaluations.map((evaluation) => ratingValues.get(evaluation.id) ?? []),
+ currentEvaluations.map(
+ (evaluation) => ratingValues.get(evaluation.id) ?? [],
+ ),
);
return {
...aggregate,
@@ -1394,14 +1405,16 @@ export const judgingScoresRouter = {
.input(judgingHackathonIdSchema)
.query(async ({ ctx, input }) => {
assertCanManageProjects(ctx);
- return db
+ const evaluations = await db
.select({
challengeLabel: ProjectChallenge.label,
+ displayName: Judge.displayName,
id: ProjectEvaluation.id,
- judgeDisplayName: Judge.displayName,
+ kind: Judge.kind,
projectTitle: Project.title,
revision: ProjectEvaluation.revision,
updatedAt: ProjectEvaluation.updatedAt,
+ userId: Judge.userId,
})
.from(ProjectEvaluation)
.innerJoin(Project, eq(Project.id, ProjectEvaluation.projectId))
@@ -1413,6 +1426,12 @@ export const judgingScoresRouter = {
.where(eq(ProjectEvaluation.hackathonId, input.hackathonId))
.orderBy(desc(ProjectEvaluation.updatedAt))
.limit(500);
+ return (await resolveCurrentJudgeDisplayNames(evaluations)).map(
+ ({ displayName, kind: _kind, userId: _userId, ...evaluation }) => ({
+ ...evaluation,
+ judgeDisplayName: displayName,
+ }),
+ );
}),
getEvaluationRevisions: permProcedure
@@ -1422,10 +1441,12 @@ export const judgingScoresRouter = {
const [evaluation] = await db
.select({
challengeLabel: ProjectChallenge.label,
+ displayName: Judge.displayName,
hackathonId: ProjectEvaluation.hackathonId,
id: ProjectEvaluation.id,
- judgeDisplayName: Judge.displayName,
+ kind: Judge.kind,
projectTitle: Project.title,
+ userId: Judge.userId,
})
.from(ProjectEvaluation)
.innerJoin(Project, eq(Project.id, ProjectEvaluation.projectId))
@@ -1437,6 +1458,10 @@ export const judgingScoresRouter = {
.where(eq(ProjectEvaluation.id, input.evaluationId))
.limit(1);
if (!evaluation) throw new TRPCError({ code: "NOT_FOUND" });
+ const [currentEvaluation] = await resolveCurrentJudgeDisplayNames([
+ evaluation,
+ ]);
+ if (!currentEvaluation) throw new TRPCError({ code: "NOT_FOUND" });
const [revisions, rubric] = await Promise.all([
db
.select({
@@ -1454,6 +1479,19 @@ export const judgingScoresRouter = {
.from(JudgingRubricItem)
.where(eq(JudgingRubricItem.hackathonId, evaluation.hackathonId)),
]);
- return { evaluation, revisions, rubric };
+ const {
+ displayName,
+ kind: _kind,
+ userId: _userId,
+ ...evaluationDetails
+ } = currentEvaluation;
+ return {
+ evaluation: {
+ ...evaluationDetails,
+ judgeDisplayName: displayName,
+ },
+ revisions,
+ rubric,
+ };
}),
} satisfies TRPCRouterRecord;
diff --git a/packages/api/src/routers/judging.ts b/packages/api/src/routers/judging.ts
index 10c009cc7..0d6f324cb 100644
--- a/packages/api/src/routers/judging.ts
+++ b/packages/api/src/routers/judging.ts
@@ -3,13 +3,26 @@ import QRCode from "qrcode";
import { z } from "zod";
import type { AuditActionKey } from "@forge/validators";
-import { and, asc, desc, eq, gte, isNull, lte, max, or, sql } from "@forge/db";
+import {
+ and,
+ asc,
+ desc,
+ eq,
+ gte,
+ inArray,
+ isNull,
+ lte,
+ max,
+ or,
+ sql,
+} from "@forge/db";
import { db } from "@forge/db/client";
import {
GuestJudgeSession,
Hackathon,
HackathonJudgingConfiguration,
Judge,
+ JudgingAnnouncement,
JudgingRoom,
JudgingRoomAccessLink,
JudgingRoomPresence,
@@ -18,6 +31,9 @@ import {
} from "@forge/db/schemas/knight-hacks";
import {
guestJudgeNameSchema,
+ judgingAnnouncementClearSchema,
+ judgingAnnouncementPublishSchema,
+ judgingCommsChannelSchema,
judgingGuestSessionIdSchema,
judgingHackathonIdSchema,
judgingJudgeIdSchema,
@@ -30,6 +46,10 @@ import {
import type { AuditActor } from "../utils/audit/service";
import type { WriteDb } from "../utils/db";
+import type {
+ GuestJudgePrincipal,
+ MemberJudgePrincipal,
+} from "../utils/judging/principal";
import {
completeGuestJudge,
judgingRoomActivationUrl,
@@ -45,7 +65,21 @@ import {
captureAdminAuditActor,
createAdminAuditEvent,
} from "../utils/audit/service";
+import {
+ deliverCurrentJudgingAnnouncement,
+ deliverJudgingRoomNotice,
+ ensureJudgingRoomThread,
+ judgingDiscordGuildId,
+ listJudgingDiscordChannels,
+ provisionJudgingRoomThreads,
+ serializeJudgingAnnouncement,
+ validateJudgingDiscordChannel,
+} from "../utils/judging/discord-comms";
import { resolveJudgeAccess } from "../utils/judging/principal";
+import {
+ resolveCurrentJudgeDisplayNames,
+ resolveMemberDisplayNamesByUserId,
+} from "../utils/member/display-name";
import { assertCanManageProjects } from "../utils/projects/access";
import { judgingScoresRouter } from "./judging-scores";
@@ -57,6 +91,28 @@ const RECENT_PRESENCE_WINDOW_MS = 15 * 60 * 1000;
const ACTIVE_ROOM_NAME_CONSTRAINT =
"knight_hacks_judging_room_active_name_unique";
+async function actorDisplayName(user: { id: string; name?: string | null }) {
+ const memberNames = await resolveMemberDisplayNamesByUserId([user.id]);
+ const memberName = memberNames.get(user.id)?.trim();
+ if (memberName?.length) return memberName;
+ const name = user.name?.trim();
+ return name?.length ? name : "An officer";
+}
+
+async function renderRoomQr(linkId: string) {
+ const url = judgingRoomActivationUrl(linkId);
+ return {
+ id: linkId,
+ qrCodeUrl: await QRCode.toDataURL(url, {
+ errorCorrectionLevel: "M",
+ margin: 1,
+ type: "image/png",
+ width: 512,
+ }),
+ url,
+ };
+}
+
function throwRoomNameConflict(error: unknown, roomName: string): never {
let current = error;
const visited = new Set();
@@ -149,6 +205,78 @@ async function listActiveRooms(hackathonId: string) {
.orderBy(asc(JudgingRoom.displayOrder), asc(JudgingRoom.name));
}
+async function listVisibleAnnouncements(input: {
+ hackathonId: string;
+ isGuest: boolean;
+ roomId: string | null;
+}) {
+ const rows = await db
+ .select({
+ id: JudgingAnnouncement.id,
+ includeGuests: JudgingAnnouncement.includeGuests,
+ isUrgent: JudgingAnnouncement.isUrgent,
+ message: JudgingAnnouncement.message,
+ publishedAt: JudgingAnnouncement.publishedAt,
+ roomId: JudgingAnnouncement.roomId,
+ roomName: JudgingRoom.name,
+ })
+ .from(JudgingAnnouncement)
+ .leftJoin(JudgingRoom, eq(JudgingRoom.id, JudgingAnnouncement.roomId))
+ .where(
+ and(
+ eq(JudgingAnnouncement.hackathonId, input.hackathonId),
+ isNull(JudgingAnnouncement.clearedAt),
+ input.isGuest ? eq(JudgingAnnouncement.includeGuests, true) : undefined,
+ input.roomId
+ ? or(
+ isNull(JudgingAnnouncement.roomId),
+ eq(JudgingAnnouncement.roomId, input.roomId),
+ )
+ : isNull(JudgingAnnouncement.roomId),
+ ),
+ );
+ return rows.sort((left, right) => {
+ if (left.roomId === null && right.roomId !== null) return -1;
+ if (left.roomId !== null && right.roomId === null) return 1;
+ return left.publishedAt.getTime() - right.publishedAt.getTime();
+ });
+}
+
+async function announcementsForPrincipal(
+ principal: GuestJudgePrincipal | MemberJudgePrincipal,
+ requestedHackathonId?: string,
+) {
+ if (principal.kind === "guest") {
+ return listVisibleAnnouncements({
+ hackathonId: principal.hackathonId,
+ isGuest: true,
+ roomId: principal.roomId,
+ });
+ }
+ const hackathon = await selectedHackathon(
+ requestedHackathonId,
+ principal.isOfficer,
+ );
+ if (!hackathon) return [];
+ const [presence] = await db
+ .select({ roomId: JudgingRoomPresence.roomId })
+ .from(Judge)
+ .innerJoin(JudgingRoomPresence, eq(JudgingRoomPresence.judgeId, Judge.id))
+ .where(
+ and(
+ eq(Judge.hackathonId, hackathon.id),
+ eq(Judge.userId, principal.userId),
+ isNull(JudgingRoomPresence.leftAt),
+ ),
+ )
+ .limit(1);
+ return listVisibleAnnouncements({
+ hackathonId: hackathon.id,
+ isGuest: false,
+ roomId: presence?.roomId ?? null,
+ });
+}
+
async function lockRoomAggregate(
tx: WriteDb,
roomId: string,
@@ -212,9 +340,9 @@ async function revokeRoomAccessWithDb(
),
)
.returning({ id: JudgingRoomAccessLink.id });
- if (!links.length) return { revoked: false };
+ if (!links.length) return { guestNames: [], revoked: false };
const activeLink = links[0];
- if (!activeLink) return { revoked: false };
+ if (!activeLink) return { guestNames: [], revoked: false };
const sessions = await tx
.update(GuestJudgeSession)
@@ -233,6 +361,14 @@ async function revokeRoomAccessWithDb(
const judgeIds = sessions
.map((session) => session.judgeId)
.filter((id): id is string => id !== null);
+ const guestNames = judgeIds.length
+ ? (
+ await tx
+ .select({ displayName: Judge.displayName })
+ .from(Judge)
+ .where(inArray(Judge.id, judgeIds))
+ ).map((judge) => judge.displayName)
+ : [];
for (const judgeId of judgeIds) {
await tx
.update(JudgingRoomPresence)
@@ -244,7 +380,7 @@ async function revokeRoomAccessWithDb(
),
);
}
- return { revoked: true };
+ return { guestNames, revoked: true };
}
async function joinMemberRoom(input: {
@@ -273,6 +409,26 @@ async function joinMemberRoom(input: {
userId: input.userId,
});
const now = new Date();
+ const [currentPresence] = await tx
+ .select({
+ id: JudgingRoomPresence.id,
+ roomId: JudgingRoomPresence.roomId,
+ })
+ .from(JudgingRoomPresence)
+ .where(
+ and(
+ eq(JudgingRoomPresence.judgeId, judge.id),
+ isNull(JudgingRoomPresence.leftAt),
+ ),
+ )
+ .limit(1);
+ if (currentPresence?.roomId === room.roomId) {
+ await tx
+ .update(JudgingRoomPresence)
+ .set({ lastSeenAt: now })
+ .where(eq(JudgingRoomPresence.id, currentPresence.id));
+ return { ...room, newlyJoined: false };
+ }
await tx
.update(JudgingRoomPresence)
.set({ leftAt: now, leaveReason: "switched-room" })
@@ -287,7 +443,7 @@ async function joinMemberRoom(input: {
judgeId: judge.id,
roomId: room.roomId,
});
- return room;
+ return { ...room, newlyJoined: true };
});
}
@@ -311,21 +467,24 @@ export const judgingRouter = createTRPCRouter({
return { ...access, ...room };
}
if (access.kind === "guest") {
- const [room] = await db
- .select({
- challengeLabel: ProjectChallenge.label,
- hackathonName: Hackathon.displayName,
- roomName: JudgingRoom.name,
- })
- .from(JudgingRoom)
- .innerJoin(Hackathon, eq(Hackathon.id, JudgingRoom.hackathonId))
- .innerJoin(
- ProjectChallenge,
- eq(ProjectChallenge.id, JudgingRoom.challengeId),
- )
- .where(eq(JudgingRoom.id, access.roomId))
- .limit(1);
- return { ...access, ...room };
+ const [[room], announcements] = await Promise.all([
+ db
+ .select({
+ challengeLabel: ProjectChallenge.label,
+ hackathonName: Hackathon.displayName,
+ roomName: JudgingRoom.name,
+ })
+ .from(JudgingRoom)
+ .innerJoin(Hackathon, eq(Hackathon.id, JudgingRoom.hackathonId))
+ .innerJoin(
+ ProjectChallenge,
+ eq(ProjectChallenge.id, JudgingRoom.challengeId),
+ )
+ .where(eq(JudgingRoom.id, access.roomId))
+ .limit(1),
+ announcementsForPrincipal(access),
+ ]);
+ return { ...access, ...room, announcements };
}
const hackathon = await selectedHackathon(
@@ -336,6 +495,7 @@ export const judgingRouter = createTRPCRouter({
return {
...access,
activeRoomId: null,
+ announcements: [],
hackathon: null,
rooms: [],
};
@@ -359,11 +519,22 @@ export const judgingRouter = createTRPCRouter({
return {
...access,
activeRoomId: presence?.roomId ?? null,
+ announcements: await listVisibleAnnouncements({
+ hackathonId: hackathon.id,
+ isGuest: false,
+ roomId: presence?.roomId ?? null,
+ }),
hackathon,
rooms,
};
}),
+ listAnnouncements: judgeProcedure
+ .input(contextInputSchema)
+ .query(({ ctx, input }) =>
+ announcementsForPrincipal(ctx.judgePrincipal, input.hackathonId),
+ ),
+
completeGuest: publicProcedure
.input(guestJudgeNameSchema)
.mutation(({ ctx, input }) =>
@@ -415,11 +586,19 @@ export const judgingRouter = createTRPCRouter({
if (ctx.judgePrincipal.kind !== "member") {
throw new TRPCError({ code: "FORBIDDEN" });
}
- return joinMemberRoom({
+ const room = await joinMemberRoom({
displayName: ctx.judgePrincipal.displayName,
roomId: input.roomId,
userId: ctx.judgePrincipal.userId,
});
+ const discordDelivery = room.newlyJoined
+ ? await deliverJudgingRoomNotice(room.roomId, {
+ discordUserId: ctx.judgePrincipal.discordUserId,
+ kind: "member_joined",
+ memberName: ctx.judgePrincipal.displayName,
+ })
+ : ("skipped" as const);
+ return { ...room, discordDelivery };
}),
leaveRoom: judgeProcedure
@@ -491,6 +670,306 @@ export const judgingRouter = createTRPCRouter({
return { updated: updated.length > 0 };
}),
+ listDiscordChannels: permProcedure.query(async ({ ctx }) => {
+ assertCanManageProjects(ctx);
+ return listJudgingDiscordChannels();
+ }),
+
+ setCommsChannel: permProcedure
+ .input(judgingCommsChannelSchema)
+ .mutation(async ({ ctx, input }) => {
+ assertCanManageProjects(ctx);
+ if (input.channelId) {
+ const validation = await validateJudgingDiscordChannel(input.channelId);
+ if (validation === "invalid") {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message:
+ "Choose a text channel from the configured Knight Hacks server.",
+ });
+ }
+ if (validation === "unavailable") {
+ throw new TRPCError({
+ code: "INTERNAL_SERVER_ERROR",
+ message:
+ "Discord could not verify that channel right now. Try again.",
+ });
+ }
+ }
+ const actor = await captureAdminAuditActor(ctx.session.user);
+ const changed = await db.transaction(async (tx) => {
+ const [hackathon] = await tx
+ .select({
+ displayName: Hackathon.displayName,
+ id: Hackathon.id,
+ })
+ .from(Hackathon)
+ .where(eq(Hackathon.id, input.hackathonId))
+ .for("update")
+ .limit(1);
+ if (!hackathon) throw new TRPCError({ code: "NOT_FOUND" });
+ const current = await tx.query.HackathonJudgingConfiguration.findFirst({
+ columns: { judgingCommsChannelId: true },
+ where: eq(
+ HackathonJudgingConfiguration.hackathonId,
+ input.hackathonId,
+ ),
+ });
+ const channelChanged =
+ (current?.judgingCommsChannelId ?? null) !== input.channelId;
+ await tx
+ .insert(HackathonJudgingConfiguration)
+ .values({
+ hackathonId: input.hackathonId,
+ judgingCommsChannelId: input.channelId,
+ })
+ .onConflictDoUpdate({
+ set: { judgingCommsChannelId: input.channelId },
+ target: HackathonJudgingConfiguration.hackathonId,
+ });
+ if (channelChanged) {
+ await tx
+ .update(JudgingRoom)
+ .set({ discordThreadId: null })
+ .where(
+ and(
+ eq(JudgingRoom.hackathonId, input.hackathonId),
+ isNull(JudgingRoom.archivedAt),
+ ),
+ );
+ }
+ await createAdminAuditEvent(
+ {
+ actionKey: "judging.comms.updated",
+ actor,
+ metadata: { channelId: input.channelId ?? "disconnected" },
+ subjects: [
+ {
+ relation: "primary",
+ targetId: hackathon.id,
+ targetLabel: hackathon.displayName,
+ targetType: "hackathon",
+ },
+ ],
+ },
+ tx,
+ );
+ return channelChanged;
+ });
+ if (!input.channelId) {
+ return {
+ channelChanged: changed,
+ failedRooms: [],
+ provisionedCount: 0,
+ };
+ }
+ return {
+ channelChanged: changed,
+ ...(await provisionJudgingRoomThreads(input.hackathonId)),
+ };
+ }),
+
+ provisionRoomThreads: permProcedure
+ .input(judgingHackathonIdSchema)
+ .mutation(async ({ ctx, input }) => {
+ assertCanManageProjects(ctx);
+ const actor = await captureAdminAuditActor(ctx.session.user);
+ const [hackathon] = await db
+ .select({ displayName: Hackathon.displayName, id: Hackathon.id })
+ .from(Hackathon)
+ .where(eq(Hackathon.id, input.hackathonId))
+ .limit(1);
+ if (!hackathon) throw new TRPCError({ code: "NOT_FOUND" });
+ const result = await provisionJudgingRoomThreads(input.hackathonId);
+ await createAdminAuditEvent({
+ actionKey: "judging.comms.threads_provisioned",
+ actor,
+ metadata: {
+ failedRoomCount: result.failedRooms.length,
+ provisionedCount: result.provisionedCount,
+ },
+ subjects: [
+ {
+ relation: "primary",
+ targetId: hackathon.id,
+ targetLabel: hackathon.displayName,
+ targetType: "hackathon",
+ },
+ ],
+ });
+ return result;
+ }),
+
+ publishAnnouncement: permProcedure
+ .input(judgingAnnouncementPublishSchema)
+ .mutation(async ({ ctx, input }) => {
+ assertCanManageProjects(ctx);
+ const actor = await captureAdminAuditActor(ctx.session.user);
+ return serializeJudgingAnnouncement(input.hackathonId, async () => {
+ const announcement = await db.transaction(async (tx) => {
+ const [hackathon] = await tx
+ .select({ displayName: Hackathon.displayName, id: Hackathon.id })
+ .from(Hackathon)
+ .where(eq(Hackathon.id, input.hackathonId))
+ .for("update")
+ .limit(1);
+ if (!hackathon) throw new TRPCError({ code: "NOT_FOUND" });
+
+ let roomName: string | null = null;
+ if (input.roomId) {
+ const [room] = await tx
+ .select({ name: JudgingRoom.name })
+ .from(JudgingRoom)
+ .where(
+ and(
+ eq(JudgingRoom.id, input.roomId),
+ eq(JudgingRoom.hackathonId, input.hackathonId),
+ isNull(JudgingRoom.archivedAt),
+ ),
+ )
+ .limit(1);
+ if (!room) throw new TRPCError({ code: "NOT_FOUND" });
+ roomName = room.name;
+ }
+
+ const now = new Date();
+ await tx
+ .update(JudgingAnnouncement)
+ .set({
+ clearedAt: now,
+ clearedByUserId: ctx.session.user.id,
+ })
+ .where(
+ and(
+ eq(JudgingAnnouncement.hackathonId, input.hackathonId),
+ input.roomId
+ ? eq(JudgingAnnouncement.roomId, input.roomId)
+ : isNull(JudgingAnnouncement.roomId),
+ isNull(JudgingAnnouncement.clearedAt),
+ ),
+ );
+ const [created] = await tx
+ .insert(JudgingAnnouncement)
+ .values({
+ hackathonId: input.hackathonId,
+ includeGuests: input.includeGuests,
+ isUrgent: input.isUrgent,
+ message: input.message,
+ publishedByUserId: ctx.session.user.id,
+ roomId: input.roomId,
+ })
+ .returning();
+ if (!created) throw new Error("Announcement was not published.");
+ await createAdminAuditEvent(
+ {
+ actionKey: "judging.announcement.published",
+ actor,
+ metadata: {
+ includeGuests: input.includeGuests,
+ isUrgent: input.isUrgent,
+ scope: input.roomId ? "room" : "global",
+ },
+ subjects: [
+ {
+ relation: "primary",
+ targetId: input.roomId ?? hackathon.id,
+ targetLabel: roomName ?? hackathon.displayName,
+ targetType: input.roomId ? "judging_room" : "hackathon",
+ },
+ ],
+ },
+ tx,
+ );
+ return created;
+ });
+ const discordDelivery = await deliverCurrentJudgingAnnouncement({
+ announcementId: announcement.id,
+ hackathonId: announcement.hackathonId,
+ isUrgent: announcement.isUrgent,
+ message: announcement.message,
+ roomId: announcement.roomId,
+ });
+ return { ...announcement, discordDelivery };
+ });
+ }),
+
+ clearAnnouncement: permProcedure
+ .input(judgingAnnouncementClearSchema)
+ .mutation(async ({ ctx, input }) => {
+ assertCanManageProjects(ctx);
+ const actor = await captureAdminAuditActor(ctx.session.user);
+ const [scope] = await db
+ .select({ hackathonId: JudgingAnnouncement.hackathonId })
+ .from(JudgingAnnouncement)
+ .where(eq(JudgingAnnouncement.id, input.announcementId))
+ .limit(1);
+ if (!scope) throw new TRPCError({ code: "NOT_FOUND" });
+ return serializeJudgingAnnouncement(scope.hackathonId, () =>
+ db.transaction(async (tx) => {
+ const [current] = await tx
+ .select({
+ hackathonId: JudgingAnnouncement.hackathonId,
+ hackathonName: Hackathon.displayName,
+ roomId: JudgingAnnouncement.roomId,
+ roomName: JudgingRoom.name,
+ })
+ .from(JudgingAnnouncement)
+ .innerJoin(
+ Hackathon,
+ eq(Hackathon.id, JudgingAnnouncement.hackathonId),
+ )
+ .leftJoin(
+ JudgingRoom,
+ eq(JudgingRoom.id, JudgingAnnouncement.roomId),
+ )
+ .where(
+ and(
+ eq(JudgingAnnouncement.id, input.announcementId),
+ isNull(JudgingAnnouncement.clearedAt),
+ ),
+ )
+ .limit(1);
+ if (!current) throw new TRPCError({ code: "NOT_FOUND" });
+ await tx
+ .select({ id: Hackathon.id })
+ .from(Hackathon)
+ .where(eq(Hackathon.id, current.hackathonId))
+ .for("update");
+ const [cleared] = await tx
+ .update(JudgingAnnouncement)
+ .set({
+ clearedAt: new Date(),
+ clearedByUserId: ctx.session.user.id,
+ })
+ .where(
+ and(
+ eq(JudgingAnnouncement.id, input.announcementId),
+ isNull(JudgingAnnouncement.clearedAt),
+ ),
+ )
+ .returning({ id: JudgingAnnouncement.id });
+ if (!cleared) throw new TRPCError({ code: "NOT_FOUND" });
+ await createAdminAuditEvent(
+ {
+ actionKey: "judging.announcement.cleared",
+ actor,
+ metadata: { scope: current.roomId ? "room" : "global" },
+ subjects: [
+ {
+ relation: "primary",
+ targetId: current.roomId ?? current.hackathonId,
+ targetLabel: current.roomName ?? current.hackathonName,
+ targetType: current.roomId ? "judging_room" : "hackathon",
+ },
+ ],
+ },
+ tx,
+ );
+ return { cleared: true };
+ }),
+ );
+ }),
+
listAdmin: permProcedure
.input(judgingHackathonIdSchema)
.query(async ({ ctx, input }) => {
@@ -514,6 +993,7 @@ export const judgingRouter = createTRPCRouter({
archivedAt: JudgingRoom.archivedAt,
challengeId: JudgingRoom.challengeId,
challengeLabel: ProjectChallenge.label,
+ discordThreadId: JudgingRoom.discordThreadId,
id: JudgingRoom.id,
name: JudgingRoom.name,
})
@@ -553,6 +1033,7 @@ export const judgingRouter = createTRPCRouter({
kind: Judge.kind,
lastSeenAt: JudgingRoomPresence.lastSeenAt,
roomId: JudgingRoomPresence.roomId,
+ userId: Judge.userId,
})
.from(JudgingRoomPresence)
.innerJoin(Judge, eq(Judge.id, JudgingRoomPresence.judgeId))
@@ -572,11 +1053,33 @@ export const judgingRouter = createTRPCRouter({
),
)
.orderBy(asc(Judge.displayName));
- const [configuration, rubric] = await Promise.all([
+ const currentRoster = (await resolveCurrentJudgeDisplayNames(roster))
+ .map(({ userId: _userId, ...judge }) => judge)
+ .sort((left, right) =>
+ left.displayName.localeCompare(right.displayName),
+ );
+ const [announcements, configuration, rubric] = await Promise.all([
+ db
+ .select({
+ id: JudgingAnnouncement.id,
+ includeGuests: JudgingAnnouncement.includeGuests,
+ isUrgent: JudgingAnnouncement.isUrgent,
+ message: JudgingAnnouncement.message,
+ publishedAt: JudgingAnnouncement.publishedAt,
+ roomId: JudgingAnnouncement.roomId,
+ })
+ .from(JudgingAnnouncement)
+ .where(
+ and(
+ eq(JudgingAnnouncement.hackathonId, input.hackathonId),
+ isNull(JudgingAnnouncement.clearedAt),
+ ),
+ ),
db.query.HackathonJudgingConfiguration.findFirst({
columns: {
closedAt: true,
displayAllResultsToMembers: true,
+ judgingCommsChannelId: true,
openedAt: true,
projectInventoryLockedAt: true,
state: true,
@@ -600,22 +1103,32 @@ export const judgingRouter = createTRPCRouter({
.where(eq(JudgingRubricItem.hackathonId, input.hackathonId))
.orderBy(asc(JudgingRubricItem.displayOrder)),
]);
+ const discordGuildId = await judgingDiscordGuildId();
return {
hackathon,
challenges,
configuration: {
closedAt: configuration?.closedAt ?? null,
displayAllResults: configuration?.displayAllResultsToMembers ?? false,
+ judgingCommsChannelId: configuration?.judgingCommsChannelId ?? null,
openedAt: configuration?.openedAt ?? null,
state: configuration?.state ?? ("draft" as const),
},
inventoryLockedAt: configuration?.projectInventoryLockedAt ?? null,
+ discordGuildId,
+ globalAnnouncement:
+ announcements.find((announcement) => announcement.roomId === null) ??
+ null,
rubric,
rooms: rooms.map((room) => ({
...room,
+ announcement:
+ announcements.find(
+ (announcement) => announcement.roomId === room.id,
+ ) ?? null,
activeLinkId:
links.find((link) => link.roomId === room.id)?.id ?? null,
- judges: roster.filter((judge) => judge.roomId === room.id),
+ judges: currentRoster.filter((judge) => judge.roomId === room.id),
})),
};
}),
@@ -626,7 +1139,7 @@ export const judgingRouter = createTRPCRouter({
assertCanManageProjects(ctx);
const actor = await captureAdminAuditActor(ctx.session.user);
try {
- return await db.transaction(async (tx) => {
+ const room = await db.transaction(async (tx) => {
const [hackathon] = await tx
.select({ id: Hackathon.id })
.from(Hackathon)
@@ -668,6 +1181,12 @@ export const judgingRouter = createTRPCRouter({
});
return room;
});
+ try {
+ await ensureJudgingRoomThread(room.id);
+ } catch {
+ // The room is durable. The officer can retry Discord provisioning.
+ }
+ return room;
} catch (error) {
throwRoomNameConflict(error, input.name);
}
@@ -679,7 +1198,7 @@ export const judgingRouter = createTRPCRouter({
assertCanManageProjects(ctx);
const actor = await captureAdminAuditActor(ctx.session.user);
try {
- return await db.transaction(async (tx) => {
+ const room = await db.transaction(async (tx) => {
const current = await lockRoomAggregate(tx, input.roomId, {
active: true,
});
@@ -736,6 +1255,12 @@ export const judgingRouter = createTRPCRouter({
});
return room;
});
+ try {
+ await ensureJudgingRoomThread(room.id);
+ } catch {
+ // The room edit is durable. The officer can retry Discord provisioning.
+ }
+ return room;
} catch (error) {
throwRoomNameConflict(error, input.name);
}
@@ -852,7 +1377,7 @@ export const judgingRouter = createTRPCRouter({
roomId: input.roomId,
roomName: room.name,
});
- return existing;
+ return { ...existing, created: false };
}
const [created] = await tx
.insert(JudgingRoomAccessLink)
@@ -892,27 +1417,71 @@ export const judgingRouter = createTRPCRouter({
roomId: input.roomId,
roomName: room.name,
});
- return created;
+ return { ...created, created: true };
});
- const url = judgingRoomActivationUrl(link.id);
+ const qr = await renderRoomQr(link.id);
+ const discordDelivery = link.created
+ ? await deliverJudgingRoomNotice(input.roomId, {
+ kind: "qr",
+ qrCodeUrl: qr.qrCodeUrl,
+ reason: "generated",
+ url: qr.url,
+ })
+ : ("skipped" as const);
return {
- id: link.id,
- qrCodeUrl: await QRCode.toDataURL(url, {
- errorCorrectionLevel: "M",
- margin: 1,
- type: "image/png",
- width: 512,
- }),
- url,
+ ...qr,
+ created: link.created,
+ discordDelivery,
};
}),
+ sendRoomQr: permProcedure
+ .input(judgingRoomIdSchema)
+ .mutation(async ({ ctx, input }) => {
+ assertCanManageProjects(ctx);
+ const actor = await captureAdminAuditActor(ctx.session.user);
+ const { link, room } = await db.transaction(async (tx) => {
+ const room = await lockRoomAggregate(tx, input.roomId, {
+ active: true,
+ });
+ const link = await tx.query.JudgingRoomAccessLink.findFirst({
+ columns: { id: true },
+ where: and(
+ eq(JudgingRoomAccessLink.roomId, room.id),
+ isNull(JudgingRoomAccessLink.revokedAt),
+ ),
+ });
+ if (!link) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Generate this room's QR before sending it.",
+ });
+ }
+ return { link, room };
+ });
+ const qr = await renderRoomQr(link.id);
+ const discordDelivery = await deliverJudgingRoomNotice(room.id, {
+ kind: "qr",
+ qrCodeUrl: qr.qrCodeUrl,
+ reason: "sent",
+ url: qr.url,
+ });
+ await writeJudgingAudit(db, {
+ actionKey: "judging.room_qr.sent",
+ actor,
+ metadata: { discordDelivery },
+ roomId: room.id,
+ roomName: room.name,
+ });
+ return { ...qr, discordDelivery };
+ }),
+
revokeRoomLink: permProcedure
.input(judgingRoomIdSchema)
.mutation(async ({ ctx, input }) => {
assertCanManageProjects(ctx);
const actor = await captureAdminAuditActor(ctx.session.user);
- return db.transaction(async (tx) => {
+ const result = await db.transaction(async (tx) => {
const room = await lockRoomAggregate(tx, input.roomId);
const result = await revokeRoomAccessWithDb(tx, {
reason: "officer-revoked",
@@ -929,6 +1498,14 @@ export const judgingRouter = createTRPCRouter({
}
return result;
});
+ const discordDelivery = result.revoked
+ ? await deliverJudgingRoomNotice(input.roomId, {
+ actorName: await actorDisplayName(ctx.session.user),
+ guestNames: result.guestNames,
+ kind: "room_link_revoked",
+ })
+ : ("skipped" as const);
+ return { ...result, discordDelivery };
}),
rotateRoomLink: permProcedure
@@ -962,11 +1539,16 @@ export const judgingRouter = createTRPCRouter({
});
return created;
});
- const url = judgingRoomActivationUrl(link.id);
+ const qr = await renderRoomQr(link.id);
+ const discordDelivery = await deliverJudgingRoomNotice(input.roomId, {
+ kind: "qr",
+ qrCodeUrl: qr.qrCodeUrl,
+ reason: "rotated",
+ url: qr.url,
+ });
return {
- id: link.id,
- qrCodeUrl: await QRCode.toDataURL(url, { margin: 1, width: 512 }),
- url,
+ ...qr,
+ discordDelivery,
};
}),
@@ -975,7 +1557,7 @@ export const judgingRouter = createTRPCRouter({
.mutation(async ({ ctx, input }) => {
assertCanManageProjects(ctx);
const actor = await captureAdminAuditActor(ctx.session.user);
- return db.transaction(async (tx) => {
+ const result = await db.transaction(async (tx) => {
const [target] = await tx
.select({
id: JudgingRoom.id,
@@ -1031,8 +1613,18 @@ export const judgingRouter = createTRPCRouter({
roomId: target.id,
roomName: target.name,
});
- return { revoked: true };
+ return {
+ guestName: target.judgeDisplayName,
+ revoked: true,
+ roomId: target.id,
+ };
+ });
+ const discordDelivery = await deliverJudgingRoomNotice(result.roomId, {
+ actorName: await actorDisplayName(ctx.session.user),
+ guestName: result.guestName,
+ kind: "guest_revoked",
});
+ return { revoked: result.revoked, discordDelivery };
}),
removeJudgeFromRoom: permProcedure
@@ -1044,6 +1636,8 @@ export const judgingRouter = createTRPCRouter({
const [target] = await tx
.select({
judgeDisplayName: Judge.displayName,
+ judgeKind: Judge.kind,
+ judgeUserId: Judge.userId,
presenceId: JudgingRoomPresence.id,
roomId: JudgingRoom.id,
roomName: JudgingRoom.name,
@@ -1063,6 +1657,18 @@ export const judgingRouter = createTRPCRouter({
.for("update")
.limit(1);
if (!target) throw new TRPCError({ code: "NOT_FOUND" });
+ const [currentTarget] = await resolveCurrentJudgeDisplayNames(
+ [
+ {
+ displayName: target.judgeDisplayName,
+ kind: target.judgeKind,
+ userId: target.judgeUserId,
+ },
+ ],
+ tx,
+ );
+ const judgeDisplayName =
+ currentTarget?.displayName ?? target.judgeDisplayName;
await tx
.update(JudgingRoomPresence)
.set({ leftAt: new Date(), leaveReason: "officer-removed" })
@@ -1071,7 +1677,7 @@ export const judgingRouter = createTRPCRouter({
actionKey: "judging.presence.removed",
actor,
metadata: {
- judgeDisplayName: target.judgeDisplayName,
+ judgeDisplayName,
judgeId: input.judgeId,
},
roomId: target.roomId,
diff --git a/packages/api/src/tests/integration/judging-access.test.ts b/packages/api/src/tests/integration/judging-access.test.ts
index dd2ae7c2b..4a65dce66 100644
--- a/packages/api/src/tests/integration/judging-access.test.ts
+++ b/packages/api/src/tests/integration/judging-access.test.ts
@@ -14,11 +14,14 @@ import { permissionBitstring } from "../support/permissions";
type DatabaseClient = typeof import("@forge/db/client").db;
type AuthSchemas = typeof import("@forge/db/schemas/auth");
type KnightHacksSchemas = typeof import("@forge/db/schemas/knight-hacks");
+type JudgingDiscordGateway =
+ import("../../utils/judging/discord-comms").JudgingDiscordGateway;
const OFFICER_USER = "10000000-0000-4000-8000-000000000901";
const OFFICER_ROLE = "20000000-0000-4000-8000-000000000901";
const MEMBER_USER = "10000000-0000-4000-8000-000000000902";
const MEMBER_ROLE = "20000000-0000-4000-8000-000000000902";
+const FALLBACK_USER = "10000000-0000-4000-8000-000000000903";
const HACKATHON = "30000000-0000-4000-8000-000000000901";
const GENERAL = "40000000-0000-4000-8000-000000000901";
const SPONSOR = "40000000-0000-4000-8000-000000000902";
@@ -29,6 +32,9 @@ const RATING_TWO = "60000000-0000-4000-8000-000000000902";
const PUBLIC_RESPONSE = "60000000-0000-4000-8000-000000000903";
const OPTIONAL_RESPONSE = "60000000-0000-4000-8000-000000000904";
const PRIVATE_RESPONSE = "60000000-0000-4000-8000-000000000905";
+const FAILED_ANNOUNCEMENT = "70000000-0000-4000-8000-000000000901";
+const CONCURRENT_ROOM = "80000000-0000-4000-8000-000000000901";
+const TRANSIENT_ROOM = "80000000-0000-4000-8000-000000000902";
function importCsv() {
const headers = [
@@ -84,6 +90,7 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
let schemas: KnightHacksSchemas;
let officer: Session;
let member: Session;
+ let fallbackMember: Session;
beforeAll(async () => {
disposable = await provisionDisposableDatabase("forge_api");
@@ -131,6 +138,47 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
session: { id: "judging-member-session", userAgent: "vitest" },
user: { id: MEMBER_USER, name: "Morgan Judge" },
} as unknown as Session;
+ await client.insert(authSchemas.User).values({
+ discordUserId: "fallback-discord-label",
+ id: FALLBACK_USER,
+ name: "Fallback Discord Label",
+ });
+ await client.insert(authSchemas.Permissions).values({
+ roleId: MEMBER_ROLE,
+ userId: FALLBACK_USER,
+ });
+ fallbackMember = {
+ session: { id: "judging-fallback-session", userAgent: "vitest" },
+ user: { id: FALLBACK_USER, name: "Fallback Discord Label" },
+ } as unknown as Session;
+ await client.insert(schemas.Member).values([
+ {
+ age: 22,
+ discordUser: "jofficer",
+ dob: "2004-01-01",
+ email: "jordan-officer@example.test",
+ firstName: "Jordan",
+ gradDate: "2027-05-01",
+ lastName: "Officer",
+ levelOfStudy: "Undergraduate University (3+ year)",
+ school: "University of Central Florida",
+ shirtSize: "M",
+ userId: OFFICER_USER,
+ },
+ {
+ age: 21,
+ discordUser: "mjudge",
+ dob: "2005-01-01",
+ email: "morgan-judge@example.test",
+ firstName: "Morgan",
+ gradDate: "2028-05-01",
+ lastName: "Judge",
+ levelOfStudy: "Undergraduate University (3+ year)",
+ school: "University of Central Florida",
+ shirtSize: "M",
+ userId: MEMBER_USER,
+ },
+ ]);
await client.insert(schemas.Hackathon).values({
displayName: "Knight Hacks IX",
@@ -335,6 +383,66 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
await guestCaller.judging.completeGuest({ displayName: "Casey Sponsor" });
+ await client
+ .insert(schemas.HackathonJudgingConfiguration)
+ .values({
+ hackathonId: HACKATHON,
+ judgingCommsChannelId: null,
+ })
+ .onConflictDoUpdate({
+ set: { judgingCommsChannelId: null },
+ target: schemas.HackathonJudgingConfiguration.hackathonId,
+ });
+ const membersOnly = await officerCaller.judging.publishAnnouncement({
+ hackathonId: HACKATHON,
+ message: "Authenticated judges only",
+ roomId: null,
+ });
+ expect(membersOnly.discordDelivery).toBe("not_configured");
+ const privateRoomNotice = await officerCaller.judging.publishAnnouncement({
+ hackathonId: HACKATHON,
+ message: "Room members only",
+ roomId: room.id,
+ });
+ await expect(guestCaller.judging.listAnnouncements({})).resolves.toEqual(
+ [],
+ );
+ const globalNotice = await officerCaller.judging.publishAnnouncement({
+ hackathonId: HACKATHON,
+ includeGuests: true,
+ isUrgent: true,
+ message: "All rooms pause judging",
+ roomId: null,
+ });
+ const roomNotice = await officerCaller.judging.publishAnnouncement({
+ hackathonId: HACKATHON,
+ includeGuests: true,
+ message: "Acme room stays put",
+ roomId: room.id,
+ });
+ expect(globalNotice.id).not.toBe(membersOnly.id);
+ expect(roomNotice.id).not.toBe(privateRoomNotice.id);
+ await expect(guestCaller.judging.listAnnouncements({})).resolves.toEqual([
+ expect.objectContaining({
+ id: globalNotice.id,
+ isUrgent: true,
+ roomId: null,
+ }),
+ expect.objectContaining({
+ id: roomNotice.id,
+ roomId: room.id,
+ roomName: "Acme room A",
+ }),
+ ]);
+ const memberCaller = await caller(member);
+ const fallbackCaller = await caller(fallbackMember);
+ await expect(
+ memberCaller.judging.publishAnnouncement({
+ hackathonId: HACKATHON,
+ message: "Unauthorized",
+ roomId: null,
+ }),
+ ).rejects.toMatchObject({ code: "FORBIDDEN" });
const scoped = await guestCaller.projects.listJudge({
challengeIds: [GENERAL],
direction: "asc",
@@ -360,6 +468,44 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
expect(activeRoom?.judges).toEqual([
expect.objectContaining({ displayName: "Casey Sponsor", kind: "guest" }),
]);
+ await memberCaller.judging.joinRoom({ roomId: room.id });
+ await fallbackCaller.judging.joinRoom({ roomId: secondRoom.id });
+ await client
+ .update(schemas.Judge)
+ .set({ displayName: "mjudge" })
+ .where(eq(schemas.Judge.userId, MEMBER_USER));
+ const controlWithMember = await officerCaller.judging.listAdmin({
+ hackathonId: HACKATHON,
+ });
+ expect(
+ controlWithMember.rooms.find((candidate) => candidate.id === room.id)
+ ?.judges,
+ ).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ displayName: "Morgan Judge",
+ kind: "member",
+ }),
+ ]),
+ );
+ expect(
+ controlWithMember.rooms.find(
+ (candidate) => candidate.id === secondRoom.id,
+ )?.judges,
+ ).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ displayName: "Fallback Discord Label",
+ kind: "member",
+ }),
+ ]),
+ );
+ await expect(
+ memberCaller.judging.listAnnouncements({ hackathonId: HACKATHON }),
+ ).resolves.toEqual([
+ expect.objectContaining({ id: globalNotice.id, roomId: null }),
+ expect.objectContaining({ id: roomNotice.id, roomId: room.id }),
+ ]);
await client
.update(schemas.JudgingRoomPresence)
.set({ lastSeenAt: new Date(Date.now() - 16 * 60 * 1000) })
@@ -443,8 +589,165 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
activeRoomId: generalRoom.id,
kind: "member",
});
+ if (memberContext.kind !== "member") {
+ throw new Error("Expected member judging context.");
+ }
+ expect(memberContext.announcements).toEqual([
+ expect.objectContaining({ id: globalNotice.id, roomId: null }),
+ ]);
+ await expect(
+ officerCaller.judging.clearAnnouncement({
+ announcementId: globalNotice.id,
+ }),
+ ).resolves.toEqual({ cleared: true });
+ await officerCaller.judging.clearAnnouncement({
+ announcementId: roomNotice.id,
+ });
+ await expect(
+ officerCaller.judging.clearAnnouncement({
+ announcementId: roomNotice.id,
+ }),
+ ).rejects.toMatchObject({ code: "NOT_FOUND" });
}, 60_000);
+ it("keeps a Blade announcement current when Discord delivery fails", async () => {
+ await client
+ .insert(schemas.HackathonJudgingConfiguration)
+ .values({
+ hackathonId: HACKATHON,
+ judgingCommsChannelId: "123456789012345678",
+ })
+ .onConflictDoUpdate({
+ set: { judgingCommsChannelId: "123456789012345678" },
+ target: schemas.HackathonJudgingConfiguration.hackathonId,
+ });
+ await client.insert(schemas.JudgingAnnouncement).values({
+ hackathonId: HACKATHON,
+ id: FAILED_ANNOUNCEMENT,
+ message: "This survives a Discord outage.",
+ publishedByUserId: OFFICER_USER,
+ });
+ const gateway: JudgingDiscordGateway = {
+ createRoomThread: vi.fn(() => Promise.reject(new Error("unavailable"))),
+ getChannel: vi.fn(() => Promise.reject(new Error("unavailable"))),
+ listTextChannels: vi.fn(() => Promise.resolve([])),
+ prepareRoomThread: vi.fn(() => Promise.reject(new Error("unavailable"))),
+ sendMessage: vi.fn(() => Promise.reject(new Error("unavailable"))),
+ };
+ const { deliverCurrentJudgingAnnouncement } =
+ await import("../../utils/judging/discord-comms");
+
+ await expect(
+ deliverCurrentJudgingAnnouncement(
+ {
+ announcementId: FAILED_ANNOUNCEMENT,
+ hackathonId: HACKATHON,
+ isUrgent: false,
+ message: "This survives a Discord outage.",
+ roomId: null,
+ },
+ gateway,
+ ),
+ ).resolves.toBe("failed");
+ const [current] = await client
+ .select({
+ clearedAt: schemas.JudgingAnnouncement.clearedAt,
+ id: schemas.JudgingAnnouncement.id,
+ })
+ .from(schemas.JudgingAnnouncement)
+ .where(eq(schemas.JudgingAnnouncement.id, FAILED_ANNOUNCEMENT));
+ expect(current).toEqual({ clearedAt: null, id: FAILED_ANNOUNCEMENT });
+ });
+
+ it("serializes concurrent room-thread provisioning", async () => {
+ await client
+ .insert(schemas.HackathonJudgingConfiguration)
+ .values({
+ hackathonId: HACKATHON,
+ judgingCommsChannelId: "123456789012345678",
+ })
+ .onConflictDoUpdate({
+ set: { judgingCommsChannelId: "123456789012345678" },
+ target: schemas.HackathonJudgingConfiguration.hackathonId,
+ });
+ await client.insert(schemas.JudgingRoom).values({
+ challengeId: SPONSOR,
+ displayOrder: 99,
+ hackathonId: HACKATHON,
+ id: CONCURRENT_ROOM,
+ name: "Concurrent room",
+ });
+ let releaseCreation: () => void = () => undefined;
+ const creationReleased = new Promise((resolve) => {
+ releaseCreation = resolve;
+ });
+ let creationStarted: () => void = () => undefined;
+ const creationDidStart = new Promise((resolve) => {
+ creationStarted = resolve;
+ });
+ const createRoomThread = vi.fn(async () => {
+ creationStarted();
+ await creationReleased;
+ return "223456789012345678";
+ });
+ const prepareRoomThread = vi.fn(() => Promise.resolve());
+ const gateway: JudgingDiscordGateway = {
+ createRoomThread,
+ getChannel: vi.fn(() => Promise.reject(new Error("unused"))),
+ listTextChannels: vi.fn(() => Promise.resolve([])),
+ prepareRoomThread,
+ sendMessage: vi.fn(() => Promise.resolve()),
+ };
+ const { ensureJudgingRoomThread } =
+ await import("../../utils/judging/discord-comms");
+
+ const first = ensureJudgingRoomThread(CONCURRENT_ROOM, gateway);
+ await creationDidStart;
+ const second = ensureJudgingRoomThread(CONCURRENT_ROOM, gateway);
+ releaseCreation();
+
+ await expect(Promise.all([first, second])).resolves.toEqual([
+ "223456789012345678",
+ "223456789012345678",
+ ]);
+ expect(createRoomThread).toHaveBeenCalledTimes(1);
+ expect(prepareRoomThread).toHaveBeenCalledWith({
+ channelId: "123456789012345678",
+ roomName: "Concurrent room",
+ threadId: "223456789012345678",
+ });
+ });
+
+ it("does not replace a room thread after a transient Discord failure", async () => {
+ await client.insert(schemas.JudgingRoom).values({
+ challengeId: SPONSOR,
+ discordThreadId: "223456789012345679",
+ displayOrder: 100,
+ hackathonId: HACKATHON,
+ id: TRANSIENT_ROOM,
+ name: "Transient room",
+ });
+ const createRoomThread = vi.fn(() => Promise.resolve("223456789012345680"));
+ const gateway: JudgingDiscordGateway = {
+ createRoomThread,
+ getChannel: vi.fn(() => Promise.reject(new Error("unused"))),
+ listTextChannels: vi.fn(() => Promise.resolve([])),
+ prepareRoomThread: vi.fn(() =>
+ Promise.reject(
+ Object.assign(new Error("rate limited"), { status: 429 }),
+ ),
+ ),
+ sendMessage: vi.fn(() => Promise.resolve()),
+ };
+ const { ensureJudgingRoomThread } =
+ await import("../../utils/judging/discord-comms");
+
+ await expect(
+ ensureJudgingRoomThread(TRANSIENT_ROOM, gateway),
+ ).rejects.toThrow("rate limited");
+ expect(createRoomThread).not.toHaveBeenCalled();
+ });
+
it("scores, edits, gates feedback, and keeps deliberation private", async () => {
const officerCaller = await caller(officer);
const memberCaller = await caller(member);
@@ -548,9 +851,14 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
revision: 2,
score: 3,
});
+ await client
+ .update(schemas.Judge)
+ .set({ displayName: "jofficer" })
+ .where(eq(schemas.Judge.userId, OFFICER_USER));
const revisions = await officerCaller.judging.getEvaluationRevisions({
evaluationId: first.evaluationId,
});
+ expect(revisions.evaluation.judgeDisplayName).toBe("Jordan Officer");
expect(revisions.revisions).toHaveLength(2);
expect(revisions.revisions[0]).toMatchObject({
responseAnswers: [
@@ -764,6 +1072,11 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
)
.every((item) => item.isPublic === false),
).toBe(true);
+ expect(
+ memberDetails.feedback.find(
+ (item) => item.value === "Updated member feedback",
+ )?.judgeDisplayName,
+ ).toBe("Jordan Officer");
const audit = await import("@forge/db/schemas/audit");
const [guestSession] = await client
@@ -831,13 +1144,24 @@ describe.runIf(canRunDatabaseTests())("judging room access", () => {
});
expect(evaluationList).toEqual(
expect.arrayContaining([
- expect.objectContaining({ id: first.evaluationId, revision: 2 }),
+ expect.objectContaining({
+ id: first.evaluationId,
+ judgeDisplayName: "Jordan Officer",
+ revision: 2,
+ }),
expect.objectContaining({
id: guestEvaluation.evaluationId,
revision: 1,
}),
]),
);
+ await expect(
+ officerCaller.judging.getEvaluationRevisions({
+ evaluationId: first.evaluationId,
+ }),
+ ).resolves.toMatchObject({
+ evaluation: { judgeDisplayName: "Jordan Officer" },
+ });
await expect(
memberCaller.judging.listEvaluationAudit({ hackathonId: HACKATHON }),
).rejects.toMatchObject({ code: "FORBIDDEN" });
diff --git a/packages/api/src/tests/judging/discord-comms.test.ts b/packages/api/src/tests/judging/discord-comms.test.ts
new file mode 100644
index 000000000..bfab3c13d
--- /dev/null
+++ b/packages/api/src/tests/judging/discord-comms.test.ts
@@ -0,0 +1,326 @@
+import type { APIChannel } from "discord-api-types/v10";
+import { ChannelType } from "discord-api-types/v10";
+import { describe, expect, it, vi } from "vitest";
+
+import type { JudgingDiscordGateway } from "../../utils/judging/discord-comms";
+import {
+ authorizedJudgingDiscordIds,
+ buildJudgingRoomMessage,
+ buildJudgingRoomMessages,
+ judgingDiscordNonce,
+ judgingRoomThreadName,
+ serializeJudgingAnnouncement,
+ validateJudgingDiscordChannel,
+ withJudgingRecipientMentions,
+} from "../../utils/judging/discord-comms";
+import { permissionKeysToBitstring } from "../../utils/roles/management";
+
+const memberOne = "111111111111111111";
+const memberTwo = "222222222222222222";
+
+vi.mock("@forge/utils/discord-config", () => ({
+ getKnightHacksGuildId: () => Promise.resolve("999999999999999999"),
+}));
+
+function validationGateway(
+ getChannel: JudgingDiscordGateway["getChannel"],
+): JudgingDiscordGateway {
+ return {
+ createRoomThread: vi.fn(() => Promise.reject(new Error("unused"))),
+ getChannel,
+ listTextChannels: vi.fn(() => Promise.resolve([])),
+ prepareRoomThread: vi.fn(() => Promise.reject(new Error("unused"))),
+ sendMessage: vi.fn(() => Promise.reject(new Error("unused"))),
+ };
+}
+
+describe("judging Discord messages", () => {
+ it("keeps only current judge and officer permission holders", () => {
+ const recipients = authorizedJudgingDiscordIds([
+ {
+ discordUserId: memberOne,
+ permissions: permissionKeysToBitstring(["IS_JUDGE"]),
+ },
+ {
+ discordUserId: memberTwo,
+ permissions: permissionKeysToBitstring(["IS_OFFICER"]),
+ },
+ {
+ discordUserId: "333333333333333333",
+ permissions: permissionKeysToBitstring(["READ_MEMBERS"]),
+ },
+ {
+ discordUserId: memberOne,
+ permissions: permissionKeysToBitstring(["READ_MEMBERS"]),
+ },
+ ]);
+
+ expect(recipients).toEqual([memberOne, memberTwo]);
+ });
+
+ it("limits inactive-hackathon recipients to officers", () => {
+ const recipients = authorizedJudgingDiscordIds(
+ [
+ {
+ discordUserId: memberOne,
+ permissions: permissionKeysToBitstring(["IS_JUDGE"]),
+ },
+ {
+ discordUserId: memberTwo,
+ permissions: permissionKeysToBitstring(["IS_OFFICER"]),
+ },
+ ],
+ false,
+ );
+
+ expect(recipients).toEqual([memberTwo]);
+ });
+
+ it("uses a Discord-safe nonce", () => {
+ const nonce = judgingDiscordNonce();
+ expect(nonce).toMatch(/^[A-Za-z0-9_-]+$/);
+ expect(nonce.length).toBeLessThanOrEqual(25);
+ expect(new Set(Array.from({ length: 50 }, judgingDiscordNonce)).size).toBe(
+ 50,
+ );
+ });
+
+ it("distinguishes invalid channels from Discord lookup failures", async () => {
+ const validChannel = {
+ guild_id: "999999999999999999",
+ id: "111111111111111111",
+ name: "judging-ops",
+ type: ChannelType.GuildText,
+ } as APIChannel;
+
+ await expect(
+ validateJudgingDiscordChannel(
+ validChannel.id,
+ validationGateway(() => Promise.resolve(validChannel)),
+ ),
+ ).resolves.toBe("valid");
+ await expect(
+ validateJudgingDiscordChannel(
+ validChannel.id,
+ validationGateway(() =>
+ Promise.resolve({
+ ...validChannel,
+ guild_id: "888888888888888888",
+ }),
+ ),
+ ),
+ ).resolves.toBe("invalid");
+ await expect(
+ validateJudgingDiscordChannel(
+ validChannel.id,
+ validationGateway(() => Promise.reject(new Error("Discord 503"))),
+ ),
+ ).resolves.toBe("unavailable");
+ });
+
+ it("serializes announcement work without overlapping operations", async () => {
+ const order: string[] = [];
+ let releaseFirst: () => void = () => undefined;
+ const firstReleased = new Promise((resolve) => {
+ releaseFirst = resolve;
+ });
+ let firstStarted: () => void = () => undefined;
+ const firstDidStart = new Promise((resolve) => {
+ firstStarted = resolve;
+ });
+
+ const first = serializeJudgingAnnouncement(
+ "hackathon-queue-test",
+ async () => {
+ order.push("first-start");
+ firstStarted();
+ await firstReleased;
+ order.push("first-end");
+ },
+ );
+ await firstDidStart;
+ const second = serializeJudgingAnnouncement("hackathon-queue-test", () => {
+ order.push("second");
+ return Promise.resolve();
+ });
+ await Promise.resolve();
+ expect(order).toEqual(["first-start"]);
+
+ releaseFirst();
+ await Promise.all([first, second]);
+ expect(order).toEqual(["first-start", "first-end", "second"]);
+ });
+
+ it("mentions only the member who newly selected the room", () => {
+ const message = buildJudgingRoomMessage({
+ notice: {
+ discordUserId: memberOne,
+ kind: "member_joined",
+ memberName: "Casey",
+ },
+ recipientIds: [memberTwo],
+ roomName: "Sponsor suite A",
+ });
+
+ expect(message.allowedMentions).toEqual({
+ parse: [],
+ users: [memberOne],
+ });
+ expect(message.content).toContain(`<@${memberOne}>`);
+ expect(message.content).not.toContain(memberTwo);
+ });
+
+ it("announces a named guest to every current member judge", () => {
+ const message = buildJudgingRoomMessage({
+ notice: {
+ guestName: "Taylor Sponsor",
+ kind: "guest_joined",
+ },
+ recipientIds: [memberTwo, memberOne],
+ roomName: "Sponsor suite A",
+ });
+
+ expect(message.allowedMentions.users).toEqual([memberOne, memberTwo]);
+ expect(message.content).toContain("Taylor Sponsor");
+ expect(message.content).toContain(`<@${memberOne}>`);
+ expect(message.content).toContain(`<@${memberTwo}>`);
+ });
+
+ it("builds an urgent room announcement with safe member mentions", () => {
+ const messages = buildJudgingRoomMessages({
+ notice: {
+ isUrgent: true,
+ kind: "announcement",
+ message: "Stop judging and return to @here.",
+ },
+ recipientIds: [memberTwo, memberOne],
+ roomName: "Sponsor suite A",
+ });
+ const message = messages[0];
+
+ expect(message?.allowedMentions.users).toEqual([memberOne, memberTwo]);
+ expect(message?.content).toContain("Urgent judging announcement");
+ expect(message?.content).toContain("Stop judging");
+ expect(message?.content).not.toContain("@here");
+ });
+
+ it("delivers every escaped character in a maximum-length announcement", () => {
+ const recipients = Array.from({ length: 101 }, (_, index) =>
+ (10_000_000_000_000_000_000n + BigInt(index)).toString(),
+ );
+ const messages = buildJudgingRoomMessages({
+ notice: {
+ isUrgent: true,
+ kind: "announcement",
+ message: "`".repeat(1000),
+ },
+ recipientIds: recipients,
+ roomName: "*".repeat(120),
+ });
+
+ expect(messages.length).toBeGreaterThan(1);
+ expect(messages.every((message) => message.content.length <= 2000)).toBe(
+ true,
+ );
+ expect(
+ messages
+ .map((message) => message.content)
+ .join("")
+ .match(/\\`/g),
+ ).toHaveLength(1000);
+ expect(
+ messages.flatMap((message) => message.allowedMentions.users).sort(),
+ ).toEqual(recipients.sort());
+ });
+
+ it("preserves a long notice and pings every large-audience recipient", () => {
+ const recipients = Array.from({ length: 101 }, (_, index) =>
+ (10_000_000_000_000_000_000n + BigInt(index)).toString(),
+ );
+ const notice = "A".repeat(1000);
+ const messages = withJudgingRecipientMentions(
+ {
+ allowedMentions: { parse: [], users: [] },
+ content: notice,
+ },
+ recipients,
+ );
+
+ expect(messages[0]?.content).toBe(notice);
+ expect(messages.every((message) => message.content.length <= 2000)).toBe(
+ true,
+ );
+ expect(
+ messages.every((message) => message.allowedMentions.users.length <= 100),
+ ).toBe(true);
+ expect(
+ messages.flatMap((message) => message.allowedMentions.users).sort(),
+ ).toEqual(recipients.sort());
+ });
+
+ it("attaches a large-audience QR only to the canonical message", () => {
+ const recipients = Array.from({ length: 101 }, (_, index) =>
+ (10_000_000_000_000_000_000n + BigInt(index)).toString(),
+ );
+ const messages = buildJudgingRoomMessages({
+ notice: {
+ kind: "qr",
+ qrCodeUrl: "data:image/png;base64,aGVsbG8=",
+ reason: "sent",
+ url: "https://example.test/judge/activate/signed",
+ },
+ recipientIds: recipients,
+ roomName: "Sponsor suite A",
+ });
+
+ expect(messages.filter((message) => message.file)).toHaveLength(1);
+ expect(
+ messages.flatMap((message) => message.allowedMentions.users).sort(),
+ ).toEqual(recipients.sort());
+ });
+
+ it("resends a QR with current recipients and a PNG attachment", () => {
+ const message = buildJudgingRoomMessage({
+ notice: {
+ kind: "qr",
+ qrCodeUrl: "data:image/png;base64,aGVsbG8=",
+ reason: "sent",
+ url: "http://localhost:3000/judge/activate/link?signature=signed",
+ },
+ recipientIds: [memberTwo],
+ roomName: "Sponsor suite A",
+ });
+
+ expect(message.allowedMentions.users).toEqual([memberTwo]);
+ expect(message.content).toContain("current guest judging QR");
+ expect(message.file).toMatchObject({ name: "sponsor-suite-a-qr.png" });
+ expect(message.file?.data.toString()).toBe("hello");
+ });
+
+ it("neutralizes names without widening allowed mentions", () => {
+ const message = buildJudgingRoomMessage({
+ notice: {
+ actorName: "@everyone <@333333333333333333>",
+ guestName: "@here",
+ kind: "guest_revoked",
+ },
+ recipientIds: [memberOne, "invalid"],
+ roomName: "**Room** <@&444444444444444444>",
+ });
+
+ expect(message.allowedMentions.users).toEqual([memberOne]);
+ expect(message.content).not.toContain("@everyone");
+ expect(message.content).not.toContain("@here");
+ expect(message.content).not.toContain("<@333333333333333333>");
+ expect(message.content).not.toContain("<@&444444444444444444>");
+ });
+
+ it("sanitizes and limits room thread names", () => {
+ const name = judgingRoomThreadName(
+ `Sponsor\nSuite @everyone <@123> ${"x".repeat(120)}`,
+ );
+ expect(name).not.toContain("\n");
+ expect(name).not.toContain("@everyone");
+ expect(name).toHaveLength(100);
+ });
+});
diff --git a/packages/api/src/tests/root/__snapshots__/api-surface.test.ts.snap b/packages/api/src/tests/root/__snapshots__/api-surface.test.ts.snap
index 733272668..b4061f80a 100644
--- a/packages/api/src/tests/root/__snapshots__/api-surface.test.ts.snap
+++ b/packages/api/src/tests/root/__snapshots__/api-surface.test.ts.snap
@@ -218,6 +218,7 @@ exports[`client-facing API surface > exposes exactly the documented procedure pa
"issues.updateTemplate",
"judging.addDeliberationProject",
"judging.archiveRoom",
+ "judging.clearAnnouncement",
"judging.completeGuest",
"judging.createDeliberationSection",
"judging.createRoom",
@@ -233,10 +234,14 @@ exports[`client-facing API surface > exposes exactly the documented procedure pa
"judging.joinRoom",
"judging.leaveRoom",
"judging.listAdmin",
+ "judging.listAnnouncements",
+ "judging.listDiscordChannels",
"judging.listEvaluationAudit",
"judging.listMyDeliberation",
"judging.listMySubmissions",
"judging.moveRoom",
+ "judging.provisionRoomThreads",
+ "judging.publishAnnouncement",
"judging.removeDeliberationProject",
"judging.removeJudgeFromRoom",
"judging.renameDeliberationSection",
@@ -247,6 +252,8 @@ exports[`client-facing API surface > exposes exactly the documented procedure pa
"judging.rotateRoomLink",
"judging.saveEvaluation",
"judging.saveRubric",
+ "judging.sendRoomQr",
+ "judging.setCommsChannel",
"judging.setDisplayAllResults",
"judging.setJudgingState",
"judging.updateRoom",
diff --git a/packages/api/src/utils/audit/coverage.ts b/packages/api/src/utils/audit/coverage.ts
index 8dc5a0b81..98ef129d7 100644
--- a/packages/api/src/utils/audit/coverage.ts
+++ b/packages/api/src/utils/audit/coverage.ts
@@ -97,13 +97,18 @@ export const AUDITED_ADMIN_PROCEDURES = [
"issues.update",
"issues.updateTemplate",
"judging.archiveRoom",
+ "judging.clearAnnouncement",
"judging.createRoom",
"judging.generateRoomLink",
"judging.moveRoom",
+ "judging.provisionRoomThreads",
+ "judging.publishAnnouncement",
"judging.removeJudgeFromRoom",
"judging.revokeGuest",
"judging.revokeRoomLink",
"judging.rotateRoomLink",
+ "judging.sendRoomQr",
+ "judging.setCommsChannel",
"judging.updateRoom",
"judging-scores.saveRubric",
"judging-scores.setDisplayAllResults",
@@ -221,6 +226,7 @@ export const EXCLUDED_ADMIN_PROCEDURES = [
"issues.listTeams",
"issues.listTemplates",
"judging.listAdmin",
+ "judging.listDiscordChannels",
"judging-scores.getEvaluationRevisions",
"judging-scores.listEvaluationAudit",
"member-admin.getAdminMember",
diff --git a/packages/api/src/utils/judging/discord-comms.ts b/packages/api/src/utils/judging/discord-comms.ts
new file mode 100644
index 000000000..7ceff6262
--- /dev/null
+++ b/packages/api/src/utils/judging/discord-comms.ts
@@ -0,0 +1,749 @@
+import { randomBytes } from "node:crypto";
+import type { APIChannel, APIMessage } from "discord-api-types/v10";
+import { ChannelType, Routes } from "discord-api-types/v10";
+
+import { and, eq, inArray, isNull } from "@forge/db";
+import { db } from "@forge/db/client";
+import { Permissions, Roles, User } from "@forge/db/schemas/auth";
+import {
+ Hackathon,
+ HackathonJudgingConfiguration,
+ Judge,
+ JudgingAnnouncement,
+ JudgingRoom,
+ JudgingRoomPresence,
+} from "@forge/db/schemas/knight-hacks";
+import * as discord from "@forge/utils/discord";
+import { getKnightHacksGuildId } from "@forge/utils/discord-config";
+
+import { roleHasPermission } from "../roles/management";
+
+const THREAD_NAME_LIMIT = 100;
+const MESSAGE_LIMIT = 2_000;
+const MENTION_BATCH_SIZE = 75;
+const DISCORD_SNOWFLAKE = /^\d{17,20}$/;
+const roomThreadQueues = new Map>();
+const announcementQueues = new Map>();
+
+class UnusableJudgingRoomThreadError extends Error {}
+
+async function runSerialized(
+ queues: Map>,
+ key: string,
+ operation: () => Promise,
+) {
+ const previous = queues.get(key) ?? Promise.resolve();
+ let release: () => void = () => undefined;
+ const current = new Promise((resolve) => {
+ release = resolve;
+ });
+ const tail = previous.catch(() => undefined).then(() => current);
+ queues.set(key, tail);
+ await previous.catch(() => undefined);
+ try {
+ return await operation();
+ } finally {
+ release();
+ if (queues.get(key) === tail) queues.delete(key);
+ }
+}
+
+function discordStatus(error: unknown) {
+ if (typeof error !== "object" || error === null) return null;
+ if ("status" in error && typeof error.status === "number") {
+ return error.status;
+ }
+ if ("statusCode" in error && typeof error.statusCode === "number") {
+ return error.statusCode;
+ }
+ return null;
+}
+
+function shouldReplaceRoomThread(error: unknown) {
+ if (error instanceof UnusableJudgingRoomThreadError) return true;
+ const status = discordStatus(error);
+ return status === 403 || status === 404;
+}
+
+export function judgingDiscordNonce() {
+ return randomBytes(16).toString("base64url");
+}
+
+export type JudgingDiscordDeliveryStatus =
+ | "delivered"
+ | "failed"
+ | "not_configured"
+ | "skipped";
+
+export type JudgingAnnouncementDeliveryStatus =
+ | JudgingDiscordDeliveryStatus
+ | "superseded";
+
+/** Serialize publication, clearing, and delivery without holding DB locks. */
+export function serializeJudgingAnnouncement(
+ hackathonId: string,
+ operation: () => Promise,
+) {
+ return runSerialized(announcementQueues, hackathonId, operation);
+}
+
+export interface JudgingDiscordMessage {
+ allowedMentions: {
+ parse: [];
+ users: string[];
+ };
+ content: string;
+ file?: {
+ data: Buffer;
+ name: string;
+ };
+}
+
+export interface JudgingDiscordGateway {
+ createRoomThread(input: {
+ channelId: string;
+ roomName: string;
+ starter: JudgingDiscordMessage;
+ }): Promise;
+ getChannel(channelId: string): Promise;
+ listTextChannels(guildId: string): Promise<{ id: string; name: string }[]>;
+ prepareRoomThread(input: {
+ channelId: string;
+ roomName: string;
+ threadId: string;
+ }): Promise;
+ sendMessage(input: {
+ message: JudgingDiscordMessage;
+ threadId: string;
+ }): Promise;
+}
+
+export type JudgingRoomNotice =
+ | { kind: "announcement"; message: string; isUrgent: boolean }
+ | { kind: "guest_joined"; guestName: string }
+ | { kind: "member_joined"; discordUserId: string; memberName: string }
+ | {
+ kind: "qr";
+ qrCodeUrl: string;
+ reason: "generated" | "rotated" | "sent";
+ url: string;
+ }
+ | { actorName: string; guestName: string; kind: "guest_revoked" }
+ | {
+ actorName: string;
+ guestNames: string[];
+ kind: "room_link_revoked";
+ };
+
+function neutralizeMentions(value: string) {
+ return value
+ .replace(/<@&?/g, "<@")
+ .replace(/])/g, "\\$1");
+}
+
+function escapedMarkdownTokens(value: string) {
+ return Array.from(neutralizeMentions(value), (character) =>
+ /[\\`*_[\]{}()~|>]/.test(character) ? `\\${character}` : character,
+ );
+}
+
+function singleLine(value: string) {
+ return value.replace(/\s+/g, " ").trim();
+}
+
+export function judgingRoomThreadName(roomName: string) {
+ const value = singleLine(neutralizeMentions(roomName)) || "Judging room";
+ return Array.from(value).slice(0, THREAD_NAME_LIMIT).join("");
+}
+
+function validRecipientIds(ids: readonly string[]) {
+ return [...new Set(ids.filter((id) => DISCORD_SNOWFLAKE.test(id)))].sort();
+}
+
+export function authorizedJudgingDiscordIds(
+ rows: readonly { discordUserId: string; permissions: string }[],
+ includeJudgeRole = true,
+) {
+ return validRecipientIds(
+ rows
+ .filter(
+ (row) =>
+ roleHasPermission(row.permissions, "IS_OFFICER") ||
+ (includeJudgeRole && roleHasPermission(row.permissions, "IS_JUDGE")),
+ )
+ .map((row) => row.discordUserId),
+ );
+}
+
+function mentionLine(ids: readonly string[]) {
+ return ids.length ? `${ids.map((id) => `<@${id}>`).join(" ")}\n` : "";
+}
+
+export function withJudgingRecipientMentions(
+ message: JudgingDiscordMessage,
+ ids: readonly string[],
+): JudgingDiscordMessage[] {
+ const recipientIds = validRecipientIds(ids);
+ const combined = `${mentionLine(recipientIds)}${message.content}`;
+ if (recipientIds.length <= 100 && combined.length <= MESSAGE_LIMIT) {
+ return [
+ {
+ ...message,
+ allowedMentions: { parse: [], users: recipientIds },
+ content: combined,
+ },
+ ];
+ }
+
+ const messages: JudgingDiscordMessage[] = [
+ { ...message, allowedMentions: { parse: [], users: [] } },
+ ];
+ for (
+ let index = 0;
+ index < recipientIds.length;
+ index += MENTION_BATCH_SIZE
+ ) {
+ const batch = recipientIds.slice(index, index + MENTION_BATCH_SIZE);
+ messages.push({
+ allowedMentions: { parse: [], users: batch },
+ content: `${mentionLine(batch)}Assigned judges: review the judging update above.`,
+ });
+ }
+ return messages;
+}
+
+export function buildJudgingAnnouncementMessages(input: {
+ isUrgent: boolean;
+ message: string;
+ recipientIds: readonly string[];
+ scopeLabel: string;
+}): JudgingDiscordMessage[] {
+ const title = input.isUrgent
+ ? "**Urgent judging announcement**"
+ : "**Judging announcement**";
+ const scope = `**${escapeMarkdown(input.scopeLabel)}**`;
+ const firstPrefix = `${title} for ${scope}\n`;
+ const continuationPrefix = `${title} for ${scope} (continued)\n`;
+ const tokens = escapedMarkdownTokens(input.message);
+ const contents: string[] = [];
+ let current = firstPrefix;
+
+ for (const token of tokens) {
+ if (current.length + token.length > MESSAGE_LIMIT) {
+ contents.push(current);
+ current = continuationPrefix;
+ }
+ current += token;
+ }
+ contents.push(current);
+
+ const [firstContent, ...continuations] = contents;
+ if (!firstContent) return [];
+ const firstMessages = withJudgingRecipientMentions(
+ {
+ allowedMentions: { parse: [], users: [] },
+ content: firstContent,
+ },
+ input.recipientIds,
+ );
+ const [canonical, ...mentionBatches] = firstMessages;
+ if (!canonical) return [];
+ return [
+ canonical,
+ ...continuations.map((content) => ({
+ allowedMentions: { parse: [] as [], users: [] },
+ content,
+ })),
+ ...mentionBatches,
+ ];
+}
+
+function qrBuffer(dataUrl: string) {
+ const match = /^data:image\/png;base64,([A-Za-z0-9+/=]+)$/.exec(dataUrl);
+ if (!match?.[1]) throw new Error("The judging QR image is invalid.");
+ return Buffer.from(match[1], "base64");
+}
+
+function qrFilename(roomName: string) {
+ const stem = singleLine(roomName)
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, "-")
+ .replace(/^-|-$/g, "")
+ .slice(0, 60);
+ return `${stem || "judging-room"}-qr.png`;
+}
+
+export function buildJudgingRoomMessage(input: {
+ notice: Exclude;
+ recipientIds: readonly string[];
+ roomName: string;
+}): JudgingDiscordMessage {
+ const recipientIds =
+ input.notice.kind === "member_joined"
+ ? validRecipientIds([input.notice.discordUserId])
+ : validRecipientIds(input.recipientIds);
+ const room = escapeMarkdown(input.roomName);
+ let content: string;
+ let file: JudgingDiscordMessage["file"];
+
+ switch (input.notice.kind) {
+ case "member_joined":
+ content = `${mentionLine(recipientIds)}${escapeMarkdown(input.notice.memberName)} joined **${room}** in Blade.`;
+ break;
+ case "guest_joined":
+ content = `${mentionLine(recipientIds)}**${escapeMarkdown(input.notice.guestName)}** signed in as a guest judge for **${room}**.`;
+ break;
+ case "qr": {
+ const action = {
+ generated: "A guest judging QR is ready",
+ rotated: "Guest judging access was rotated",
+ sent: "Here is the current guest judging QR",
+ }[input.notice.reason];
+ content = `${mentionLine(recipientIds)}${action} for **${room}**.\n<${input.notice.url}>`;
+ file = {
+ data: qrBuffer(input.notice.qrCodeUrl),
+ name: qrFilename(input.roomName),
+ };
+ break;
+ }
+ case "guest_revoked":
+ content = `${mentionLine(recipientIds)}**${escapeMarkdown(input.notice.actorName)}** revoked guest access for **${escapeMarkdown(input.notice.guestName)}** in **${room}**.`;
+ break;
+ case "room_link_revoked": {
+ const guests = input.notice.guestNames
+ .slice(0, 20)
+ .map((name) => `**${escapeMarkdown(name)}**`)
+ .join(", ");
+ const suffix = guests
+ ? ` Affected guests: ${guests}.`
+ : " No active named guest sessions were present.";
+ content = `${mentionLine(recipientIds)}**${escapeMarkdown(input.notice.actorName)}** revoked the guest judging QR for **${room}**.${suffix}`;
+ break;
+ }
+ }
+
+ if (content.length > MESSAGE_LIMIT) {
+ content = `${content.slice(0, MESSAGE_LIMIT - 1).trimEnd()}…`;
+ }
+ return {
+ allowedMentions: { parse: [], users: recipientIds },
+ content,
+ ...(file && { file }),
+ };
+}
+
+export function buildJudgingRoomMessages(input: {
+ notice: JudgingRoomNotice;
+ recipientIds: readonly string[];
+ roomName: string;
+}) {
+ const { notice } = input;
+ if (notice.kind === "announcement") {
+ return buildJudgingAnnouncementMessages({
+ isUrgent: notice.isUrgent,
+ message: notice.message,
+ recipientIds: input.recipientIds,
+ scopeLabel: input.roomName,
+ });
+ }
+ if (notice.kind === "member_joined") {
+ return [buildJudgingRoomMessage({ ...input, notice })];
+ }
+ const canonical = buildJudgingRoomMessage({
+ ...input,
+ notice,
+ recipientIds: [],
+ });
+ return withJudgingRecipientMentions(canonical, input.recipientIds);
+}
+
+export const liveJudgingDiscordGateway: JudgingDiscordGateway = {
+ async createRoomThread({ channelId, roomName, starter }) {
+ const created = (await discord.api.post(Routes.channelMessages(channelId), {
+ body: {
+ allowed_mentions: starter.allowedMentions,
+ content: starter.content,
+ enforce_nonce: true,
+ nonce: judgingDiscordNonce(),
+ },
+ })) as APIMessage;
+ const thread = (await discord.api.post(
+ Routes.threads(channelId, created.id),
+ {
+ body: { name: judgingRoomThreadName(roomName) },
+ },
+ )) as APIChannel;
+ return thread.id;
+ },
+ async getChannel(channelId) {
+ return (await discord.api.get(Routes.channel(channelId))) as APIChannel;
+ },
+ async listTextChannels(guildId) {
+ const channels = (await discord.api.get(
+ Routes.guildChannels(guildId),
+ )) as APIChannel[];
+ return channels
+ .filter(
+ (channel) =>
+ "guild_id" in channel &&
+ channel.guild_id === guildId &&
+ "name" in channel &&
+ (channel.type === ChannelType.GuildText ||
+ channel.type === ChannelType.GuildAnnouncement),
+ )
+ .map((channel) => ({ id: channel.id, name: channel.name ?? channel.id }))
+ .sort((left, right) => left.name.localeCompare(right.name));
+ },
+ async prepareRoomThread({ channelId, roomName, threadId }) {
+ const channel = (await discord.api.get(
+ Routes.channel(threadId),
+ )) as APIChannel;
+ if (
+ channel.type !== ChannelType.PublicThread &&
+ channel.type !== ChannelType.AnnouncementThread
+ ) {
+ throw new UnusableJudgingRoomThreadError(
+ "The saved judging room thread is not usable.",
+ );
+ }
+ if (!("parent_id" in channel) || channel.parent_id !== channelId) {
+ throw new UnusableJudgingRoomThreadError(
+ "The saved judging room thread has the wrong parent.",
+ );
+ }
+ await discord.api.patch(Routes.channel(threadId), {
+ body: { archived: false, name: judgingRoomThreadName(roomName) },
+ });
+ },
+ async sendMessage({ message, threadId }) {
+ await discord.api.post(Routes.channelMessages(threadId), {
+ body: {
+ allowed_mentions: message.allowedMentions,
+ content: message.content,
+ enforce_nonce: true,
+ nonce: judgingDiscordNonce(),
+ },
+ ...(message.file && {
+ files: [{ data: message.file.data, name: message.file.name }],
+ }),
+ });
+ },
+};
+
+function starterMessage(roomName: string): JudgingDiscordMessage {
+ return {
+ allowedMentions: { parse: [], users: [] },
+ content: `Judging communications for **${escapeMarkdown(roomName)}**. Blade posts room arrivals and guest-access updates here.`,
+ };
+}
+
+async function roomDiscordTarget(roomId: string) {
+ const [target] = await db
+ .select({
+ channelId: HackathonJudgingConfiguration.judgingCommsChannelId,
+ roomName: JudgingRoom.name,
+ threadId: JudgingRoom.discordThreadId,
+ })
+ .from(JudgingRoom)
+ .leftJoin(
+ HackathonJudgingConfiguration,
+ eq(HackathonJudgingConfiguration.hackathonId, JudgingRoom.hackathonId),
+ )
+ .where(and(eq(JudgingRoom.id, roomId), isNull(JudgingRoom.archivedAt)))
+ .limit(1);
+ return target ?? null;
+}
+
+async function currentAuthorizedDiscordIds(input: {
+ includeJudgeRole: boolean;
+ userIds?: readonly string[];
+}) {
+ if (input.userIds?.length === 0) return [];
+ const rows = await db
+ .select({
+ discordUserId: User.discordUserId,
+ permissions: Roles.permissions,
+ })
+ .from(Permissions)
+ .innerJoin(Roles, eq(Roles.id, Permissions.roleId))
+ .innerJoin(User, eq(User.id, Permissions.userId))
+ .where(
+ input.userIds
+ ? inArray(Permissions.userId, [...input.userIds])
+ : undefined,
+ );
+ return authorizedJudgingDiscordIds(rows, input.includeJudgeRole);
+}
+
+function isHackathonActive(startDate: Date, endDate: Date) {
+ const now = Date.now();
+ return startDate.getTime() <= now && endDate.getTime() >= now;
+}
+
+async function currentMemberDiscordIds(roomId: string) {
+ const rows = await db
+ .select({
+ endDate: Hackathon.endDate,
+ startDate: Hackathon.startDate,
+ userId: Judge.userId,
+ })
+ .from(JudgingRoomPresence)
+ .innerJoin(Judge, eq(Judge.id, JudgingRoomPresence.judgeId))
+ .innerJoin(JudgingRoom, eq(JudgingRoom.id, JudgingRoomPresence.roomId))
+ .innerJoin(Hackathon, eq(Hackathon.id, JudgingRoom.hackathonId))
+ .where(
+ and(
+ eq(JudgingRoomPresence.roomId, roomId),
+ eq(Judge.kind, "member"),
+ isNull(JudgingRoomPresence.leftAt),
+ ),
+ );
+ return currentAuthorizedDiscordIds({
+ includeJudgeRole:
+ !!rows[0] && isHackathonActive(rows[0].startDate, rows[0].endDate),
+ userIds: rows
+ .map((row) => row.userId)
+ .filter((id): id is string => id !== null),
+ });
+}
+
+async function hackathonMemberDiscordIds(hackathonId: string) {
+ const [hackathon] = await db
+ .select({ endDate: Hackathon.endDate, startDate: Hackathon.startDate })
+ .from(Hackathon)
+ .where(eq(Hackathon.id, hackathonId))
+ .limit(1);
+ if (!hackathon) return [];
+ return currentAuthorizedDiscordIds({
+ includeJudgeRole: isHackathonActive(hackathon.startDate, hackathon.endDate),
+ });
+}
+
+export async function ensureJudgingRoomThread(
+ roomId: string,
+ gateway: JudgingDiscordGateway = liveJudgingDiscordGateway,
+) {
+ return runSerialized(roomThreadQueues, roomId, async () => {
+ const target = await roomDiscordTarget(roomId);
+ if (!target?.channelId) return null;
+
+ if (target.threadId) {
+ try {
+ await gateway.prepareRoomThread({
+ channelId: target.channelId,
+ roomName: target.roomName,
+ threadId: target.threadId,
+ });
+ return target.threadId;
+ } catch (error) {
+ if (!shouldReplaceRoomThread(error)) throw error;
+ }
+ }
+ const threadId = await gateway.createRoomThread({
+ channelId: target.channelId,
+ roomName: target.roomName,
+ starter: starterMessage(target.roomName),
+ });
+ return db.transaction(async (tx) => {
+ const [current] = await tx
+ .select({
+ hackathonId: JudgingRoom.hackathonId,
+ threadId: JudgingRoom.discordThreadId,
+ })
+ .from(JudgingRoom)
+ .where(and(eq(JudgingRoom.id, roomId), isNull(JudgingRoom.archivedAt)))
+ .for("update")
+ .limit(1);
+ if (!current) return null;
+ const [configuration] = await tx
+ .select({
+ channelId: HackathonJudgingConfiguration.judgingCommsChannelId,
+ })
+ .from(HackathonJudgingConfiguration)
+ .where(
+ eq(HackathonJudgingConfiguration.hackathonId, current.hackathonId),
+ )
+ .limit(1);
+ if (configuration?.channelId !== target.channelId) return null;
+ if (current.threadId && current.threadId !== target.threadId) {
+ return current.threadId;
+ }
+ await tx
+ .update(JudgingRoom)
+ .set({ discordThreadId: threadId })
+ .where(eq(JudgingRoom.id, roomId));
+ return threadId;
+ });
+ });
+}
+
+export async function deliverJudgingRoomNotice(
+ roomId: string,
+ notice: JudgingRoomNotice,
+ gateway: JudgingDiscordGateway = liveJudgingDiscordGateway,
+): Promise {
+ try {
+ const target = await roomDiscordTarget(roomId);
+ if (!target?.channelId) return "not_configured";
+ const threadId = await ensureJudgingRoomThread(roomId, gateway);
+ if (!threadId) return "not_configured";
+ const recipientIds =
+ notice.kind === "member_joined"
+ ? [notice.discordUserId]
+ : await currentMemberDiscordIds(roomId);
+ const messages = buildJudgingRoomMessages({
+ notice,
+ recipientIds,
+ roomName: target.roomName,
+ });
+ for (const message of messages) {
+ await gateway.sendMessage({ message, threadId });
+ }
+ return "delivered";
+ } catch {
+ return "failed";
+ }
+}
+
+export async function deliverJudgingAnnouncement(
+ input: {
+ hackathonId: string;
+ isUrgent: boolean;
+ message: string;
+ roomId: string | null;
+ },
+ gateway: JudgingDiscordGateway = liveJudgingDiscordGateway,
+): Promise {
+ if (input.roomId) {
+ return deliverJudgingRoomNotice(
+ input.roomId,
+ {
+ isUrgent: input.isUrgent,
+ kind: "announcement",
+ message: input.message,
+ },
+ gateway,
+ );
+ }
+
+ try {
+ const [configuration] = await db
+ .select({
+ channelId: HackathonJudgingConfiguration.judgingCommsChannelId,
+ })
+ .from(HackathonJudgingConfiguration)
+ .where(eq(HackathonJudgingConfiguration.hackathonId, input.hackathonId))
+ .limit(1);
+ if (!configuration?.channelId) return "not_configured";
+ const recipientIds = await hackathonMemberDiscordIds(input.hackathonId);
+ const messages = buildJudgingAnnouncementMessages({
+ isUrgent: input.isUrgent,
+ message: input.message,
+ recipientIds,
+ scopeLabel: "all judging rooms",
+ });
+ for (const message of messages) {
+ await gateway.sendMessage({
+ message,
+ threadId: configuration.channelId,
+ });
+ }
+ return "delivered";
+ } catch {
+ return "failed";
+ }
+}
+
+/** Verify that a queued announcement is still current before delivery. */
+export async function deliverCurrentJudgingAnnouncement(
+ input: {
+ announcementId: string;
+ hackathonId: string;
+ isUrgent: boolean;
+ message: string;
+ roomId: string | null;
+ },
+ gateway: JudgingDiscordGateway = liveJudgingDiscordGateway,
+): Promise {
+ const [current] = await db
+ .select({ id: JudgingAnnouncement.id })
+ .from(JudgingAnnouncement)
+ .where(
+ and(
+ eq(JudgingAnnouncement.id, input.announcementId),
+ eq(JudgingAnnouncement.hackathonId, input.hackathonId),
+ isNull(JudgingAnnouncement.clearedAt),
+ ),
+ )
+ .limit(1);
+ if (!current) return "superseded";
+
+ return deliverJudgingAnnouncement(input, gateway);
+}
+
+export async function listJudgingDiscordChannels(
+ gateway: JudgingDiscordGateway = liveJudgingDiscordGateway,
+) {
+ const guildId = await getKnightHacksGuildId();
+ return gateway.listTextChannels(guildId);
+}
+
+export async function validateJudgingDiscordChannel(
+ channelId: string,
+ gateway: JudgingDiscordGateway = liveJudgingDiscordGateway,
+): Promise<"invalid" | "unavailable" | "valid"> {
+ try {
+ const [channel, guildId] = await Promise.all([
+ gateway.getChannel(channelId),
+ getKnightHacksGuildId(),
+ ]);
+ return "guild_id" in channel &&
+ channel.guild_id === guildId &&
+ (channel.type === ChannelType.GuildText ||
+ channel.type === ChannelType.GuildAnnouncement)
+ ? "valid"
+ : "invalid";
+ } catch {
+ return "unavailable";
+ }
+}
+
+export async function judgingDiscordGuildId() {
+ try {
+ return await getKnightHacksGuildId();
+ } catch {
+ return null;
+ }
+}
+
+export async function provisionJudgingRoomThreads(
+ hackathonId: string,
+ gateway: JudgingDiscordGateway = liveJudgingDiscordGateway,
+) {
+ const rooms = await db
+ .select({ id: JudgingRoom.id, name: JudgingRoom.name })
+ .from(JudgingRoom)
+ .where(
+ and(
+ eq(JudgingRoom.hackathonId, hackathonId),
+ isNull(JudgingRoom.archivedAt),
+ ),
+ );
+ const failedRooms: { id: string; name: string }[] = [];
+ let provisionedCount = 0;
+ for (const room of rooms) {
+ try {
+ const threadId = await ensureJudgingRoomThread(room.id, gateway);
+ if (threadId) provisionedCount += 1;
+ } catch {
+ failedRooms.push(room);
+ }
+ }
+ return { failedRooms, provisionedCount };
+}
diff --git a/packages/api/src/utils/judging/principal.ts b/packages/api/src/utils/judging/principal.ts
index 6bbe230cd..5b126869a 100644
--- a/packages/api/src/utils/judging/principal.ts
+++ b/packages/api/src/utils/judging/principal.ts
@@ -13,10 +13,12 @@ import {
JudgingRoomAccessLink,
} from "@forge/db/schemas/knight-hacks";
+import { resolveMemberDisplayNamesByUserId } from "../member/display-name";
import { loadPermissionsForUser } from "../permissions-db";
export interface MemberJudgePrincipal {
kind: "member";
+ discordUserId: string;
displayName: string;
isOfficer: boolean;
userId: string;
@@ -52,8 +54,13 @@ export async function resolveJudgeAccess(input: {
if (input.session?.user) {
const permissions = await loadPermissionsForUser(input.session.user.id);
if (permissions.IS_JUDGE === true || permissions.IS_OFFICER === true) {
+ const memberNames = await resolveMemberDisplayNamesByUserId([
+ input.session.user.id,
+ ]);
return {
- displayName: input.session.user.name,
+ displayName:
+ memberNames.get(input.session.user.id) ?? input.session.user.name,
+ discordUserId: input.session.user.discordUserId,
isOfficer: permissions.IS_OFFICER === true,
kind: "member",
userId: input.session.user.id,
diff --git a/packages/api/src/utils/member/display-name.ts b/packages/api/src/utils/member/display-name.ts
index a02c33cca..33976021b 100644
--- a/packages/api/src/utils/member/display-name.ts
+++ b/packages/api/src/utils/member/display-name.ts
@@ -2,6 +2,8 @@ import { inArray } from "@forge/db";
import { db } from "@forge/db/client";
import { Member } from "@forge/db/schemas/knight-hacks";
+import type { WriteDb } from "../db";
+
// Read-time actor-name resolution for Issue history and Admin logs (R-20).
// Both surfaces store a permanent write-time name snapshot alongside a
// nullable link to the acting Member. Resolving the current name here, at
@@ -38,11 +40,12 @@ export async function resolveMemberDisplayNames(
*/
export async function resolveMemberDisplayNamesByUserId(
userIds: readonly (string | null | undefined)[],
+ executor: WriteDb = db,
): Promise> {
const ids = [...new Set(userIds.filter((id): id is string => !!id))];
if (ids.length === 0) return new Map();
- const rows = await db
+ const rows = await executor
.select({
firstName: Member.firstName,
lastName: Member.lastName,
@@ -53,3 +56,29 @@ export async function resolveMemberDisplayNamesByUserId(
return new Map(rows.map((row) => [row.userId, fullName(row)]));
}
+
+/**
+ * Replace stored labels for authenticated judges with their current Member
+ * profile name. Guest-entered names and member labels without a linked Member
+ * record remain unchanged.
+ */
+export async function resolveCurrentJudgeDisplayNames<
+ T extends {
+ displayName: string;
+ kind: string;
+ userId: string | null;
+ },
+>(rows: readonly T[], executor: WriteDb = db): Promise {
+ const memberNames = await resolveMemberDisplayNamesByUserId(
+ rows.map((row) => (row.kind === "member" ? row.userId : null)),
+ executor,
+ );
+
+ return rows.map((row) => ({
+ ...row,
+ displayName:
+ row.kind === "member" && row.userId
+ ? (memberNames.get(row.userId) ?? row.displayName)
+ : row.displayName,
+ }));
+}
diff --git a/packages/db/drizzle/0048_little_tarot.sql b/packages/db/drizzle/0048_little_tarot.sql
new file mode 100644
index 000000000..a76f398a6
--- /dev/null
+++ b/packages/db/drizzle/0048_little_tarot.sql
@@ -0,0 +1,2 @@
+ALTER TABLE "knight_hacks_hackathon_judging_configuration" ADD COLUMN "judging_comms_channel_id" varchar(20);--> statement-breakpoint
+ALTER TABLE "knight_hacks_judging_room" ADD COLUMN "discord_thread_id" varchar(20);
\ No newline at end of file
diff --git a/packages/db/drizzle/0049_real_redwing.sql b/packages/db/drizzle/0049_real_redwing.sql
new file mode 100644
index 000000000..dcad96bf7
--- /dev/null
+++ b/packages/db/drizzle/0049_real_redwing.sql
@@ -0,0 +1,21 @@
+CREATE TABLE "knight_hacks_judging_announcement" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "hackathon_id" uuid NOT NULL,
+ "room_id" uuid,
+ "message" varchar(1000) NOT NULL,
+ "include_guests" boolean DEFAULT false NOT NULL,
+ "is_urgent" boolean DEFAULT false NOT NULL,
+ "published_by_user_id" uuid NOT NULL,
+ "published_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "cleared_at" timestamp with time zone,
+ "cleared_by_user_id" uuid,
+ CONSTRAINT "knight_hacks_judging_announcement_message_not_blank_check" CHECK ("knight_hacks_judging_announcement"."message" ~ '[^[:space:]]')
+);
+--> statement-breakpoint
+ALTER TABLE "knight_hacks_judging_announcement" ADD CONSTRAINT "knight_hacks_judging_announcement_hackathon_id_knight_hacks_hackathon_id_fk" FOREIGN KEY ("hackathon_id") REFERENCES "public"."knight_hacks_hackathon"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "knight_hacks_judging_announcement" ADD CONSTRAINT "knight_hacks_judging_announcement_published_by_user_id_auth_user_id_fk" FOREIGN KEY ("published_by_user_id") REFERENCES "public"."auth_user"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "knight_hacks_judging_announcement" ADD CONSTRAINT "knight_hacks_judging_announcement_cleared_by_user_id_auth_user_id_fk" FOREIGN KEY ("cleared_by_user_id") REFERENCES "public"."auth_user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "knight_hacks_judging_announcement" ADD CONSTRAINT "knight_hacks_judging_announcement_room_scope_fk" FOREIGN KEY ("room_id","hackathon_id") REFERENCES "public"."knight_hacks_judging_room"("id","hackathon_id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE UNIQUE INDEX "knight_hacks_judging_announcement_current_global_unique" ON "knight_hacks_judging_announcement" USING btree ("hackathon_id") WHERE "knight_hacks_judging_announcement"."room_id" IS NULL AND "knight_hacks_judging_announcement"."cleared_at" IS NULL;--> statement-breakpoint
+CREATE UNIQUE INDEX "knight_hacks_judging_announcement_current_room_unique" ON "knight_hacks_judging_announcement" USING btree ("room_id") WHERE "knight_hacks_judging_announcement"."room_id" IS NOT NULL AND "knight_hacks_judging_announcement"."cleared_at" IS NULL;--> statement-breakpoint
+CREATE INDEX "knight_hacks_judging_announcement_current_lookup_idx" ON "knight_hacks_judging_announcement" USING btree ("hackathon_id","room_id","cleared_at");
diff --git a/packages/db/drizzle/meta/0048_snapshot.json b/packages/db/drizzle/meta/0048_snapshot.json
new file mode 100644
index 000000000..e75a4b4b4
--- /dev/null
+++ b/packages/db/drizzle/meta/0048_snapshot.json
@@ -0,0 +1,12730 @@
+{
+ "id": "a303798c-6c7d-44c9-a6f8-4efdfd97527c",
+ "prevId": "a89bd205-56f1-422e-965d-ba8fae2d68b0",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.audit_event": {
+ "name": "audit_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "action_key": {
+ "name": "action_key",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "operation_id": {
+ "name": "operation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_user_id": {
+ "name": "actor_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_member_id": {
+ "name": "actor_member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_discord_user_id": {
+ "name": "actor_discord_user_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_label": {
+ "name": "actor_label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_role_label": {
+ "name": "actor_role_label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_role_color": {
+ "name": "actor_role_color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "changes": {
+ "name": "changes",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ }
+ },
+ "indexes": {
+ "audit_event_action_occurred_idx": {
+ "name": "audit_event_action_occurred_idx",
+ "columns": [
+ {
+ "expression": "action_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_actor_occurred_idx": {
+ "name": "audit_event_actor_occurred_idx",
+ "columns": [
+ {
+ "expression": "actor_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_actor_member_occurred_idx": {
+ "name": "audit_event_actor_member_occurred_idx",
+ "columns": [
+ {
+ "expression": "actor_member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_domain_occurred_idx": {
+ "name": "audit_event_domain_occurred_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_occurred_idx": {
+ "name": "audit_event_occurred_idx",
+ "columns": [
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_operation_idx": {
+ "name": "audit_event_operation_idx",
+ "columns": [
+ {
+ "expression": "operation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_outcome_occurred_idx": {
+ "name": "audit_event_outcome_occurred_idx",
+ "columns": [
+ {
+ "expression": "outcome",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "audit_event_actor_role_color_check": {
+ "name": "audit_event_actor_role_color_check",
+ "value": "\"audit_event\".\"actor_role_color\" IS NULL OR \"audit_event\".\"actor_role_color\" ~ '^#[0-9A-Fa-f]{6}$'"
+ },
+ "audit_event_outcome_check": {
+ "name": "audit_event_outcome_check",
+ "value": "\"audit_event\".\"outcome\" IN ('committed', 'partial_external')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.audit_subject": {
+ "name": "audit_subject",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "relation": {
+ "name": "relation",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_label": {
+ "name": "target_label",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_outcome": {
+ "name": "result_outcome",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "position": {
+ "name": "position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ }
+ },
+ "indexes": {
+ "audit_subject_event_position_idx": {
+ "name": "audit_subject_event_position_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "position",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_subject_member_event_idx": {
+ "name": "audit_subject_member_event_idx",
+ "columns": [
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_subject_one_primary_idx": {
+ "name": "audit_subject_one_primary_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"audit_subject\".\"relation\" = 'primary'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_subject_target_event_idx": {
+ "name": "audit_subject_target_event_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "audit_subject_event_id_audit_event_id_fk": {
+ "name": "audit_subject_event_id_audit_event_id_fk",
+ "tableFrom": "audit_subject",
+ "tableTo": "audit_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "audit_subject_relation_check": {
+ "name": "audit_subject_relation_check",
+ "value": "\"audit_subject\".\"relation\" IN ('primary', 'secondary', 'result')"
+ },
+ "audit_subject_result_outcome_check": {
+ "name": "audit_subject_result_outcome_check",
+ "value": "\"audit_subject\".\"result_outcome\" IS NULL OR \"audit_subject\".\"result_outcome\" IN ('succeeded', 'skipped', 'failed_external', 'failed_internal', 'compensated')"
+ },
+ "audit_subject_result_outcome_shape_check": {
+ "name": "audit_subject_result_outcome_shape_check",
+ "value": "(\"audit_subject\".\"relation\" = 'result') = (\"audit_subject\".\"result_outcome\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.auth_account": {
+ "name": "auth_account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_account_id": {
+ "name": "provider_account_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "auth_account_user_id_auth_user_id_fk": {
+ "name": "auth_account_user_id_auth_user_id_fk",
+ "tableFrom": "auth_account",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "auth_account_provider_provider_account_id_pk": {
+ "name": "auth_account_provider_provider_account_id_pk",
+ "columns": ["provider", "provider_account_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_permissions": {
+ "name": "auth_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "auth_permissions_role_id_auth_roles_id_fk": {
+ "name": "auth_permissions_role_id_auth_roles_id_fk",
+ "tableFrom": "auth_permissions",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "auth_permissions_user_id_auth_user_id_fk": {
+ "name": "auth_permissions_user_id_auth_user_id_fk",
+ "tableFrom": "auth_permissions",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_roles": {
+ "name": "auth_roles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "discord_role_id": {
+ "name": "discord_role_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_feedback_excluded": {
+ "name": "event_feedback_excluded",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "email_audience_enabled": {
+ "name": "email_audience_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "issue_reminder_channel": {
+ "name": "issue_reminder_channel",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'1459204271655489567'"
+ },
+ "issue_reminders_enabled": {
+ "name": "issue_reminders_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "team_hexcode_color": {
+ "name": "team_hexcode_color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "auth_roles_discordRoleId_unique": {
+ "name": "auth_roles_discordRoleId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["discord_role_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_session": {
+ "name": "auth_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires": {
+ "name": "expires",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "varchar(1024)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "auth_session_user_id_auth_user_id_fk": {
+ "name": "auth_session_user_id_auth_user_id_fk",
+ "tableFrom": "auth_session",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_user": {
+ "name": "auth_user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "discord_user_id": {
+ "name": "discord_user_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_verification": {
+ "name": "auth_verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_club_team": {
+ "name": "knight_hacks_club_team",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "heading": {
+ "name": "heading",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_club_team_display_order_unique": {
+ "name": "knight_hacks_club_team_display_order_unique",
+ "columns": [
+ {
+ "expression": "display_order",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_club_team_kind_unique": {
+ "name": "knight_hacks_club_team_kind_unique",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_club_team\".\"kind\" <> 'team'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_club_team_slug_unique": {
+ "name": "knight_hacks_club_team_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": ["slug"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_club_team_kind_check": {
+ "name": "knight_hacks_club_team_kind_check",
+ "value": "\"knight_hacks_club_team\".\"kind\" IN ('executive', 'director', 'team')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_club_team_role": {
+ "name": "knight_hacks_club_team_role",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rank": {
+ "name": "rank",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "roster_label": {
+ "name": "roster_label",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callout_label": {
+ "name": "callout_label",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_club_team_role_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_club_team_role_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_club_team_role",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_club_team_role_team_id_knight_hacks_club_team_id_fk": {
+ "name": "knight_hacks_club_team_role_team_id_knight_hacks_club_team_id_fk",
+ "tableFrom": "knight_hacks_club_team_role",
+ "tableTo": "knight_hacks_club_team",
+ "columnsFrom": ["team_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_club_team_role_roleId_unique": {
+ "name": "knight_hacks_club_team_role_roleId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["role_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_club_team_role_kind_check": {
+ "name": "knight_hacks_club_team_role_kind_check",
+ "value": "\"knight_hacks_club_team_role\".\"kind\" IN ('executive', 'director', 'team')"
+ },
+ "knight_hacks_club_team_role_team_check": {
+ "name": "knight_hacks_club_team_role_team_check",
+ "value": "\"knight_hacks_club_team_role\".\"kind\" <> 'team' OR \"knight_hacks_club_team_role\".\"team_id\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_discord_config": {
+ "name": "knight_hacks_discord_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "production_id": {
+ "name": "production_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "development_id": {
+ "name": "development_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_discord_config_key_unique": {
+ "name": "knight_hacks_discord_config_key_unique",
+ "nullsNotDistinct": false,
+ "columns": ["key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_discord_config_kind_check": {
+ "name": "knight_hacks_discord_config_kind_check",
+ "value": "\"knight_hacks_discord_config\".\"kind\" IN ('channel', 'guild', 'role')"
+ },
+ "knight_hacks_discord_config_production_id_check": {
+ "name": "knight_hacks_discord_config_production_id_check",
+ "value": "\"knight_hacks_discord_config\".\"production_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_discord_config_development_id_check": {
+ "name": "knight_hacks_discord_config_development_id_check",
+ "value": "\"knight_hacks_discord_config\".\"development_id\" IS NULL OR \"knight_hacks_discord_config\".\"development_id\" ~ '^[0-9]{17,20}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_channel": {
+ "name": "discord_archive_channel",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topic": {
+ "name": "topic",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_thread": {
+ "name": "is_thread",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_private_thread": {
+ "name": "is_private_thread",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "archived": {
+ "name": "archived",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "locked": {
+ "name": "locked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "discovered_at": {
+ "name": "discovered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "discord_updated_at": {
+ "name": "discord_updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "discord_archive_channel_guild_type_idx": {
+ "name": "discord_archive_channel_guild_type_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_channel_parent_idx": {
+ "name": "discord_archive_channel_parent_idx",
+ "columns": [
+ {
+ "expression": "parent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_checkpoint": {
+ "name": "discord_archive_checkpoint",
+ "schema": "",
+ "columns": {
+ "channel_id": {
+ "name": "channel_id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oldest_message_id": {
+ "name": "oldest_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "newest_message_id": {
+ "name": "newest_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backfill_before_message_id": {
+ "name": "backfill_before_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backfill_status": {
+ "name": "backfill_status",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "backfill_completed_at": {
+ "name": "backfill_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_backfill_at": {
+ "name": "last_backfill_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_discovered_at": {
+ "name": "last_discovered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciled_at": {
+ "name": "last_reconciled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "processed_message_count": {
+ "name": "processed_message_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry_count": {
+ "name": "retry_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error_code": {
+ "name": "last_error_code",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_message": {
+ "name": "last_error_message",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "discord_archive_checkpoint_guild_status_updated_idx": {
+ "name": "discord_archive_checkpoint_guild_status_updated_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "backfill_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "discord_archive_checkpoint_channel_id_discord_archive_channel_id_fk": {
+ "name": "discord_archive_checkpoint_channel_id_discord_archive_channel_id_fk",
+ "tableFrom": "discord_archive_checkpoint",
+ "tableTo": "discord_archive_channel",
+ "columnsFrom": ["channel_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "discord_archive_checkpoint_status_check": {
+ "name": "discord_archive_checkpoint_status_check",
+ "value": "\"discord_archive_checkpoint\".\"backfill_status\" IN ('pending', 'running', 'complete', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_message": {
+ "name": "discord_archive_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_discord_user_id": {
+ "name": "author_discord_user_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_label": {
+ "name": "author_label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_avatar_url": {
+ "name": "author_avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_is_bot": {
+ "name": "author_is_bot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "webhook_id": {
+ "name": "webhook_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message_type": {
+ "name": "message_type",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "edited_at": {
+ "name": "edited_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reply_to_message_id": {
+ "name": "reply_to_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pinned": {
+ "name": "pinned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "flags": {
+ "name": "flags",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "mentions": {
+ "name": "mentions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"everyone\":false,\"roleIds\":[],\"userIds\":[]}'::jsonb"
+ },
+ "embeds": {
+ "name": "embeds",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "attachments": {
+ "name": "attachments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "components": {
+ "name": "components",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "stickers": {
+ "name": "stickers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "poll": {
+ "name": "poll",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ingested_at": {
+ "name": "ingested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_observed_at": {
+ "name": "last_observed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "discord_archive_message_author_created_idx": {
+ "name": "discord_archive_message_author_created_idx",
+ "columns": [
+ {
+ "expression": "author_discord_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_message_channel_created_idx": {
+ "name": "discord_archive_message_channel_created_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_message_guild_created_idx": {
+ "name": "discord_archive_message_guild_created_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_message_non_deleted_created_idx": {
+ "name": "discord_archive_message_non_deleted_created_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"discord_archive_message\".\"deleted_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "discord_archive_message_channel_id_discord_archive_channel_id_fk": {
+ "name": "discord_archive_message_channel_id_discord_archive_channel_id_fk",
+ "tableFrom": "discord_archive_message",
+ "tableTo": "discord_archive_channel",
+ "columnsFrom": ["channel_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_state": {
+ "name": "discord_archive_state",
+ "schema": "",
+ "columns": {
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'idle'"
+ },
+ "last_gateway_event_at": {
+ "name": "last_gateway_event_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_live_write_at": {
+ "name": "last_live_write_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_discovery_started_at": {
+ "name": "last_discovery_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_discovery_completed_at": {
+ "name": "last_discovery_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciliation_started_at": {
+ "name": "last_reconciliation_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciliation_completed_at": {
+ "name": "last_reconciliation_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_backfill_progress_at": {
+ "name": "last_backfill_progress_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_owner": {
+ "name": "lease_owner",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_code": {
+ "name": "last_error_code",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_message": {
+ "name": "last_error_message",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_count": {
+ "name": "failure_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "discord_archive_state_status_check": {
+ "name": "discord_archive_state_status_check",
+ "value": "\"discord_archive_state\".\"status\" IN ('disabled', 'idle', 'healthy', 'degraded', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_alumni_bulletin_post": {
+ "name": "knight_hacks_alumni_bulletin_post",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image_object_name": {
+ "name": "image_object_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image_alt": {
+ "name": "image_alt",
+ "type": "varchar(240)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cta_label": {
+ "name": "cta_label",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "varchar(2048)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "alumni_bulletin_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "publish_at": {
+ "name": "publish_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by_user_id": {
+ "name": "updated_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_alumni_bulletin_state_order_idx": {
+ "name": "knight_hacks_alumni_bulletin_state_order_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "display_order",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_alumni_bulletin_publication_window_idx": {
+ "name": "knight_hacks_alumni_bulletin_publication_window_idx",
+ "columns": [
+ {
+ "expression": "publish_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_alumni_bulletin_post_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_alumni_bulletin_post_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_alumni_bulletin_post",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_alumni_bulletin_post_created_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_alumni_bulletin_post_created_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_alumni_bulletin_post",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_alumni_bulletin_post_updated_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_alumni_bulletin_post_updated_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_alumni_bulletin_post",
+ "tableTo": "auth_user",
+ "columnsFrom": ["updated_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "alumni_bulletin_action_exclusive": {
+ "name": "alumni_bulletin_action_exclusive",
+ "value": "NOT (\"knight_hacks_alumni_bulletin_post\".\"external_url\" IS NOT NULL AND \"knight_hacks_alumni_bulletin_post\".\"form_id\" IS NOT NULL)"
+ },
+ "alumni_bulletin_action_pair": {
+ "name": "alumni_bulletin_action_pair",
+ "value": "((\"knight_hacks_alumni_bulletin_post\".\"external_url\" IS NULL AND \"knight_hacks_alumni_bulletin_post\".\"form_id\" IS NULL) OR \"knight_hacks_alumni_bulletin_post\".\"cta_label\" IS NOT NULL)"
+ },
+ "alumni_bulletin_display_order_nonnegative": {
+ "name": "alumni_bulletin_display_order_nonnegative",
+ "value": "\"knight_hacks_alumni_bulletin_post\".\"display_order\" >= 0"
+ },
+ "alumni_bulletin_image_alt_pair": {
+ "name": "alumni_bulletin_image_alt_pair",
+ "value": "((\"knight_hacks_alumni_bulletin_post\".\"image_object_name\" IS NULL AND \"knight_hacks_alumni_bulletin_post\".\"image_alt\" IS NULL) OR (\"knight_hacks_alumni_bulletin_post\".\"image_object_name\" IS NOT NULL AND \"knight_hacks_alumni_bulletin_post\".\"image_alt\" IS NOT NULL))"
+ },
+ "alumni_bulletin_schedule_order": {
+ "name": "alumni_bulletin_schedule_order",
+ "value": "\"knight_hacks_alumni_bulletin_post\".\"expires_at\" IS NULL OR \"knight_hacks_alumni_bulletin_post\".\"publish_at\" IS NULL OR \"knight_hacks_alumni_bulletin_post\".\"expires_at\" > \"knight_hacks_alumni_bulletin_post\".\"publish_at\""
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_company": {
+ "name": "knight_hacks_company",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_display_name": {
+ "name": "normalized_display_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "legal_name": {
+ "name": "legal_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "varchar(253)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logo_object_name": {
+ "name": "logo_object_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aliases": {
+ "name": "aliases",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "review_state": {
+ "name": "review_state",
+ "type": "company_review_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "merged_into_company_id": {
+ "name": "merged_into_company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_company_review_state_idx": {
+ "name": "knight_hacks_company_review_state_idx",
+ "columns": [
+ {
+ "expression": "review_state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_company_domain_idx": {
+ "name": "knight_hacks_company_domain_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_company_created_by_user_idx": {
+ "name": "knight_hacks_company_created_by_user_idx",
+ "columns": [
+ {
+ "expression": "created_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_company_created_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_company_created_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_company",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_company_merged_into_fk": {
+ "name": "knight_hacks_company_merged_into_fk",
+ "tableFrom": "knight_hacks_company",
+ "tableTo": "knight_hacks_company",
+ "columnsFrom": ["merged_into_company_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_company_normalized_display_name_unique": {
+ "name": "knight_hacks_company_normalized_display_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["normalized_display_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_company_merged_state_consistency": {
+ "name": "knight_hacks_company_merged_state_consistency",
+ "value": "(\"knight_hacks_company\".\"review_state\" = 'merged') = (\"knight_hacks_company\".\"merged_into_company_id\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_dues_configuration": {
+ "name": "knight_hacks_dues_configuration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(32)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "payments_enabled": {
+ "name": "payments_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_dues_configuration_singleton_check": {
+ "name": "knight_hacks_dues_configuration_singleton_check",
+ "value": "\"knight_hacks_dues_configuration\".\"id\" = 'global'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_dues_payment": {
+ "name": "knight_hacks_dues_payment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount": {
+ "name": "amount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payment_date": {
+ "name": "payment_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "year": {
+ "name": "year",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "stripe_payment_intent_id": {
+ "name": "stripe_payment_intent_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_dues_payment_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_dues_payment_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_dues_payment",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_dues_payment_memberId_year_unique": {
+ "name": "knight_hacks_dues_payment_memberId_year_unique",
+ "nullsNotDistinct": false,
+ "columns": ["member_id", "year"]
+ },
+ "knight_hacks_dues_payment_stripe_payment_intent_id_unique": {
+ "name": "knight_hacks_dues_payment_stripe_payment_intent_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["stripe_payment_intent_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_send": {
+ "name": "email_send",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "subject": {
+ "name": "subject",
+ "type": "varchar(200)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "template_revision_id": {
+ "name": "template_revision_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "plain_text_source": {
+ "name": "plain_text_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_html": {
+ "name": "compiled_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_text": {
+ "name": "compiled_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_hash": {
+ "name": "content_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience_definition": {
+ "name": "audience_definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience_version": {
+ "name": "audience_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "audience_hash": {
+ "name": "audience_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "preview_version": {
+ "name": "preview_version",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "preview_expires_at": {
+ "name": "preview_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "raw_match_count": {
+ "name": "raw_match_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "duplicate_count": {
+ "name": "duplicate_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_invalid_count": {
+ "name": "excluded_invalid_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_suppressed_count": {
+ "name": "excluded_suppressed_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_missing_field_count": {
+ "name": "excluded_missing_field_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_manual_count": {
+ "name": "excluded_manual_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "final_recipient_count": {
+ "name": "final_recipient_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "scheduled_for": {
+ "name": "scheduled_for",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listmonk_list_id": {
+ "name": "listmonk_list_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listmonk_campaign_id": {
+ "name": "listmonk_campaign_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_tag": {
+ "name": "provider_tag",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_may_have_started": {
+ "name": "provider_may_have_started",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "provider_sent_count": {
+ "name": "provider_sent_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "provider_bounce_count": {
+ "name": "provider_bounce_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry_attempt_count": {
+ "name": "retry_attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry_lease_expires_at": {
+ "name": "retry_lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "safe_error": {
+ "name": "safe_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confirmed_at": {
+ "name": "confirmed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled_at": {
+ "name": "cancelled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled_by": {
+ "name": "cancelled_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "terminal_at": {
+ "name": "terminal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_send_status_scheduled_for_idx": {
+ "name": "email_send_status_scheduled_for_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scheduled_for",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_send_retry_idx": {
+ "name": "email_send_retry_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_retry_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "retry_lease_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_send_template_revision_id_email_template_revision_id_fk": {
+ "name": "email_send_template_revision_id_email_template_revision_id_fk",
+ "tableFrom": "email_send",
+ "tableTo": "email_template_revision",
+ "columnsFrom": ["template_revision_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_send_created_by_auth_user_id_fk": {
+ "name": "email_send_created_by_auth_user_id_fk",
+ "tableFrom": "email_send",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_send_cancelled_by_auth_user_id_fk": {
+ "name": "email_send_cancelled_by_auth_user_id_fk",
+ "tableFrom": "email_send",
+ "tableTo": "auth_user",
+ "columnsFrom": ["cancelled_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_send_provider_tag_unique": {
+ "name": "email_send_provider_tag_unique",
+ "nullsNotDistinct": false,
+ "columns": ["provider_tag"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_send_event": {
+ "name": "email_send_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "send_id": {
+ "name": "send_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "from_status": {
+ "name": "from_status",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "to_status": {
+ "name": "to_status",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_send_event_send_created_idx": {
+ "name": "email_send_event_send_created_idx",
+ "columns": [
+ {
+ "expression": "send_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_send_event_send_id_email_send_id_fk": {
+ "name": "email_send_event_send_id_email_send_id_fk",
+ "tableFrom": "email_send_event",
+ "tableTo": "email_send",
+ "columnsFrom": ["send_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_send_event_actor_id_auth_user_id_fk": {
+ "name": "email_send_event_actor_id_auth_user_id_fk",
+ "tableFrom": "email_send_event",
+ "tableTo": "auth_user",
+ "columnsFrom": ["actor_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_send_recipient": {
+ "name": "email_send_recipient",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "send_id": {
+ "name": "send_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_email": {
+ "name": "normalized_email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attributes": {
+ "name": "attributes",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "match_reasons": {
+ "name": "match_reasons",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "exclusion_reason": {
+ "name": "exclusion_reason",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listmonk_subscriber_id": {
+ "name": "listmonk_subscriber_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_send_recipient_send_id_idx": {
+ "name": "email_send_recipient_send_id_idx",
+ "columns": [
+ {
+ "expression": "send_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "exclusion_reason",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_send_recipient_send_id_email_send_id_fk": {
+ "name": "email_send_recipient_send_id_email_send_id_fk",
+ "tableFrom": "email_send_recipient",
+ "tableTo": "email_send",
+ "columnsFrom": ["send_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_send_recipient_send_id_normalized_email_unique": {
+ "name": "email_send_recipient_send_id_normalized_email_unique",
+ "nullsNotDistinct": false,
+ "columns": ["send_id", "normalized_email"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template": {
+ "name": "email_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_name": {
+ "name": "normalized_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'club'"
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_archived_updated_idx": {
+ "name": "email_template_archived_updated_idx",
+ "columns": [
+ {
+ "expression": "archived_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_created_by_auth_user_id_fk": {
+ "name": "email_template_created_by_auth_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_template_updated_by_auth_user_id_fk": {
+ "name": "email_template_updated_by_auth_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "auth_user",
+ "columnsFrom": ["updated_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_template_normalized_name_unique": {
+ "name": "email_template_normalized_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["normalized_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template_revision": {
+ "name": "email_template_revision",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visual_document": {
+ "name": "visual_document",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_html": {
+ "name": "compiled_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_text": {
+ "name": "compiled_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "personalization_contract": {
+ "name": "personalization_contract",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "checksum": {
+ "name": "checksum",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_revision_template_state_idx": {
+ "name": "email_template_revision_template_state_idx",
+ "columns": [
+ {
+ "expression": "template_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "version",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_revision_template_id_email_template_id_fk": {
+ "name": "email_template_revision_template_id_email_template_id_fk",
+ "tableFrom": "email_template_revision",
+ "tableTo": "email_template",
+ "columnsFrom": ["template_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_template_revision_created_by_auth_user_id_fk": {
+ "name": "email_template_revision_created_by_auth_user_id_fk",
+ "tableFrom": "email_template_revision",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_template_revision_template_version_unique": {
+ "name": "email_template_revision_template_version_unique",
+ "nullsNotDistinct": false,
+ "columns": ["template_id", "version"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_employment": {
+ "name": "knight_hacks_employment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "experience_type": {
+ "name": "experience_type",
+ "type": "employment_experience_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "employment_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "start_month": {
+ "name": "start_month",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "end_month": {
+ "name": "end_month",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "city_key": {
+ "name": "city_key",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guild_visible": {
+ "name": "guild_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_employment_member_idx": {
+ "name": "knight_hacks_employment_member_idx",
+ "columns": [
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_employment_company_idx": {
+ "name": "knight_hacks_employment_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_employment_company_state_idx": {
+ "name": "knight_hacks_employment_company_state_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_employment_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_employment_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_employment",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_employment_company_id_knight_hacks_company_id_fk": {
+ "name": "knight_hacks_employment_company_id_knight_hacks_company_id_fk",
+ "tableFrom": "knight_hacks_employment",
+ "tableTo": "knight_hacks_company",
+ "columnsFrom": ["company_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_employment_current_has_no_end": {
+ "name": "knight_hacks_employment_current_has_no_end",
+ "value": "\"knight_hacks_employment\".\"state\" <> 'current' OR \"knight_hacks_employment\".\"end_month\" IS NULL"
+ },
+ "knight_hacks_employment_start_month_shape": {
+ "name": "knight_hacks_employment_start_month_shape",
+ "value": "\"knight_hacks_employment\".\"start_month\" IS NULL OR \"knight_hacks_employment\".\"start_month\" ~ '^[0-9]{4}-(0[1-9]|1[0-2])$'"
+ },
+ "knight_hacks_employment_end_month_shape": {
+ "name": "knight_hacks_employment_end_month_shape",
+ "value": "\"knight_hacks_employment\".\"end_month\" IS NULL OR \"knight_hacks_employment\".\"end_month\" ~ '^[0-9]{4}-(0[1-9]|1[0-2])$'"
+ },
+ "knight_hacks_employment_date_order": {
+ "name": "knight_hacks_employment_date_order",
+ "value": "\"knight_hacks_employment\".\"start_month\" IS NULL OR \"knight_hacks_employment\".\"end_month\" IS NULL OR \"knight_hacks_employment\".\"end_month\" >= \"knight_hacks_employment\".\"start_month\""
+ },
+ "knight_hacks_employment_city_key_shape": {
+ "name": "knight_hacks_employment_city_key_shape",
+ "value": "\"knight_hacks_employment\".\"city_key\" IS NULL OR \"knight_hacks_employment\".\"city_key\" ~ '^[0-9]{2}-[0-9]{5}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event": {
+ "name": "knight_hacks_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "discord_id": {
+ "name": "discord_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_id": {
+ "name": "google_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tag": {
+ "name": "tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tag_color": {
+ "name": "tag_color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#0D9488'"
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "start_datetime": {
+ "name": "start_datetime",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_datetime": {
+ "name": "end_datetime",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "location": {
+ "name": "location",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dues_paying": {
+ "name": "dues_paying",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_operations_calendar": {
+ "name": "is_operations_calendar",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "roles": {
+ "name": "roles",
+ "type": "varchar(255)[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "points": {
+ "name": "points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "purpose": {
+ "name": "purpose",
+ "type": "event_purpose",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'event'"
+ },
+ "discord_channel_id": {
+ "name": "discord_channel_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "legacy": {
+ "name": "legacy",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "discord_sync_state": {
+ "name": "discord_sync_state",
+ "type": "event_sync_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'pending'"
+ },
+ "google_sync_state": {
+ "name": "google_sync_state",
+ "type": "event_sync_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'pending'"
+ },
+ "discord_last_error": {
+ "name": "discord_last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_last_error": {
+ "name": "google_last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletion_intent_at": {
+ "name": "deletion_intent_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_key": {
+ "name": "creation_key",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_payload_hash": {
+ "name": "creation_payload_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_revision": {
+ "name": "sync_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "sync_lease_token": {
+ "name": "sync_lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_lease_expires_at": {
+ "name": "sync_lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_lease_revision": {
+ "name": "sync_lease_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_outbound_attempt_token": {
+ "name": "discord_outbound_attempt_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_outbound_attempted_at": {
+ "name": "discord_outbound_attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_outbound_attempt_revision": {
+ "name": "discord_outbound_attempt_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_outbound_attempt_token": {
+ "name": "google_outbound_attempt_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_outbound_attempted_at": {
+ "name": "google_outbound_attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_outbound_attempt_revision": {
+ "name": "google_outbound_attempt_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_applied_revision": {
+ "name": "discord_applied_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_applied_entity_type": {
+ "name": "discord_applied_entity_type",
+ "type": "event_discord_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_applied_channel_id": {
+ "name": "discord_applied_channel_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_applied_revision": {
+ "name": "google_applied_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_applied_destination": {
+ "name": "google_applied_destination",
+ "type": "event_google_destination",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_applied_calendar_id": {
+ "name": "google_applied_calendar_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_revision": {
+ "name": "visibility_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_dues_paying": {
+ "name": "visibility_dues_paying",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_roles": {
+ "name": "visibility_roles",
+ "type": "varchar(255)[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_internal": {
+ "name": "visibility_internal",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_no_projection_acknowledged_at": {
+ "name": "discord_no_projection_acknowledged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_no_projection_acknowledged_by": {
+ "name": "discord_no_projection_acknowledged_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_start_datetime_idx": {
+ "name": "knight_hacks_event_start_datetime_idx",
+ "columns": [
+ {
+ "expression": "start_datetime",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_hackathon_id_idx": {
+ "name": "knight_hacks_event_hackathon_id_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_one_primary_per_hackathon": {
+ "name": "knight_hacks_event_one_primary_per_hackathon",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_event\".\"purpose\" = 'primary_check_in'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_event_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_event",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_discord_no_projection_acknowledged_by_auth_user_id_fk": {
+ "name": "knight_hacks_event_discord_no_projection_acknowledged_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_event",
+ "tableTo": "auth_user",
+ "columnsFrom": ["discord_no_projection_acknowledged_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_creationKey_unique": {
+ "name": "knight_hacks_event_creationKey_unique",
+ "nullsNotDistinct": false,
+ "columns": ["creation_key"]
+ },
+ "knight_hacks_event_id_hackathon_unique": {
+ "name": "knight_hacks_event_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_primary_requires_hackathon_check": {
+ "name": "knight_hacks_event_primary_requires_hackathon_check",
+ "value": "\"knight_hacks_event\".\"purpose\" <> 'primary_check_in' OR \"knight_hacks_event\".\"hackathon_id\" IS NOT NULL"
+ },
+ "knight_hacks_event_tag_color_check": {
+ "name": "knight_hacks_event_tag_color_check",
+ "value": "\"knight_hacks_event\".\"tag_color\" ~ '^#[0-9A-Fa-f]{6}$'"
+ },
+ "knight_hacks_event_sync_revision_check": {
+ "name": "knight_hacks_event_sync_revision_check",
+ "value": "\"knight_hacks_event\".\"sync_revision\" >= 0"
+ },
+ "knight_hacks_event_new_points_check": {
+ "name": "knight_hacks_event_new_points_check",
+ "value": "\"knight_hacks_event\".\"legacy\" OR (\"knight_hacks_event\".\"points\" IS NOT NULL AND \"knight_hacks_event\".\"points\" >= 0)"
+ },
+ "knight_hacks_event_nonlegacy_sync_states_check": {
+ "name": "knight_hacks_event_nonlegacy_sync_states_check",
+ "value": "\"knight_hacks_event\".\"legacy\" OR (\"knight_hacks_event\".\"discord_sync_state\" IS NOT NULL AND \"knight_hacks_event\".\"google_sync_state\" IS NOT NULL)"
+ },
+ "knight_hacks_event_creation_identity_pair_check": {
+ "name": "knight_hacks_event_creation_identity_pair_check",
+ "value": "(\"knight_hacks_event\".\"creation_key\" IS NULL) = (\"knight_hacks_event\".\"creation_payload_hash\" IS NULL)"
+ },
+ "knight_hacks_event_new_creation_identity_check": {
+ "name": "knight_hacks_event_new_creation_identity_check",
+ "value": "\"knight_hacks_event\".\"legacy\" OR (\"knight_hacks_event\".\"creation_key\" IS NOT NULL AND \"knight_hacks_event\".\"creation_payload_hash\" IS NOT NULL)"
+ },
+ "knight_hacks_event_sync_lease_set_check": {
+ "name": "knight_hacks_event_sync_lease_set_check",
+ "value": "(\"knight_hacks_event\".\"sync_lease_token\" IS NULL AND \"knight_hacks_event\".\"sync_lease_expires_at\" IS NULL AND \"knight_hacks_event\".\"sync_lease_revision\" IS NULL) OR (\"knight_hacks_event\".\"sync_lease_token\" IS NOT NULL AND \"knight_hacks_event\".\"sync_lease_expires_at\" IS NOT NULL AND \"knight_hacks_event\".\"sync_lease_revision\" IS NOT NULL)"
+ },
+ "knight_hacks_event_discord_attempt_set_check": {
+ "name": "knight_hacks_event_discord_attempt_set_check",
+ "value": "(\"knight_hacks_event\".\"discord_outbound_attempt_token\" IS NULL AND \"knight_hacks_event\".\"discord_outbound_attempted_at\" IS NULL AND \"knight_hacks_event\".\"discord_outbound_attempt_revision\" IS NULL) OR (\"knight_hacks_event\".\"discord_outbound_attempt_token\" IS NOT NULL AND \"knight_hacks_event\".\"discord_outbound_attempted_at\" IS NOT NULL AND \"knight_hacks_event\".\"discord_outbound_attempt_revision\" IS NOT NULL)"
+ },
+ "knight_hacks_event_google_attempt_set_check": {
+ "name": "knight_hacks_event_google_attempt_set_check",
+ "value": "(\"knight_hacks_event\".\"google_outbound_attempt_token\" IS NULL AND \"knight_hacks_event\".\"google_outbound_attempted_at\" IS NULL AND \"knight_hacks_event\".\"google_outbound_attempt_revision\" IS NULL) OR (\"knight_hacks_event\".\"google_outbound_attempt_token\" IS NOT NULL AND \"knight_hacks_event\".\"google_outbound_attempted_at\" IS NOT NULL AND \"knight_hacks_event\".\"google_outbound_attempt_revision\" IS NOT NULL)"
+ },
+ "knight_hacks_event_visibility_set_check": {
+ "name": "knight_hacks_event_visibility_set_check",
+ "value": "(\"knight_hacks_event\".\"visibility_revision\" IS NULL AND \"knight_hacks_event\".\"visibility_dues_paying\" IS NULL AND \"knight_hacks_event\".\"visibility_roles\" IS NULL AND \"knight_hacks_event\".\"visibility_internal\" IS NULL) OR (\"knight_hacks_event\".\"visibility_revision\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_dues_paying\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_roles\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_internal\" IS NOT NULL)"
+ },
+ "knight_hacks_event_visibility_revision_check": {
+ "name": "knight_hacks_event_visibility_revision_check",
+ "value": "\"knight_hacks_event\".\"visibility_revision\" IS NULL OR \"knight_hacks_event\".\"visibility_revision\" <= \"knight_hacks_event\".\"sync_revision\""
+ },
+ "knight_hacks_event_discord_applied_revision_check": {
+ "name": "knight_hacks_event_discord_applied_revision_check",
+ "value": "\"knight_hacks_event\".\"discord_applied_revision\" IS NULL OR \"knight_hacks_event\".\"discord_applied_revision\" <= \"knight_hacks_event\".\"sync_revision\""
+ },
+ "knight_hacks_event_google_applied_revision_check": {
+ "name": "knight_hacks_event_google_applied_revision_check",
+ "value": "\"knight_hacks_event\".\"google_applied_revision\" IS NULL OR \"knight_hacks_event\".\"google_applied_revision\" <= \"knight_hacks_event\".\"sync_revision\""
+ },
+ "knight_hacks_event_discord_synced_state_check": {
+ "name": "knight_hacks_event_discord_synced_state_check",
+ "value": "\"knight_hacks_event\".\"discord_sync_state\" IS DISTINCT FROM 'synced' OR (\"knight_hacks_event\".\"discord_id\" IS NOT NULL AND \"knight_hacks_event\".\"discord_applied_revision\" IS NOT NULL AND \"knight_hacks_event\".\"discord_applied_revision\" = \"knight_hacks_event\".\"sync_revision\" AND \"knight_hacks_event\".\"discord_applied_entity_type\" IS NOT NULL AND ((\"knight_hacks_event\".\"discord_applied_entity_type\" = 'external' AND \"knight_hacks_event\".\"discord_applied_channel_id\" IS NULL) OR (\"knight_hacks_event\".\"discord_applied_entity_type\" IN ('voice', 'stage') AND \"knight_hacks_event\".\"discord_applied_channel_id\" IS NOT NULL)))"
+ },
+ "knight_hacks_event_google_synced_state_check": {
+ "name": "knight_hacks_event_google_synced_state_check",
+ "value": "\"knight_hacks_event\".\"google_sync_state\" IS DISTINCT FROM 'synced' OR (\"knight_hacks_event\".\"google_id\" IS NOT NULL AND \"knight_hacks_event\".\"google_applied_revision\" IS NOT NULL AND \"knight_hacks_event\".\"google_applied_revision\" = \"knight_hacks_event\".\"sync_revision\" AND \"knight_hacks_event\".\"google_applied_destination\" IS NOT NULL AND \"knight_hacks_event\".\"google_applied_calendar_id\" IS NOT NULL)"
+ },
+ "knight_hacks_event_published_visibility_check": {
+ "name": "knight_hacks_event_published_visibility_check",
+ "value": "\"knight_hacks_event\".\"published_at\" IS NULL OR (\"knight_hacks_event\".\"visibility_revision\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_dues_paying\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_roles\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_internal\" IS NOT NULL)"
+ },
+ "knight_hacks_event_discord_acknowledgement_time_check": {
+ "name": "knight_hacks_event_discord_acknowledgement_time_check",
+ "value": "\"knight_hacks_event\".\"discord_no_projection_acknowledged_by\" IS NULL OR \"knight_hacks_event\".\"discord_no_projection_acknowledged_at\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_attendee": {
+ "name": "knight_hacks_event_attendee",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "checked_in_at": {
+ "name": "checked_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_by": {
+ "name": "checked_in_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded_estimated": {
+ "name": "points_awarded_estimated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_attendee_event_member_idx": {
+ "name": "knight_hacks_event_attendee_event_member_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_attendee_member_event_idx": {
+ "name": "knight_hacks_event_attendee_member_event_idx",
+ "columns": [
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_attendee_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_event_attendee_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_event_attendee",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_attendee_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_attendee_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_attendee",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_attendee_checked_in_by_auth_user_id_fk": {
+ "name": "knight_hacks_event_attendee_checked_in_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_event_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["checked_in_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_feedback": {
+ "name": "knight_hacks_event_feedback",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "overall_event_rating": {
+ "name": "overall_event_rating",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fun_rating": {
+ "name": "fun_rating",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "learned_rating": {
+ "name": "learned_rating",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "heard_about_us": {
+ "name": "heard_about_us",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "additional_feedback": {
+ "name": "additional_feedback",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "similar_event": {
+ "name": "similar_event",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_event_feedback_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_event_feedback_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_event_feedback",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_feedback_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_feedback",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_feedback_config": {
+ "name": "knight_hacks_event_feedback_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "closes_at": {
+ "name": "closes_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reward_points": {
+ "name": "reward_points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 5
+ },
+ "template_revision": {
+ "name": "template_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "custom_questions": {
+ "name": "custom_questions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_event_feedback_config_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_feedback_config_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_config",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_config_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_event_feedback_config_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_config",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_feedback_config_eventId_unique": {
+ "name": "knight_hacks_event_feedback_config_eventId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id"]
+ },
+ "knight_hacks_event_feedback_config_formId_unique": {
+ "name": "knight_hacks_event_feedback_config_formId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["form_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_feedback_config_reward_check": {
+ "name": "knight_hacks_event_feedback_config_reward_check",
+ "value": "\"knight_hacks_event_feedback_config\".\"reward_points\" = 5"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_feedback_reward": {
+ "name": "knight_hacks_event_feedback_reward",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 5
+ },
+ "awarded_at": {
+ "name": "awarded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_event_feedback_reward_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_feedback_reward_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_reward",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_reward_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_event_feedback_reward_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_reward",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_reward_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_event_feedback_reward_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_reward",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_feedback_reward_event_member_unique": {
+ "name": "knight_hacks_event_feedback_reward_event_member_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id", "member_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_feedback_reward_points_check": {
+ "name": "knight_hacks_event_feedback_reward_points_check",
+ "value": "\"knight_hacks_event_feedback_reward\".\"points_awarded\" = 5"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_publication_work": {
+ "name": "knight_hacks_event_publication_work",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "publication_id": {
+ "name": "publication_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "hackathon_event_publication_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_enabled": {
+ "name": "target_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_revision": {
+ "name": "event_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "publication_revision": {
+ "name": "publication_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "event_publication_work_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "last_attempt_at": {
+ "name": "last_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_token": {
+ "name": "lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_publication_work_due_idx": {
+ "name": "knight_hacks_event_publication_work_due_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_publication_work_health_idx": {
+ "name": "knight_hacks_event_publication_work_health_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_publication_work_lease_expiry_idx": {
+ "name": "knight_hacks_event_publication_work_lease_expiry_idx",
+ "columns": [
+ {
+ "expression": "lease_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_publication_work_scoped_publication_fk": {
+ "name": "knight_hacks_event_publication_work_scoped_publication_fk",
+ "tableFrom": "knight_hacks_event_publication_work",
+ "tableTo": "knight_hacks_hackathon_event_publication",
+ "columnsFrom": ["publication_id", "hackathon_id", "provider"],
+ "columnsTo": ["id", "hackathon_id", "provider"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_publication_work_scoped_event_fk": {
+ "name": "knight_hacks_event_publication_work_scoped_event_fk",
+ "tableFrom": "knight_hacks_event_publication_work",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_publication_work_event_provider_unique": {
+ "name": "knight_hacks_event_publication_work_event_provider_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id", "provider"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_publication_work_attempt_count_check": {
+ "name": "knight_hacks_event_publication_work_attempt_count_check",
+ "value": "\"knight_hacks_event_publication_work\".\"attempt_count\" >= 0"
+ },
+ "knight_hacks_event_publication_work_revisions_check": {
+ "name": "knight_hacks_event_publication_work_revisions_check",
+ "value": "\"knight_hacks_event_publication_work\".\"event_revision\" >= 1 AND \"knight_hacks_event_publication_work\".\"publication_revision\" >= 1"
+ },
+ "knight_hacks_event_publication_work_lease_pair_check": {
+ "name": "knight_hacks_event_publication_work_lease_pair_check",
+ "value": "(\"knight_hacks_event_publication_work\".\"lease_token\" IS NULL) = (\"knight_hacks_event_publication_work\".\"lease_expires_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_tag": {
+ "name": "knight_hacks_event_tag",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_name": {
+ "name": "normalized_name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "default_points": {
+ "name": "default_points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "color": {
+ "name": "color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_tag_club_normalized_name_unique": {
+ "name": "knight_hacks_event_tag_club_normalized_name_unique",
+ "columns": [
+ {
+ "expression": "normalized_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_event_tag\".\"hackathon_id\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_tag_hackathon_normalized_name_unique": {
+ "name": "knight_hacks_event_tag_hackathon_normalized_name_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "normalized_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_event_tag\".\"hackathon_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_tag_hackathon_idx": {
+ "name": "knight_hacks_event_tag_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_tag_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_event_tag_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_event_tag",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_tag_default_points_check": {
+ "name": "knight_hacks_event_tag_default_points_check",
+ "value": "\"knight_hacks_event_tag\".\"default_points\" >= 0"
+ },
+ "knight_hacks_event_tag_color_check": {
+ "name": "knight_hacks_event_tag_color_check",
+ "value": "\"knight_hacks_event_tag\".\"color\" ~ '^#[0-9A-Fa-f]{6}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_attachment": {
+ "name": "knight_hacks_form_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "object_name": {
+ "name": "object_name",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_name": {
+ "name": "file_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_type": {
+ "name": "content_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size": {
+ "name": "size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "purpose": {
+ "name": "purpose",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'response'"
+ },
+ "finalized_at": {
+ "name": "finalized_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_form_attachment_form_idx": {
+ "name": "knight_hacks_form_attachment_form_idx",
+ "columns": [
+ {
+ "expression": "form_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_form_attachment_response_idx": {
+ "name": "knight_hacks_form_attachment_response_idx",
+ "columns": [
+ {
+ "expression": "response_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_form_attachment_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_attachment_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_attachment",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_attachment_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_form_attachment_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_form_attachment",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_attachment_owner_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_form_attachment_owner_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_form_attachment",
+ "tableTo": "auth_user",
+ "columnsFrom": ["owner_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_attachment_objectName_unique": {
+ "name": "knight_hacks_form_attachment_objectName_unique",
+ "nullsNotDistinct": false,
+ "columns": ["object_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_form_attachment_purpose_check": {
+ "name": "knight_hacks_form_attachment_purpose_check",
+ "value": "\"knight_hacks_form_attachment\".\"purpose\" IN ('banner', 'instruction', 'response')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_callback_configuration": {
+ "name": "knight_hacks_form_callback_configuration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "callback_slug": {
+ "name": "callback_slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "mappings": {
+ "name": "mappings",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_callback_configuration_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_callback_configuration_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_callback_configuration",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_callback_configuration_form_slug_unique": {
+ "name": "knight_hacks_form_callback_configuration_form_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": ["form_id", "callback_slug"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_callback_execution": {
+ "name": "knight_hacks_form_callback_execution",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "configuration_id": {
+ "name": "configuration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callback_slug": {
+ "name": "callback_slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input": {
+ "name": "input",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "form_callback_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "available_at": {
+ "name": "available_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "lease_token": {
+ "name": "lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "succeeded_at": {
+ "name": "succeeded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_form_callback_execution_status_available_idx": {
+ "name": "knight_hacks_form_callback_execution_status_available_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "available_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_form_callback_execution_configuration_id_knight_hacks_form_callback_configuration_id_fk": {
+ "name": "knight_hacks_form_callback_execution_configuration_id_knight_hacks_form_callback_configuration_id_fk",
+ "tableFrom": "knight_hacks_form_callback_execution",
+ "tableTo": "knight_hacks_form_callback_configuration",
+ "columnsFrom": ["configuration_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_callback_execution_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_form_callback_execution_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_form_callback_execution",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_response": {
+ "name": "knight_hacks_form_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form": {
+ "name": "form",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_data": {
+ "name": "response_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "form_revision": {
+ "name": "form_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "response_snapshot": {
+ "name": "response_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "edited_at": {
+ "name": "edited_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_form_response_form_created_idx": {
+ "name": "knight_hacks_form_response_form_created_idx",
+ "columns": [
+ {
+ "expression": "form",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_form_response_user_created_idx": {
+ "name": "knight_hacks_form_response_user_created_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_form_response_form_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_response_form_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_response",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_response_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_form_response_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_form_response",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_response_roles": {
+ "name": "knight_hacks_form_response_roles",
+ "schema": "",
+ "columns": {
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_response_roles_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_response_roles_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_response_roles",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_response_roles_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_response_roles_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_response_roles",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_response_roles_form_id_role_id_pk": {
+ "name": "knight_hacks_form_response_roles_form_id_role_id_pk",
+ "columns": ["form_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_section_edit_role": {
+ "name": "knight_hacks_form_section_edit_role",
+ "schema": "",
+ "columns": {
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_section_edit_role_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_section_edit_role_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_section_edit_role",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_section_edit_role_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_section_edit_role_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_section_edit_role",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_section_edit_role_section_id_role_id_pk": {
+ "name": "knight_hacks_form_section_edit_role_section_id_role_id_pk",
+ "columns": ["section_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_section_roles": {
+ "name": "knight_hacks_form_section_roles",
+ "schema": "",
+ "columns": {
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_section_roles_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_section_roles_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_section_roles",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_section_roles_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_section_roles_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_section_roles",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_section_roles_section_id_role_id_pk": {
+ "name": "knight_hacks_form_section_roles_section_id_role_id_pk",
+ "columns": ["section_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_section_view_role": {
+ "name": "knight_hacks_form_section_view_role",
+ "schema": "",
+ "columns": {
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_section_view_role_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_section_view_role_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_section_view_role",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_section_view_role_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_section_view_role_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_section_view_role",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_section_view_role_section_id_role_id_pk": {
+ "name": "knight_hacks_form_section_view_role_section_id_role_id_pk",
+ "columns": ["section_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_sections": {
+ "name": "knight_hacks_form_sections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "order": {
+ "name": "order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_sections_name_unique": {
+ "name": "knight_hacks_form_sections_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_single_response_claim": {
+ "name": "knight_hacks_form_single_response_claim",
+ "schema": "",
+ "columns": {
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_single_response_claim_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_single_response_claim_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_single_response_claim",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_single_response_claim_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_form_single_response_claim_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_form_single_response_claim",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_single_response_claim_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_form_single_response_claim_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_form_single_response_claim",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_single_response_claim_form_id_user_id_pk": {
+ "name": "knight_hacks_form_single_response_claim_form_id_user_id_pk",
+ "columns": ["form_id", "user_id"]
+ }
+ },
+ "uniqueConstraints": {
+ "knight_hacks_form_single_response_claim_responseId_unique": {
+ "name": "knight_hacks_form_single_response_claim_responseId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["response_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_schemas": {
+ "name": "knight_hacks_form_schemas",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug_name": {
+ "name": "slug_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "kind": {
+ "name": "kind",
+ "type": "form_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'general'"
+ },
+ "state": {
+ "name": "state",
+ "type": "form_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "opens_at": {
+ "name": "opens_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "closes_at": {
+ "name": "closes_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "manually_closed": {
+ "name": "manually_closed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "response_mode": {
+ "name": "response_mode",
+ "type": "form_response_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'single_locked'"
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "dues_only": {
+ "name": "dues_only",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "allow_resubmission": {
+ "name": "allow_resubmission",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "allow_edit": {
+ "name": "allow_edit",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "form_data": {
+ "name": "form_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "form_validator_json": {
+ "name": "form_validator_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "section": {
+ "name": "section",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'General'"
+ },
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_closed": {
+ "name": "is_closed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_schemas_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_schemas_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_schemas",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_schemas_slugName_unique": {
+ "name": "knight_hacks_form_schemas_slugName_unique",
+ "nullsNotDistinct": false,
+ "columns": ["slug_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_guest_judge_session": {
+ "name": "knight_hacks_guest_judge_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_link_id": {
+ "name": "access_link_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "char(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_seen_at": {
+ "name": "last_seen_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_by_user_id": {
+ "name": "revoked_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revocation_reason": {
+ "name": "revocation_reason",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_guest_judge_session_access_link_idx": {
+ "name": "knight_hacks_guest_judge_session_access_link_idx",
+ "columns": [
+ {
+ "expression": "access_link_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_guest_judge_session_revoked_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_guest_judge_session_revoked_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_guest_judge_session",
+ "tableTo": "auth_user",
+ "columnsFrom": ["revoked_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_guest_judge_session_access_link_scope_fk": {
+ "name": "knight_hacks_guest_judge_session_access_link_scope_fk",
+ "tableFrom": "knight_hacks_guest_judge_session",
+ "tableTo": "knight_hacks_judging_room_access_link",
+ "columnsFrom": ["access_link_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_guest_judge_session_judge_scope_fk": {
+ "name": "knight_hacks_guest_judge_session_judge_scope_fk",
+ "tableFrom": "knight_hacks_guest_judge_session",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_guest_judge_session_token_hash_unique": {
+ "name": "knight_hacks_guest_judge_session_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon": {
+ "name": "knight_hacks_hackathon",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "theme": {
+ "name": "theme",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'America/New_York'"
+ },
+ "application_url": {
+ "name": "application_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "application_background_enabled": {
+ "name": "application_background_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "application_background_key": {
+ "name": "application_background_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_template_enabled": {
+ "name": "email_template_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "email_template_key": {
+ "name": "email_template_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "application_open": {
+ "name": "application_open",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "application_deadline": {
+ "name": "application_deadline",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "confirmation_deadline": {
+ "name": "confirmation_deadline",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "start_date": {
+ "name": "start_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_date": {
+ "name": "end_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confirmation_capacity": {
+ "name": "confirmation_capacity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "general_hacker_discord_role_id": {
+ "name": "general_hacker_discord_role_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_announcement_channel_id": {
+ "name": "event_announcement_channel_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_name_unique": {
+ "name": "knight_hacks_hackathon_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_event_announcement_channel_id_check": {
+ "name": "knight_hacks_hackathon_event_announcement_channel_id_check",
+ "value": "\"knight_hacks_hackathon\".\"event_announcement_channel_id\" IS NULL OR \"knight_hacks_hackathon\".\"event_announcement_channel_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_general_hacker_discord_role_id_check": {
+ "name": "knight_hacks_hackathon_general_hacker_discord_role_id_check",
+ "value": "\"knight_hacks_hackathon\".\"general_hacker_discord_role_id\" IS NULL OR \"knight_hacks_hackathon\".\"general_hacker_discord_role_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_confirmation_capacity_check": {
+ "name": "knight_hacks_hackathon_confirmation_capacity_check",
+ "value": "\"knight_hacks_hackathon\".\"confirmation_capacity\" IS NULL OR \"knight_hacks_hackathon\".\"confirmation_capacity\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_agreement_definition": {
+ "name": "knight_hacks_hackathon_agreement_definition",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stage": {
+ "name": "stage",
+ "type": "hacker_agreement_stage",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "legal_text": {
+ "name": "legal_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_agreement_definition_active_unique": {
+ "name": "knight_hacks_hackathon_agreement_definition_active_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "stage",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hackathon_agreement_definition\".\"active\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_agreement_definition_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_agreement_definition_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_agreement_definition",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_agreement_definition_created_by_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_agreement_definition_created_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_agreement_definition",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_agreement_definition_version_unique": {
+ "name": "knight_hacks_hackathon_agreement_definition_version_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "stage", "key", "version"]
+ },
+ "knight_hacks_hackathon_agreement_definition_id_hackathon_unique": {
+ "name": "knight_hacks_hackathon_agreement_definition_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_agreement_definition_content_check": {
+ "name": "knight_hacks_hackathon_agreement_definition_content_check",
+ "value": "\"knight_hacks_hackathon_agreement_definition\".\"legal_text\" IS NOT NULL OR \"knight_hacks_hackathon_agreement_definition\".\"url\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_class": {
+ "name": "knight_hacks_hackathon_class",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_role_id": {
+ "name": "discord_role_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "color": {
+ "name": "color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_class_hackathon_idx": {
+ "name": "knight_hacks_hackathon_class_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_class_one_vip_per_hackathon": {
+ "name": "knight_hacks_hackathon_class_one_vip_per_hackathon",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hackathon_class\".\"kind\" = 'vip'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_class_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_class_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_class",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_class_id_hackathon_unique": {
+ "name": "knight_hacks_hackathon_class_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_class_discord_role_id_check": {
+ "name": "knight_hacks_hackathon_class_discord_role_id_check",
+ "value": "\"knight_hacks_hackathon_class\".\"discord_role_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_class_color_check": {
+ "name": "knight_hacks_hackathon_class_color_check",
+ "value": "\"knight_hacks_hackathon_class\".\"color\" ~ '^#[0-9a-fA-F]{6}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_event_publication": {
+ "name": "knight_hacks_hackathon_event_publication",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "hackathon_event_publication_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "desired_enabled": {
+ "name": "desired_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "requested_at": {
+ "name": "requested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "requested_by": {
+ "name": "requested_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciled_at": {
+ "name": "last_reconciled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_converged_at": {
+ "name": "last_converged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_event_publication_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_event_publication_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_event_publication",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_event_publication_requested_by_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_event_publication_requested_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_event_publication",
+ "tableTo": "auth_user",
+ "columnsFrom": ["requested_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_event_publication_provider_unique": {
+ "name": "knight_hacks_hackathon_event_publication_provider_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "provider"]
+ },
+ "knight_hacks_hackathon_event_publication_id_scope_unique": {
+ "name": "knight_hacks_hackathon_event_publication_id_scope_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id", "provider"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_event_publication_revision_check": {
+ "name": "knight_hacks_hackathon_event_publication_revision_check",
+ "value": "\"knight_hacks_hackathon_event_publication\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_event_reminder_delivery": {
+ "name": "knight_hacks_hackathon_event_reminder_delivery",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_start_at": {
+ "name": "event_start_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reminder_key": {
+ "name": "reminder_key",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "destination_channel_id_snapshot": {
+ "name": "destination_channel_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id_snapshot": {
+ "name": "role_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_event_id_snapshot": {
+ "name": "discord_event_id_snapshot",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "content_snapshot": {
+ "name": "content_snapshot",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "hackathon_event_reminder_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "locked_at": {
+ "name": "locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_event_reminder_delivery_pending_idx": {
+ "name": "knight_hacks_hackathon_event_reminder_delivery_pending_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_event_reminder_scoped_event_fk": {
+ "name": "knight_hacks_hackathon_event_reminder_scoped_event_fk",
+ "tableFrom": "knight_hacks_hackathon_event_reminder_delivery",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_event_reminder_delivery_identity_unique": {
+ "name": "knight_hacks_hackathon_event_reminder_delivery_identity_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id", "reminder_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_event_reminder_channel_id_check": {
+ "name": "knight_hacks_hackathon_event_reminder_channel_id_check",
+ "value": "\"knight_hacks_hackathon_event_reminder_delivery\".\"destination_channel_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_event_reminder_role_id_check": {
+ "name": "knight_hacks_hackathon_event_reminder_role_id_check",
+ "value": "\"knight_hacks_hackathon_event_reminder_delivery\".\"role_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_event_reminder_attempt_count_check": {
+ "name": "knight_hacks_hackathon_event_reminder_attempt_count_check",
+ "value": "\"knight_hacks_hackathon_event_reminder_delivery\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_judging_configuration": {
+ "name": "knight_hacks_hackathon_judging_configuration",
+ "schema": "",
+ "columns": {
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "project_inventory_locked_at": {
+ "name": "project_inventory_locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_inventory_locked_by_user_id": {
+ "name": "project_inventory_locked_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "judging_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "display_all_results_to_members": {
+ "name": "display_all_results_to_members",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "judging_comms_channel_id": {
+ "name": "judging_comms_channel_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "opened_at": {
+ "name": "opened_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "closed_at": {
+ "name": "closed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_judging_configuration_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_judging_configuration_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_judging_configuration",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_judging_configuration_project_inventory_locked_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_judging_configuration_project_inventory_locked_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_judging_configuration",
+ "tableTo": "auth_user",
+ "columnsFrom": ["project_inventory_locked_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_authorization_code": {
+ "name": "knight_hacks_hackathon_portal_authorization_code",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "portal_client_id": {
+ "name": "portal_client_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "better_auth_session_id": {
+ "name": "better_auth_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code_hash": {
+ "name": "code_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code_challenge": {
+ "name": "code_challenge",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code_challenge_method": {
+ "name": "code_challenge_method",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'S256'"
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_portal_authorization_code_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_expiry_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_portal_authorization_code_client_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_client_expiry_idx",
+ "columns": [
+ {
+ "expression": "portal_client_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_authorization_code_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_authorization_code",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_authorization_code_better_auth_session_id_auth_session_id_fk": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_better_auth_session_id_auth_session_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_authorization_code",
+ "tableTo": "auth_session",
+ "columnsFrom": ["better_auth_session_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_authorization_code_scoped_client_fk": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_scoped_client_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_authorization_code",
+ "tableTo": "knight_hacks_hackathon_portal_client",
+ "columnsFrom": ["portal_client_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_authorization_code_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["code_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_authorization_code_hash_check": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_authorization_code\".\"code_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_authorization_code_method_check": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_method_check",
+ "value": "\"knight_hacks_hackathon_portal_authorization_code\".\"code_challenge_method\" = 'S256'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_client": {
+ "name": "knight_hacks_hackathon_portal_client",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_id": {
+ "name": "client_id",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "production_origin": {
+ "name": "production_origin",
+ "type": "varchar(2048)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_client_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_portal_client_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_client",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_client_created_by_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_portal_client_created_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_client",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_client_client_id_unique": {
+ "name": "knight_hacks_hackathon_portal_client_client_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["client_id"]
+ },
+ "knight_hacks_hackathon_portal_client_hackathon_unique": {
+ "name": "knight_hacks_hackathon_portal_client_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id"]
+ },
+ "knight_hacks_hackathon_portal_client_origin_unique": {
+ "name": "knight_hacks_hackathon_portal_client_origin_unique",
+ "nullsNotDistinct": false,
+ "columns": ["production_origin"]
+ },
+ "knight_hacks_hackathon_portal_client_id_hackathon_unique": {
+ "name": "knight_hacks_hackathon_portal_client_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_client_origin_check": {
+ "name": "knight_hacks_hackathon_portal_client_origin_check",
+ "value": "\"knight_hacks_hackathon_portal_client\".\"production_origin\" ~ '^https://[a-z0-9-]+([.][a-z0-9-]+)*[.]knighthacks[.]org$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_session": {
+ "name": "knight_hacks_hackathon_portal_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "portal_client_id": {
+ "name": "portal_client_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "better_auth_session_id": {
+ "name": "better_auth_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token_hash": {
+ "name": "access_token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_hash": {
+ "name": "refresh_token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_expires_at": {
+ "name": "access_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_expires_at": {
+ "name": "refresh_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_version": {
+ "name": "refresh_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_portal_session_access_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_session_access_expiry_idx",
+ "columns": [
+ {
+ "expression": "access_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "revoked_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_portal_session_refresh_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_expiry_idx",
+ "columns": [
+ {
+ "expression": "refresh_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_portal_session_user_client_idx": {
+ "name": "knight_hacks_hackathon_portal_session_user_client_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "portal_client_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_session_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_portal_session_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_session_better_auth_session_id_auth_session_id_fk": {
+ "name": "knight_hacks_hackathon_portal_session_better_auth_session_id_auth_session_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session",
+ "tableTo": "auth_session",
+ "columnsFrom": ["better_auth_session_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_session_scoped_client_fk": {
+ "name": "knight_hacks_hackathon_portal_session_scoped_client_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session",
+ "tableTo": "knight_hacks_hackathon_portal_client",
+ "columnsFrom": ["portal_client_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_session_access_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_session_access_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["access_token_hash"]
+ },
+ "knight_hacks_hackathon_portal_session_refresh_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["refresh_token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_session_access_hash_check": {
+ "name": "knight_hacks_hackathon_portal_session_access_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_session\".\"access_token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_session_refresh_hash_check": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_session\".\"refresh_token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_session_refresh_version_check": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_version_check",
+ "value": "\"knight_hacks_hackathon_portal_session\".\"refresh_version\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_session_credential": {
+ "name": "knight_hacks_hackathon_portal_session_credential",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "portal_session_id": {
+ "name": "portal_session_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_kind": {
+ "name": "token_kind",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rotated_at": {
+ "name": "rotated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_portal_session_credential_session_idx": {
+ "name": "knight_hacks_hackathon_portal_session_credential_session_idx",
+ "columns": [
+ {
+ "expression": "portal_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_session_credential_portal_session_id_knight_hacks_hackathon_portal_session_id_fk": {
+ "name": "knight_hacks_hackathon_portal_session_credential_portal_session_id_knight_hacks_hackathon_portal_session_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session_credential",
+ "tableTo": "knight_hacks_hackathon_portal_session",
+ "columnsFrom": ["portal_session_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_session_credential_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_session_credential_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_session_credential_hash_check": {
+ "name": "knight_hacks_hackathon_portal_session_credential_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_session_credential\".\"token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_session_credential_kind_check": {
+ "name": "knight_hacks_hackathon_portal_session_credential_kind_check",
+ "value": "\"knight_hacks_hackathon_portal_session_credential\".\"token_kind\" IN ('access', 'refresh')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_sponsor": {
+ "name": "knight_hacks_hackathon_sponsor",
+ "schema": "",
+ "columns": {
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sponsor_id": {
+ "name": "sponsor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tier": {
+ "name": "tier",
+ "type": "sponsor_tier",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_sponsor_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_sponsor_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_sponsor",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_sponsor_sponsor_id_knight_hacks_sponsor_id_fk": {
+ "name": "knight_hacks_hackathon_sponsor_sponsor_id_knight_hacks_sponsor_id_fk",
+ "tableFrom": "knight_hacks_hackathon_sponsor",
+ "tableTo": "knight_hacks_sponsor",
+ "columnsFrom": ["sponsor_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_status_email": {
+ "name": "knight_hacks_hackathon_status_email",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "varchar(200)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_status_email_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_status_email_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_status_email",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_status_email_template_id_email_template_id_fk": {
+ "name": "knight_hacks_hackathon_status_email_template_id_email_template_id_fk",
+ "tableFrom": "knight_hacks_hackathon_status_email",
+ "tableTo": "email_template",
+ "columnsFrom": ["template_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_status_email_hackathon_status_unique": {
+ "name": "knight_hacks_hackathon_status_email_hackathon_status_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "status"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_status_email_status_check": {
+ "name": "knight_hacks_hackathon_status_email_status_check",
+ "value": "\"knight_hacks_hackathon_status_email\".\"status\" <> 'checkedin'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker": {
+ "name": "knight_hacks_hacker",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "age": {
+ "name": "age",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'United States of America'"
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Computer Science'"
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "survey_1": {
+ "name": "survey_1",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "survey_2": {
+ "name": "survey_2",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_first_time": {
+ "name": "is_first_time",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "food_allergies": {
+ "name": "food_allergies",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agrees_to_receive_emails_from_mlh": {
+ "name": "agrees_to_receive_emails_from_mlh",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "agrees_to_mlh_code_of_conduct": {
+ "name": "agrees_to_mlh_code_of_conduct",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "agrees_to_mlh_data_sharing": {
+ "name": "agrees_to_mlh_data_sharing",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "date_created": {
+ "name": "date_created",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "time_created": {
+ "name": "time_created",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hacker_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_agreement_acceptance": {
+ "name": "knight_hacks_hacker_agreement_acceptance",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "attendee_id": {
+ "name": "attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agreement_definition_id": {
+ "name": "agreement_definition_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "accepted": {
+ "name": "accepted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provenance": {
+ "name": "provenance",
+ "type": "hacker_agreement_provenance",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'explicit'"
+ },
+ "accepted_at": {
+ "name": "accepted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "recorded_at": {
+ "name": "recorded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_agreement_acceptance_attendee_idx": {
+ "name": "knight_hacks_hacker_agreement_acceptance_attendee_idx",
+ "columns": [
+ {
+ "expression": "attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_agreement_acceptance_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_agreement_acceptance_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_agreement_acceptance",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_agreement_acceptance_scoped_definition_fk": {
+ "name": "knight_hacks_hacker_agreement_acceptance_scoped_definition_fk",
+ "tableFrom": "knight_hacks_hacker_agreement_acceptance",
+ "tableTo": "knight_hacks_hackathon_agreement_definition",
+ "columnsFrom": ["agreement_definition_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_agreement_acceptance_attendee_definition_unique": {
+ "name": "knight_hacks_hacker_agreement_acceptance_attendee_definition_unique",
+ "nullsNotDistinct": false,
+ "columns": ["attendee_id", "agreement_definition_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_agreement_acceptance_timestamp_check": {
+ "name": "knight_hacks_hacker_agreement_acceptance_timestamp_check",
+ "value": "(\"knight_hacks_hacker_agreement_acceptance\".\"accepted\" = true AND \"knight_hacks_hacker_agreement_acceptance\".\"accepted_at\" IS NOT NULL) OR (\"knight_hacks_hacker_agreement_acceptance\".\"accepted\" = false AND \"knight_hacks_hacker_agreement_acceptance\".\"accepted_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_attendee": {
+ "name": "knight_hacks_hacker_attendee",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hacker_id": {
+ "name": "hacker_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_revision_id": {
+ "name": "profile_revision_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "time_applied": {
+ "name": "time_applied",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "time_confirmed": {
+ "name": "time_confirmed",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points": {
+ "name": "points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "class_id": {
+ "name": "class_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_vip": {
+ "name": "is_vip",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_first_time": {
+ "name": "is_first_time",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "survey1": {
+ "name": "survey1",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "survey2": {
+ "name": "survey2",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_at": {
+ "name": "checked_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_by": {
+ "name": "checked_in_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class": {
+ "name": "class",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": null
+ },
+ "blacklisted_at": {
+ "name": "blacklisted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "blacklisted_by": {
+ "name": "blacklisted_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "blacklist_reason": {
+ "name": "blacklist_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_status_send_id": {
+ "name": "last_status_send_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_attendee_class_idx": {
+ "name": "knight_hacks_hacker_attendee_class_idx",
+ "columns": [
+ {
+ "expression": "class_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_hackathon_class_idx": {
+ "name": "knight_hacks_hacker_attendee_hackathon_class_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "class_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_checked_in_by_idx": {
+ "name": "knight_hacks_hacker_attendee_checked_in_by_idx",
+ "columns": [
+ {
+ "expression": "checked_in_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_hackathon_checked_in_idx": {
+ "name": "knight_hacks_hacker_attendee_hackathon_checked_in_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_profile_hackathon_unique": {
+ "name": "knight_hacks_hacker_attendee_profile_hackathon_unique",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hacker_attendee\".\"profile_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_profile_idx": {
+ "name": "knight_hacks_hacker_attendee_profile_idx",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_profile_revision_idx": {
+ "name": "knight_hacks_hacker_attendee_profile_revision_idx",
+ "columns": [
+ {
+ "expression": "profile_revision_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_hackathon_status_idx": {
+ "name": "knight_hacks_hacker_attendee_hackathon_status_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_blacklisted_by_idx": {
+ "name": "knight_hacks_hacker_attendee_blacklisted_by_idx",
+ "columns": [
+ {
+ "expression": "blacklisted_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_last_status_send_idx": {
+ "name": "knight_hacks_hacker_attendee_last_status_send_idx",
+ "columns": [
+ {
+ "expression": "last_status_send_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_attendee_hacker_id_knight_hacks_hacker_id_fk": {
+ "name": "knight_hacks_hacker_attendee_hacker_id_knight_hacks_hacker_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hacker",
+ "columnsFrom": ["hacker_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_profile_id_knight_hacks_hacker_profile_id_fk": {
+ "name": "knight_hacks_hacker_attendee_profile_id_knight_hacks_hacker_profile_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hacker_profile",
+ "columnsFrom": ["profile_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hacker_attendee_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_checked_in_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_attendee_checked_in_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["checked_in_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_blacklisted_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_attendee_blacklisted_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["blacklisted_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_last_status_send_id_email_send_id_fk": {
+ "name": "knight_hacks_hacker_attendee_last_status_send_id_email_send_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "email_send",
+ "columnsFrom": ["last_status_send_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_scoped_class_fk": {
+ "name": "knight_hacks_hacker_attendee_scoped_class_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hackathon_class",
+ "columnsFrom": ["class_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_scoped_profile_revision_fk": {
+ "name": "knight_hacks_hacker_attendee_scoped_profile_revision_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hacker_profile_revision",
+ "columnsFrom": ["profile_revision_id", "profile_id"],
+ "columnsTo": ["id", "profile_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_attendee_hacker_hackathon_unique": {
+ "name": "knight_hacks_hacker_attendee_hacker_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hacker_id", "hackathon_id"]
+ },
+ "knight_hacks_hacker_attendee_id_hackathon_unique": {
+ "name": "knight_hacks_hacker_attendee_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_attendee_blacklist_reason_check": {
+ "name": "knight_hacks_hacker_attendee_blacklist_reason_check",
+ "value": "(\"knight_hacks_hacker_attendee\".\"blacklisted_at\" is null and \"knight_hacks_hacker_attendee\".\"blacklist_reason\" is null)\n or (\"knight_hacks_hacker_attendee\".\"blacklisted_at\" is not null and \"knight_hacks_hacker_attendee\".\"blacklist_reason\" is not null)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_check_in_attempt": {
+ "name": "knight_hacks_hacker_check_in_attempt",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hacker_attendee_id": {
+ "name": "hacker_attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attendance_id": {
+ "name": "attendance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "operator_id": {
+ "name": "operator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "operator_display_name_snapshot": {
+ "name": "operator_display_name_snapshot",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mode": {
+ "name": "mode",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "hacker_check_in_outcome",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_purpose": {
+ "name": "event_purpose",
+ "type": "event_purpose",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_name_snapshot": {
+ "name": "event_name_snapshot",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hacker_name_snapshot": {
+ "name": "hacker_name_snapshot",
+ "type": "varchar(511)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class_id": {
+ "name": "class_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class_name_snapshot": {
+ "name": "class_name_snapshot",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class_color_snapshot": {
+ "name": "class_color_snapshot",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_vip_snapshot": {
+ "name": "is_vip_snapshot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "was_minor_at_attempt": {
+ "name": "was_minor_at_attempt",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_repeat_occurrence": {
+ "name": "is_repeat_occurrence",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "attempted_at": {
+ "name": "attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_check_in_attempt_hackathon_history_idx": {
+ "name": "knight_hacks_hacker_check_in_attempt_hackathon_history_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "attempted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_check_in_attempt_attendee_history_idx": {
+ "name": "knight_hacks_hacker_check_in_attempt_attendee_history_idx",
+ "columns": [
+ {
+ "expression": "hacker_attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "attempted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_check_in_attempt_expiring_idx": {
+ "name": "knight_hacks_hacker_check_in_attempt_expiring_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"knight_hacks_hacker_check_in_attempt\".\"expires_at\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_check_in_attempt_operator_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_operator_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "auth_user",
+ "columnsFrom": ["operator_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["hacker_attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_attendance_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_attendance_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_hacker_event_attendee",
+ "columnsFrom": [
+ "attendance_id",
+ "hackathon_id",
+ "event_id",
+ "hacker_attendee_id"
+ ],
+ "columnsTo": ["id", "hackathon_id", "event_id", "hacker_att_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_class_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_class_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_hackathon_class",
+ "columnsFrom": ["class_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_event_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_event_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_check_in_attempt_attendanceId_unique": {
+ "name": "knight_hacks_hacker_check_in_attempt_attendanceId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["attendance_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_check_in_attempt_mode_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_mode_check",
+ "value": "\"knight_hacks_hacker_check_in_attempt\".\"mode\" IN ('scanner', 'manual')"
+ },
+ "knight_hacks_hacker_check_in_attempt_class_color_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_class_color_check",
+ "value": "\"knight_hacks_hacker_check_in_attempt\".\"class_color_snapshot\" IS NULL OR \"knight_hacks_hacker_check_in_attempt\".\"class_color_snapshot\" ~ '^#[0-9A-Fa-f]{6}$'"
+ },
+ "knight_hacks_hacker_check_in_attempt_points_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_points_check",
+ "value": "\"knight_hacks_hacker_check_in_attempt\".\"points_awarded\" >= 0"
+ },
+ "knight_hacks_hacker_check_in_attempt_success_retention_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_success_retention_check",
+ "value": "(\"knight_hacks_hacker_check_in_attempt\".\"outcome\" = 'checked_in' AND \"knight_hacks_hacker_check_in_attempt\".\"attendance_id\" IS NOT NULL AND \"knight_hacks_hacker_check_in_attempt\".\"expires_at\" IS NULL) OR (\"knight_hacks_hacker_check_in_attempt\".\"outcome\" <> 'checked_in' AND \"knight_hacks_hacker_check_in_attempt\".\"attendance_id\" IS NULL AND \"knight_hacks_hacker_check_in_attempt\".\"expires_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_check_in_pass": {
+ "name": "knight_hacks_hacker_check_in_pass",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "attendee_id": {
+ "name": "attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "issued_at": {
+ "name": "issued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_check_in_pass_active_attendee_unique": {
+ "name": "knight_hacks_hacker_check_in_pass_active_attendee_unique",
+ "columns": [
+ {
+ "expression": "attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hacker_check_in_pass\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_check_in_pass_attendee_idx": {
+ "name": "knight_hacks_hacker_check_in_pass_attendee_idx",
+ "columns": [
+ {
+ "expression": "attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_check_in_pass_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_check_in_pass_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_pass",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_check_in_pass_token_hash_unique": {
+ "name": "knight_hacks_hacker_check_in_pass_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_check_in_pass_token_hash_check": {
+ "name": "knight_hacks_hacker_check_in_pass_token_hash_check",
+ "value": "\"knight_hacks_hacker_check_in_pass\".\"token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hacker_check_in_pass_version_check": {
+ "name": "knight_hacks_hacker_check_in_pass_version_check",
+ "value": "\"knight_hacks_hacker_check_in_pass\".\"version\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_discord_role_grant": {
+ "name": "knight_hacks_hacker_discord_role_grant",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hacker_attendee_id": {
+ "name": "hacker_attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_attendance_id": {
+ "name": "source_attendance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_event_id": {
+ "name": "source_event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "hacker_discord_role_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "desired_role_id": {
+ "name": "desired_role_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "hacker_discord_role_grant_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_attempt_at": {
+ "name": "last_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "succeeded_at": {
+ "name": "succeeded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_token": {
+ "name": "lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_discord_role_grant_repair_queue_idx": {
+ "name": "knight_hacks_hacker_discord_role_grant_repair_queue_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "lease_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_discord_role_grant_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["hacker_attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_discord_role_grant_scoped_attendance_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_scoped_attendance_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant",
+ "tableTo": "knight_hacks_hacker_event_attendee",
+ "columnsFrom": [
+ "source_attendance_id",
+ "hackathon_id",
+ "source_event_id",
+ "hacker_attendee_id"
+ ],
+ "columnsTo": ["id", "hackathon_id", "event_id", "hacker_att_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_discord_role_grant_logical_unique": {
+ "name": "knight_hacks_hacker_discord_role_grant_logical_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hacker_attendee_id", "kind"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_discord_role_grant_role_id_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_role_id_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant\".\"desired_role_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_count_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_count_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant\".\"attempt_count\" >= 0"
+ },
+ "knight_hacks_hacker_discord_role_grant_lease_pair_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_lease_pair_check",
+ "value": "(\"knight_hacks_hacker_discord_role_grant\".\"lease_token\" IS NULL) = (\"knight_hacks_hacker_discord_role_grant\".\"lease_expires_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_discord_role_grant_attempt": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "grant_id": {
+ "name": "grant_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempt_token": {
+ "name": "attempt_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id_snapshot": {
+ "name": "role_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user_id_snapshot": {
+ "name": "discord_user_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempted_by": {
+ "name": "attempted_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "hacker_discord_role_attempt_outcome",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "error": {
+ "name": "error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_discord_role_grant_attempt_history_idx": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_history_idx",
+ "columns": [
+ {
+ "expression": "grant_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_discord_role_grant_attempt_grant_id_knight_hacks_hacker_discord_role_grant_id_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_grant_id_knight_hacks_hacker_discord_role_grant_id_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant_attempt",
+ "tableTo": "knight_hacks_hacker_discord_role_grant",
+ "columnsFrom": ["grant_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_attempted_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_attempted_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant_attempt",
+ "tableTo": "auth_user",
+ "columnsFrom": ["attempted_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_discord_role_grant_attempt_attemptToken_unique": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_attemptToken_unique",
+ "nullsNotDistinct": false,
+ "columns": ["attempt_token"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_discord_role_grant_attempt_role_id_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_role_id_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant_attempt\".\"role_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_user_id_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_user_id_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant_attempt\".\"discord_user_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_completion_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_completion_check",
+ "value": "(\"knight_hacks_hacker_discord_role_grant_attempt\".\"outcome\" = 'pending' AND \"knight_hacks_hacker_discord_role_grant_attempt\".\"finished_at\" IS NULL) OR (\"knight_hacks_hacker_discord_role_grant_attempt\".\"outcome\" <> 'pending' AND \"knight_hacks_hacker_discord_role_grant_attempt\".\"finished_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_event_attendee": {
+ "name": "knight_hacks_hacker_event_attendee",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hacker_att_id": {
+ "name": "hacker_att_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "checked_in_at": {
+ "name": "checked_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_by": {
+ "name": "checked_in_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_initial_attendance": {
+ "name": "is_initial_attendance",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "voided_at": {
+ "name": "voided_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "voided_by": {
+ "name": "voided_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "void_reason": {
+ "name": "void_reason",
+ "type": "varchar(300)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_event_attendee_event_attendee_history_idx": {
+ "name": "knight_hacks_hacker_event_attendee_event_attendee_history_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "hacker_att_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_attendee_event_history_idx": {
+ "name": "knight_hacks_hacker_event_attendee_attendee_event_history_idx",
+ "columns": [
+ {
+ "expression": "hacker_att_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_hackathon_history_idx": {
+ "name": "knight_hacks_hacker_event_attendee_hackathon_history_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_checked_in_by_idx": {
+ "name": "knight_hacks_hacker_event_attendee_checked_in_by_idx",
+ "columns": [
+ {
+ "expression": "checked_in_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_voided_by_idx": {
+ "name": "knight_hacks_hacker_event_attendee_voided_by_idx",
+ "columns": [
+ {
+ "expression": "voided_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_one_active_initial": {
+ "name": "knight_hacks_hacker_event_attendee_one_active_initial",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "hacker_att_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hacker_event_attendee\".\"is_initial_attendance\" = true AND \"knight_hacks_hacker_event_attendee\".\"voided_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_event_attendee_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hacker_event_attendee_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_checked_in_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_event_attendee_checked_in_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["checked_in_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_voided_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_event_attendee_voided_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["voided_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_event_attendee_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["hacker_att_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_scoped_event_fk": {
+ "name": "knight_hacks_hacker_event_attendee_scoped_event_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_event_attendee_attempt_scope_unique": {
+ "name": "knight_hacks_hacker_event_attendee_attempt_scope_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id", "event_id", "hacker_att_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_event_attendee_points_awarded_check": {
+ "name": "knight_hacks_hacker_event_attendee_points_awarded_check",
+ "value": "\"knight_hacks_hacker_event_attendee\".\"points_awarded\" IS NULL OR \"knight_hacks_hacker_event_attendee\".\"points_awarded\" >= 0"
+ },
+ "knight_hacks_hacker_event_attendee_void_metadata_check": {
+ "name": "knight_hacks_hacker_event_attendee_void_metadata_check",
+ "value": "(\"knight_hacks_hacker_event_attendee\".\"voided_at\" IS NULL AND \"knight_hacks_hacker_event_attendee\".\"void_reason\" IS NULL) OR (\"knight_hacks_hacker_event_attendee\".\"voided_at\" IS NOT NULL AND \"knight_hacks_hacker_event_attendee\".\"void_reason\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_participant_command": {
+ "name": "knight_hacks_hacker_participant_command",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "operation": {
+ "name": "operation",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload_hash": {
+ "name": "payload_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "hacker_participant_command_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'started'"
+ },
+ "result": {
+ "name": "result",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "safe_error_code": {
+ "name": "safe_error_code",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_participant_command_expiry_idx": {
+ "name": "knight_hacks_hacker_participant_command_expiry_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_participant_command_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_participant_command_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_participant_command",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_participant_command_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hacker_participant_command_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hacker_participant_command",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_participant_command_identity_unique": {
+ "name": "knight_hacks_hacker_participant_command_identity_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "hackathon_id", "operation", "idempotency_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_participant_command_payload_hash_check": {
+ "name": "knight_hacks_hacker_participant_command_payload_hash_check",
+ "value": "\"knight_hacks_hacker_participant_command\".\"payload_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hacker_participant_command_completion_check": {
+ "name": "knight_hacks_hacker_participant_command_completion_check",
+ "value": "(\"knight_hacks_hacker_participant_command\".\"state\" = 'started' AND \"knight_hacks_hacker_participant_command\".\"completed_at\" IS NULL) OR (\"knight_hacks_hacker_participant_command\".\"state\" <> 'started' AND \"knight_hacks_hacker_participant_command\".\"completed_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_profile": {
+ "name": "knight_hacks_hacker_profile",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "food_allergies": {
+ "name": "food_allergies",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hacker_profile_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_profile_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_profile_user_unique": {
+ "name": "knight_hacks_hacker_profile_user_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_profile_revision_check": {
+ "name": "knight_hacks_hacker_profile_revision_check",
+ "value": "\"knight_hacks_hacker_profile\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_profile_revision": {
+ "name": "knight_hacks_hacker_profile_revision",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "legacy_hacker_id": {
+ "name": "legacy_hacker_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "food_allergies": {
+ "name": "food_allergies",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_profile_revision_profile_created_idx": {
+ "name": "knight_hacks_hacker_profile_revision_profile_created_idx",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_profile_revision_profile_id_knight_hacks_hacker_profile_id_fk": {
+ "name": "knight_hacks_hacker_profile_revision_profile_id_knight_hacks_hacker_profile_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile_revision",
+ "tableTo": "knight_hacks_hacker_profile",
+ "columnsFrom": ["profile_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_profile_revision_legacy_hacker_id_knight_hacks_hacker_id_fk": {
+ "name": "knight_hacks_hacker_profile_revision_legacy_hacker_id_knight_hacks_hacker_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile_revision",
+ "tableTo": "knight_hacks_hacker",
+ "columnsFrom": ["legacy_hacker_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_profile_revision_created_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_profile_revision_created_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile_revision",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_profile_revision_legacyHackerId_unique": {
+ "name": "knight_hacks_hacker_profile_revision_legacyHackerId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["legacy_hacker_id"]
+ },
+ "knight_hacks_hacker_profile_revision_profile_version_unique": {
+ "name": "knight_hacks_hacker_profile_revision_profile_version_unique",
+ "nullsNotDistinct": false,
+ "columns": ["profile_id", "revision"]
+ },
+ "knight_hacks_hacker_profile_revision_id_profile_unique": {
+ "name": "knight_hacks_hacker_profile_revision_id_profile_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "profile_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_profile_revision_version_check": {
+ "name": "knight_hacks_hacker_profile_revision_version_check",
+ "value": "\"knight_hacks_hacker_profile_revision\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue": {
+ "name": "knight_hacks_issue",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "status": {
+ "name": "status",
+ "type": "issue_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "links": {
+ "name": "links",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event": {
+ "name": "event",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_thread_id": {
+ "name": "discord_thread_id",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "date": {
+ "name": "date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "due_at": {
+ "name": "due_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "issue_priority",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team": {
+ "name": "team",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator": {
+ "name": "creator",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "parent": {
+ "name": "parent",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_by": {
+ "name": "archived_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archive_batch_id": {
+ "name": "archive_batch_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_key": {
+ "name": "creation_key",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_hash": {
+ "name": "creation_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_team_idx": {
+ "name": "issue_team_idx",
+ "columns": [
+ {
+ "expression": "team",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_creator_idx": {
+ "name": "issue_creator_idx",
+ "columns": [
+ {
+ "expression": "creator",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_status_idx": {
+ "name": "issue_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_date_idx": {
+ "name": "issue_date_idx",
+ "columns": [
+ {
+ "expression": "date",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_due_at_idx": {
+ "name": "issue_due_at_idx",
+ "columns": [
+ {
+ "expression": "due_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_archive_idx": {
+ "name": "issue_archive_idx",
+ "columns": [
+ {
+ "expression": "archived_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "archive_batch_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_parent_idx": {
+ "name": "issue_parent_idx",
+ "columns": [
+ {
+ "expression": "parent",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_priority_idx": {
+ "name": "issue_priority_idx",
+ "columns": [
+ {
+ "expression": "priority",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_event_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_issue_event_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_team_auth_roles_id_fk": {
+ "name": "knight_hacks_issue_team_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["team"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_creator_auth_user_id_fk": {
+ "name": "knight_hacks_issue_creator_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "auth_user",
+ "columnsFrom": ["creator"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_archived_by_auth_user_id_fk": {
+ "name": "knight_hacks_issue_archived_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "auth_user",
+ "columnsFrom": ["archived_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issue_parent_fk": {
+ "name": "issue_parent_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["parent"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_issue_creation_key_unique": {
+ "name": "knight_hacks_issue_creation_key_unique",
+ "nullsNotDistinct": false,
+ "columns": ["creation_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_attachment": {
+ "name": "knight_hacks_issue_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "draft_key": {
+ "name": "draft_key",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "object_name": {
+ "name": "object_name",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_name": {
+ "name": "file_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_type": {
+ "name": "content_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size": {
+ "name": "size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "finalized_at": {
+ "name": "finalized_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_issue_attachment_issue_idx": {
+ "name": "knight_hacks_issue_attachment_issue_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_issue_attachment_draft_idx": {
+ "name": "knight_hacks_issue_attachment_draft_idx",
+ "columns": [
+ {
+ "expression": "draft_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_attachment_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_attachment_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_attachment_team_id_auth_roles_id_fk": {
+ "name": "knight_hacks_issue_attachment_team_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["team_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_attachment_owner_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_issue_attachment_owner_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment",
+ "tableTo": "auth_user",
+ "columnsFrom": ["owner_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_issue_attachment_objectName_unique": {
+ "name": "knight_hacks_issue_attachment_objectName_unique",
+ "nullsNotDistinct": false,
+ "columns": ["object_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_issue_attachment_owner_check": {
+ "name": "knight_hacks_issue_attachment_owner_check",
+ "value": "NOT (\"knight_hacks_issue_attachment\".\"issue_id\" IS NOT NULL AND \"knight_hacks_issue_attachment\".\"draft_key\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_attachment_reference": {
+ "name": "knight_hacks_issue_attachment_reference",
+ "schema": "",
+ "columns": {
+ "attachment_id": {
+ "name": "attachment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "issue_attachment_reference_issue_idx": {
+ "name": "issue_attachment_reference_issue_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_attachment_reference_attachment_id_knight_hacks_issue_attachment_id_fk": {
+ "name": "knight_hacks_issue_attachment_reference_attachment_id_knight_hacks_issue_attachment_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment_reference",
+ "tableTo": "knight_hacks_issue_attachment",
+ "columnsFrom": ["attachment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_attachment_reference_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_attachment_reference_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment_reference",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_issue_attachment_reference_attachment_id_issue_id_pk": {
+ "name": "knight_hacks_issue_attachment_reference_attachment_id_issue_id_pk",
+ "columns": ["attachment_id", "issue_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_history": {
+ "name": "knight_hacks_issue_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_display_name": {
+ "name": "actor_display_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "changed_fields": {
+ "name": "changed_fields",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "before": {
+ "name": "before",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "after": {
+ "name": "after",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_history_issue_created_idx": {
+ "name": "issue_history_issue_created_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_history_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_history_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_history",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_history_actor_id_auth_user_id_fk": {
+ "name": "knight_hacks_issue_history_actor_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue_history",
+ "tableTo": "auth_user",
+ "columnsFrom": ["actor_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_reminder_delivery": {
+ "name": "knight_hacks_issue_reminder_delivery",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "due_at": {
+ "name": "due_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reminder_key": {
+ "name": "reminder_key",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "destination_snapshot": {
+ "name": "destination_snapshot",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_snapshot": {
+ "name": "content_snapshot",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar(24)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "locked_at": {
+ "name": "locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_reminder_delivery_pending_idx": {
+ "name": "issue_reminder_delivery_pending_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_reminder_delivery_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_reminder_delivery_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_reminder_delivery",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "issue_reminder_delivery_identity_unique": {
+ "name": "issue_reminder_delivery_identity_unique",
+ "nullsNotDistinct": false,
+ "columns": ["issue_id", "due_at", "reminder_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issues_to_teams_visibility": {
+ "name": "knight_hacks_issues_to_teams_visibility",
+ "schema": "",
+ "columns": {
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_issues_to_teams_visibility_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issues_to_teams_visibility_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issues_to_teams_visibility",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issues_to_teams_visibility_team_id_auth_roles_id_fk": {
+ "name": "knight_hacks_issues_to_teams_visibility_team_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_issues_to_teams_visibility",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["team_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_issues_to_teams_visibility_issue_id_team_id_pk": {
+ "name": "knight_hacks_issues_to_teams_visibility_issue_id_team_id_pk",
+ "columns": ["issue_id", "team_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issues_to_users_assignment": {
+ "name": "knight_hacks_issues_to_users_assignment",
+ "schema": "",
+ "columns": {
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_issues_to_users_assignment_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issues_to_users_assignment_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issues_to_users_assignment",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issues_to_users_assignment_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_issues_to_users_assignment_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issues_to_users_assignment",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_issues_to_users_assignment_issue_id_user_id_pk": {
+ "name": "knight_hacks_issues_to_users_assignment_issue_id_user_id_pk",
+ "columns": ["issue_id", "user_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judge": {
+ "name": "knight_hacks_judge",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "judge_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_judge_member_unique": {
+ "name": "knight_hacks_judge_member_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judge\".\"user_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judge_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_judge_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_judge",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judge_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judge_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judge",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judge_id_hackathon_unique": {
+ "name": "knight_hacks_judge_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judge_kind_identity_check": {
+ "name": "knight_hacks_judge_kind_identity_check",
+ "value": "\"knight_hacks_judge\".\"kind\" = 'member' OR (\"knight_hacks_judge\".\"kind\" = 'guest' AND \"knight_hacks_judge\".\"user_id\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judge_deliberation_entry": {
+ "name": "knight_hacks_judge_deliberation_entry",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_judge_deliberation_entry_section_scope_fk": {
+ "name": "knight_hacks_judge_deliberation_entry_section_scope_fk",
+ "tableFrom": "knight_hacks_judge_deliberation_entry",
+ "tableTo": "knight_hacks_judge_deliberation_section",
+ "columnsFrom": ["section_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judge_deliberation_entry_project_scope_fk": {
+ "name": "knight_hacks_judge_deliberation_entry_project_scope_fk",
+ "tableFrom": "knight_hacks_judge_deliberation_entry",
+ "tableTo": "knight_hacks_project",
+ "columnsFrom": ["project_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judge_deliberation_entry_section_project_unique": {
+ "name": "knight_hacks_judge_deliberation_entry_section_project_unique",
+ "nullsNotDistinct": false,
+ "columns": ["section_id", "project_id"]
+ },
+ "knight_hacks_judge_deliberation_entry_section_order_unique": {
+ "name": "knight_hacks_judge_deliberation_entry_section_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["section_id", "display_order"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judge_deliberation_entry_order_check": {
+ "name": "knight_hacks_judge_deliberation_entry_order_check",
+ "value": "\"knight_hacks_judge_deliberation_entry\".\"display_order\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judge_deliberation_section": {
+ "name": "knight_hacks_judge_deliberation_section",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_judge_deliberation_section_judge_scope_fk": {
+ "name": "knight_hacks_judge_deliberation_section_judge_scope_fk",
+ "tableFrom": "knight_hacks_judge_deliberation_section",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judge_deliberation_section_judge_order_unique": {
+ "name": "knight_hacks_judge_deliberation_section_judge_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["judge_id", "display_order"]
+ },
+ "knight_hacks_judge_deliberation_section_id_hackathon_unique": {
+ "name": "knight_hacks_judge_deliberation_section_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judge_deliberation_section_order_check": {
+ "name": "knight_hacks_judge_deliberation_section_order_check",
+ "value": "\"knight_hacks_judge_deliberation_section\".\"display_order\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_room": {
+ "name": "knight_hacks_judging_room",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "challenge_id": {
+ "name": "challenge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "discord_thread_id": {
+ "name": "discord_thread_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_by_user_id": {
+ "name": "archived_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_judging_room_active_name_unique": {
+ "name": "knight_hacks_judging_room_active_name_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_room\".\"archived_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_judging_room_hackathon_idx": {
+ "name": "knight_hacks_judging_room_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "display_order",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judging_room_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_judging_room_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_judging_room",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_archived_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_room_archived_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_room",
+ "tableTo": "auth_user",
+ "columnsFrom": ["archived_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_challenge_scope_fk": {
+ "name": "knight_hacks_judging_room_challenge_scope_fk",
+ "tableFrom": "knight_hacks_judging_room",
+ "tableTo": "knight_hacks_project_challenge",
+ "columnsFrom": ["challenge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judging_room_id_hackathon_unique": {
+ "name": "knight_hacks_judging_room_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_room_access_link": {
+ "name": "knight_hacks_judging_room_access_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "room_id": {
+ "name": "room_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_by_user_id": {
+ "name": "revoked_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revocation_reason": {
+ "name": "revocation_reason",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_judging_room_access_link_active_room_unique": {
+ "name": "knight_hacks_judging_room_access_link_active_room_unique",
+ "columns": [
+ {
+ "expression": "room_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_room_access_link\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judging_room_access_link_created_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_room_access_link_created_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_room_access_link",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_access_link_revoked_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_room_access_link_revoked_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_room_access_link",
+ "tableTo": "auth_user",
+ "columnsFrom": ["revoked_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_access_link_room_scope_fk": {
+ "name": "knight_hacks_judging_room_access_link_room_scope_fk",
+ "tableFrom": "knight_hacks_judging_room_access_link",
+ "tableTo": "knight_hacks_judging_room",
+ "columnsFrom": ["room_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judging_room_access_link_id_hackathon_unique": {
+ "name": "knight_hacks_judging_room_access_link_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_room_presence": {
+ "name": "knight_hacks_judging_room_presence",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "room_id": {
+ "name": "room_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "joined_at": {
+ "name": "joined_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_seen_at": {
+ "name": "last_seen_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "left_at": {
+ "name": "left_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "leave_reason": {
+ "name": "leave_reason",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_judging_room_presence_active_judge_unique": {
+ "name": "knight_hacks_judging_room_presence_active_judge_unique",
+ "columns": [
+ {
+ "expression": "judge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_room_presence\".\"left_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_judging_room_presence_room_idx": {
+ "name": "knight_hacks_judging_room_presence_room_idx",
+ "columns": [
+ {
+ "expression": "room_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "left_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judging_room_presence_room_scope_fk": {
+ "name": "knight_hacks_judging_room_presence_room_scope_fk",
+ "tableFrom": "knight_hacks_judging_room_presence",
+ "tableTo": "knight_hacks_judging_room",
+ "columnsFrom": ["room_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_presence_judge_scope_fk": {
+ "name": "knight_hacks_judging_room_presence_judge_scope_fk",
+ "tableFrom": "knight_hacks_judging_room_presence",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_rubric_item": {
+ "name": "knight_hacks_judging_rubric_item",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "judging_rubric_item_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "member_visibility_policy": {
+ "name": "member_visibility_policy",
+ "type": "judging_response_visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guest_visibility_policy": {
+ "name": "guest_visibility_policy",
+ "type": "judging_response_visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_judging_rubric_item_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_judging_rubric_item_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_judging_rubric_item",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judging_rubric_item_hackathon_order_unique": {
+ "name": "knight_hacks_judging_rubric_item_hackathon_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "display_order"]
+ },
+ "knight_hacks_judging_rubric_item_id_hackathon_unique": {
+ "name": "knight_hacks_judging_rubric_item_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judging_rubric_item_display_order_check": {
+ "name": "knight_hacks_judging_rubric_item_display_order_check",
+ "value": "\"knight_hacks_judging_rubric_item\".\"display_order\" >= 0"
+ },
+ "knight_hacks_judging_rubric_item_visibility_check": {
+ "name": "knight_hacks_judging_rubric_item_visibility_check",
+ "value": "(\"knight_hacks_judging_rubric_item\".\"kind\" = 'rating' AND \"knight_hacks_judging_rubric_item\".\"required\" = true AND \"knight_hacks_judging_rubric_item\".\"member_visibility_policy\" IS NULL AND \"knight_hacks_judging_rubric_item\".\"guest_visibility_policy\" IS NULL) OR (\"knight_hacks_judging_rubric_item\".\"kind\" = 'short_response' AND \"knight_hacks_judging_rubric_item\".\"member_visibility_policy\" IS NOT NULL AND \"knight_hacks_judging_rubric_item\".\"guest_visibility_policy\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_member": {
+ "name": "knight_hacks_member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "age": {
+ "name": "age",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Computer Science'"
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "guild_profile_visible": {
+ "name": "guild_profile_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "guild_resume_visible": {
+ "name": "guild_resume_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "guild_opportunity_statuses": {
+ "name": "guild_opportunity_statuses",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "tagline": {
+ "name": "tagline",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "about": {
+ "name": "about",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_picture_url": {
+ "name": "profile_picture_url",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "alumni_confirmed_at": {
+ "name": "alumni_confirmed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "company": {
+ "name": "company",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_city_key": {
+ "name": "current_city_key",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guild_location_visible": {
+ "name": "guild_location_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "points": {
+ "name": "points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "date_created": {
+ "name": "date_created",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "time_created": {
+ "name": "time_created",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_member_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_member_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_member",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_member_email_unique": {
+ "name": "knight_hacks_member_email_unique",
+ "nullsNotDistinct": false,
+ "columns": ["email"]
+ },
+ "knight_hacks_member_phoneNumber_unique": {
+ "name": "knight_hacks_member_phoneNumber_unique",
+ "nullsNotDistinct": false,
+ "columns": ["phone_number"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_member_valid_guild_opportunity_statuses": {
+ "name": "knight_hacks_member_valid_guild_opportunity_statuses",
+ "value": "\"knight_hacks_member\".\"guild_opportunity_statuses\" <@ ARRAY['internships', 'full-time', 'freelance-contract', 'project-collaboration', 'offering-mentorship', 'seeking-mentorship']::text[] AND cardinality(\"knight_hacks_member\".\"guild_opportunity_statuses\") <= 3"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_companies": {
+ "name": "knight_hacks_companies",
+ "schema": "",
+ "columns": {
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": true,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project": {
+ "name": "knight_hacks_project",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "submission_url": {
+ "name": "submission_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "demo_links": {
+ "name": "demo_links",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "video_url": {
+ "name": "video_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "technologies": {
+ "name": "technologies",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "universities": {
+ "name": "universities",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "prize_categories": {
+ "name": "prize_categories",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "participant_count": {
+ "name": "participant_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_created_at": {
+ "name": "project_created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "submitted_at": {
+ "name": "submitted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_by_user_id": {
+ "name": "deleted_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_hackathon_idx": {
+ "name": "knight_hacks_project_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_project_hackathon_deleted_idx": {
+ "name": "knight_hacks_project_hackathon_deleted_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deleted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_project_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_project",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_deleted_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_project_deleted_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_project",
+ "tableTo": "auth_user",
+ "columnsFrom": ["deleted_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_hackathon_submission_url_unique": {
+ "name": "knight_hacks_project_hackathon_submission_url_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "submission_url"]
+ },
+ "knight_hacks_project_id_hackathon_unique": {
+ "name": "knight_hacks_project_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_participant_count_check": {
+ "name": "knight_hacks_project_participant_count_check",
+ "value": "\"knight_hacks_project\".\"participant_count\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_challenge": {
+ "name": "knight_hacks_project_challenge",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_challenge_hackathon_idx": {
+ "name": "knight_hacks_project_challenge_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_challenge_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_project_challenge_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_project_challenge",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_challenge_hackathon_label_unique": {
+ "name": "knight_hacks_project_challenge_hackathon_label_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "label"]
+ },
+ "knight_hacks_project_challenge_id_hackathon_unique": {
+ "name": "knight_hacks_project_challenge_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation": {
+ "name": "knight_hacks_project_evaluation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "challenge_id": {
+ "name": "challenge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_evaluation_project_challenge_idx": {
+ "name": "knight_hacks_project_evaluation_project_challenge_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "challenge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_project_evaluation_judge_idx": {
+ "name": "knight_hacks_project_evaluation_judge_idx",
+ "columns": [
+ {
+ "expression": "judge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_project_challenge_scope_fk": {
+ "name": "knight_hacks_project_evaluation_project_challenge_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation",
+ "tableTo": "knight_hacks_project_to_challenge",
+ "columnsFrom": ["project_id", "challenge_id", "hackathon_id"],
+ "columnsTo": ["project_id", "challenge_id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_evaluation_judge_scope_fk": {
+ "name": "knight_hacks_project_evaluation_judge_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_evaluation_judge_project_challenge_unique": {
+ "name": "knight_hacks_project_evaluation_judge_project_challenge_unique",
+ "nullsNotDistinct": false,
+ "columns": ["judge_id", "project_id", "challenge_id"]
+ },
+ "knight_hacks_project_evaluation_id_hackathon_unique": {
+ "name": "knight_hacks_project_evaluation_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation_rating": {
+ "name": "knight_hacks_project_evaluation_rating",
+ "schema": "",
+ "columns": {
+ "evaluation_id": {
+ "name": "evaluation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rubric_item_id": {
+ "name": "rubric_item_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_rating_evaluation_scope_fk": {
+ "name": "knight_hacks_project_evaluation_rating_evaluation_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_rating",
+ "tableTo": "knight_hacks_project_evaluation",
+ "columnsFrom": ["evaluation_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_evaluation_rating_rubric_scope_fk": {
+ "name": "knight_hacks_project_evaluation_rating_rubric_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_rating",
+ "tableTo": "knight_hacks_judging_rubric_item",
+ "columnsFrom": ["rubric_item_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_project_evaluation_rating_evaluation_id_rubric_item_id_pk": {
+ "name": "knight_hacks_project_evaluation_rating_evaluation_id_rubric_item_id_pk",
+ "columns": ["evaluation_id", "rubric_item_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_evaluation_rating_value_check": {
+ "name": "knight_hacks_project_evaluation_rating_value_check",
+ "value": "\"knight_hacks_project_evaluation_rating\".\"value\" BETWEEN 1 AND 5"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation_response": {
+ "name": "knight_hacks_project_evaluation_response",
+ "schema": "",
+ "columns": {
+ "evaluation_id": {
+ "name": "evaluation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rubric_item_id": {
+ "name": "rubric_item_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_response_evaluation_scope_fk": {
+ "name": "knight_hacks_project_evaluation_response_evaluation_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_response",
+ "tableTo": "knight_hacks_project_evaluation",
+ "columnsFrom": ["evaluation_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_evaluation_response_rubric_scope_fk": {
+ "name": "knight_hacks_project_evaluation_response_rubric_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_response",
+ "tableTo": "knight_hacks_judging_rubric_item",
+ "columnsFrom": ["rubric_item_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_project_evaluation_response_evaluation_id_rubric_item_id_pk": {
+ "name": "knight_hacks_project_evaluation_response_evaluation_id_rubric_item_id_pk",
+ "columns": ["evaluation_id", "rubric_item_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation_revision": {
+ "name": "knight_hacks_project_evaluation_revision",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "evaluation_id": {
+ "name": "evaluation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_kind": {
+ "name": "actor_kind",
+ "type": "judge_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rating_answers": {
+ "name": "rating_answers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "response_answers": {
+ "name": "response_answers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_revision_evaluation_scope_fk": {
+ "name": "knight_hacks_project_evaluation_revision_evaluation_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_revision",
+ "tableTo": "knight_hacks_project_evaluation",
+ "columnsFrom": ["evaluation_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_evaluation_revision_unique": {
+ "name": "knight_hacks_project_evaluation_revision_unique",
+ "nullsNotDistinct": false,
+ "columns": ["evaluation_id", "revision"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_evaluation_revision_revision_check": {
+ "name": "knight_hacks_project_evaluation_revision_revision_check",
+ "value": "\"knight_hacks_project_evaluation_revision\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_member": {
+ "name": "knight_hacks_project_member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_member_project_idx": {
+ "name": "knight_hacks_project_member_project_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_member_project_id_knight_hacks_project_id_fk": {
+ "name": "knight_hacks_project_member_project_id_knight_hacks_project_id_fk",
+ "tableFrom": "knight_hacks_project_member",
+ "tableTo": "knight_hacks_project",
+ "columnsFrom": ["project_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_member_project_order_unique": {
+ "name": "knight_hacks_project_member_project_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "display_order"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_member_display_order_check": {
+ "name": "knight_hacks_project_member_display_order_check",
+ "value": "\"knight_hacks_project_member\".\"display_order\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_to_challenge": {
+ "name": "knight_hacks_project_to_challenge",
+ "schema": "",
+ "columns": {
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "challenge_id": {
+ "name": "challenge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_to_challenge_challenge_idx": {
+ "name": "knight_hacks_project_to_challenge_challenge_idx",
+ "columns": [
+ {
+ "expression": "challenge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_project_to_challenge_hackathon_idx": {
+ "name": "knight_hacks_project_to_challenge_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_to_challenge_project_scope_fk": {
+ "name": "knight_hacks_project_to_challenge_project_scope_fk",
+ "tableFrom": "knight_hacks_project_to_challenge",
+ "tableTo": "knight_hacks_project",
+ "columnsFrom": ["project_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_to_challenge_challenge_scope_fk": {
+ "name": "knight_hacks_project_to_challenge_challenge_scope_fk",
+ "tableFrom": "knight_hacks_project_to_challenge",
+ "tableTo": "knight_hacks_project_challenge",
+ "columnsFrom": ["challenge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_project_to_challenge_project_id_challenge_id_pk": {
+ "name": "knight_hacks_project_to_challenge_project_id_challenge_id_pk",
+ "columns": ["project_id", "challenge_id"]
+ }
+ },
+ "uniqueConstraints": {
+ "knight_hacks_project_to_challenge_project_challenge_hackathon_unique": {
+ "name": "knight_hacks_project_to_challenge_project_challenge_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "challenge_id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_sponsor": {
+ "name": "knight_hacks_sponsor",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo_url": {
+ "name": "logo_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_template": {
+ "name": "knight_hacks_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_name": {
+ "name": "normalized_name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "body": {
+ "name": "body",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "disabled_at": {
+ "name": "disabled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "disabled_reason": {
+ "name": "disabled_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_template_normalized_name_unique": {
+ "name": "knight_hacks_template_normalized_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["normalized_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_trpc_form_connection": {
+ "name": "knight_hacks_trpc_form_connection",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form": {
+ "name": "form",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proc": {
+ "name": "proc",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connections": {
+ "name": "connections",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_trpc_form_connection_form_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_trpc_form_connection_form_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_trpc_form_connection",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.alumni_bulletin_state": {
+ "name": "alumni_bulletin_state",
+ "schema": "public",
+ "values": ["draft", "published", "archived"]
+ },
+ "public.company_review_state": {
+ "name": "company_review_state",
+ "schema": "public",
+ "values": ["pending", "approved", "rejected", "merged"]
+ },
+ "public.employment_experience_type": {
+ "name": "employment_experience_type",
+ "schema": "public",
+ "values": [
+ "internship",
+ "full_time",
+ "part_time",
+ "co_op",
+ "contract",
+ "fellowship",
+ "self_employed",
+ "other"
+ ]
+ },
+ "public.employment_state": {
+ "name": "employment_state",
+ "schema": "public",
+ "values": ["current", "past", "unknown"]
+ },
+ "public.event_discord_entity_type": {
+ "name": "event_discord_entity_type",
+ "schema": "public",
+ "values": ["external", "voice", "stage"]
+ },
+ "public.event_google_destination": {
+ "name": "event_google_destination",
+ "schema": "public",
+ "values": ["public", "internal"]
+ },
+ "public.event_publication_work_state": {
+ "name": "event_publication_work_state",
+ "schema": "public",
+ "values": ["pending", "processing", "succeeded", "failed", "blocked"]
+ },
+ "public.event_purpose": {
+ "name": "event_purpose",
+ "schema": "public",
+ "values": ["event", "primary_check_in"]
+ },
+ "public.event_sync_state": {
+ "name": "event_sync_state",
+ "schema": "public",
+ "values": ["disabled", "pending", "synced", "error", "unknown"]
+ },
+ "public.form_callback_status": {
+ "name": "form_callback_status",
+ "schema": "public",
+ "values": ["pending", "running", "succeeded", "failed", "cancelled"]
+ },
+ "public.form_kind": {
+ "name": "form_kind",
+ "schema": "public",
+ "values": ["general", "event_feedback", "system"]
+ },
+ "public.form_response_mode": {
+ "name": "form_response_mode",
+ "schema": "public",
+ "values": ["single_locked", "single_editable", "multiple_locked"]
+ },
+ "public.form_state": {
+ "name": "form_state",
+ "schema": "public",
+ "values": ["draft", "published", "archived"]
+ },
+ "public.gender": {
+ "name": "gender",
+ "schema": "public",
+ "values": [
+ "Man",
+ "Woman",
+ "Non-binary",
+ "Prefer to self-describe",
+ "Prefer not to answer"
+ ]
+ },
+ "public.hackathon_application_state": {
+ "name": "hackathon_application_state",
+ "schema": "public",
+ "values": [
+ "withdrawn",
+ "pending",
+ "accepted",
+ "waitlisted",
+ "checkedin",
+ "confirmed",
+ "denied"
+ ]
+ },
+ "public.hackathon_event_publication_provider": {
+ "name": "hackathon_event_publication_provider",
+ "schema": "public",
+ "values": ["discord", "google"]
+ },
+ "public.hackathon_event_reminder_state": {
+ "name": "hackathon_event_reminder_state",
+ "schema": "public",
+ "values": ["pending", "delivering", "delivered", "failed", "unknown"]
+ },
+ "public.hacker_agreement_provenance": {
+ "name": "hacker_agreement_provenance",
+ "schema": "public",
+ "values": ["explicit", "legacy_unversioned"]
+ },
+ "public.hacker_agreement_stage": {
+ "name": "hacker_agreement_stage",
+ "schema": "public",
+ "values": ["application", "confirmation"]
+ },
+ "public.hacker_check_in_outcome": {
+ "name": "hacker_check_in_outcome",
+ "schema": "public",
+ "values": [
+ "checked_in",
+ "already_checked_in",
+ "invalid_qr",
+ "hacker_not_found",
+ "wrong_status",
+ "not_checked_in",
+ "wrong_class",
+ "not_ready"
+ ]
+ },
+ "public.hacker_discord_role_attempt_outcome": {
+ "name": "hacker_discord_role_attempt_outcome",
+ "schema": "public",
+ "values": ["pending", "succeeded", "failed", "unknown"]
+ },
+ "public.hacker_discord_role_grant_state": {
+ "name": "hacker_discord_role_grant_state",
+ "schema": "public",
+ "values": ["pending", "succeeded", "failed", "unknown"]
+ },
+ "public.hacker_discord_role_kind": {
+ "name": "hacker_discord_role_kind",
+ "schema": "public",
+ "values": ["general", "class", "vip"]
+ },
+ "public.hacker_participant_command_state": {
+ "name": "hacker_participant_command_state",
+ "schema": "public",
+ "values": ["started", "completed"]
+ },
+ "public.issue_priority": {
+ "name": "issue_priority",
+ "schema": "public",
+ "values": ["Lowest", "Low", "Medium", "High", "Highest"]
+ },
+ "public.issue_status": {
+ "name": "issue_status",
+ "schema": "public",
+ "values": ["Backlog", "Planning", "In Progress", "Finished"]
+ },
+ "public.judge_kind": {
+ "name": "judge_kind",
+ "schema": "public",
+ "values": ["member", "guest"]
+ },
+ "public.judging_response_visibility": {
+ "name": "judging_response_visibility",
+ "schema": "public",
+ "values": ["public", "public_optional", "private"]
+ },
+ "public.judging_rubric_item_kind": {
+ "name": "judging_rubric_item_kind",
+ "schema": "public",
+ "values": ["rating", "short_response"]
+ },
+ "public.judging_state": {
+ "name": "judging_state",
+ "schema": "public",
+ "values": ["draft", "open", "closed"]
+ },
+ "public.race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "schema": "public",
+ "values": [
+ "White",
+ "Black or African American",
+ "Hispanic / Latino / Spanish Origin",
+ "Asian",
+ "Native Hawaiian or Other Pacific Islander",
+ "Native American or Alaskan Native",
+ "Middle Eastern",
+ "Prefer not to answer",
+ "Other"
+ ]
+ },
+ "public.shirt_size": {
+ "name": "shirt_size",
+ "schema": "public",
+ "values": ["XS", "S", "M", "L", "XL", "2XL", "3XL"]
+ },
+ "public.sponsor_tier": {
+ "name": "sponsor_tier",
+ "schema": "public",
+ "values": ["gold", "silver", "bronze", "other"]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
diff --git a/packages/db/drizzle/meta/0049_snapshot.json b/packages/db/drizzle/meta/0049_snapshot.json
new file mode 100644
index 000000000..90b129fdd
--- /dev/null
+++ b/packages/db/drizzle/meta/0049_snapshot.json
@@ -0,0 +1,12909 @@
+{
+ "id": "b565b1ca-b72f-47ea-8bb1-0fb67f73d7f6",
+ "prevId": "a303798c-6c7d-44c9-a6f8-4efdfd97527c",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.audit_event": {
+ "name": "audit_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "action_key": {
+ "name": "action_key",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "operation_id": {
+ "name": "operation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_user_id": {
+ "name": "actor_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_member_id": {
+ "name": "actor_member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_discord_user_id": {
+ "name": "actor_discord_user_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_label": {
+ "name": "actor_label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_role_label": {
+ "name": "actor_role_label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_role_color": {
+ "name": "actor_role_color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "changes": {
+ "name": "changes",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ }
+ },
+ "indexes": {
+ "audit_event_action_occurred_idx": {
+ "name": "audit_event_action_occurred_idx",
+ "columns": [
+ {
+ "expression": "action_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_actor_occurred_idx": {
+ "name": "audit_event_actor_occurred_idx",
+ "columns": [
+ {
+ "expression": "actor_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_actor_member_occurred_idx": {
+ "name": "audit_event_actor_member_occurred_idx",
+ "columns": [
+ {
+ "expression": "actor_member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_domain_occurred_idx": {
+ "name": "audit_event_domain_occurred_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_occurred_idx": {
+ "name": "audit_event_occurred_idx",
+ "columns": [
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_operation_idx": {
+ "name": "audit_event_operation_idx",
+ "columns": [
+ {
+ "expression": "operation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_event_outcome_occurred_idx": {
+ "name": "audit_event_outcome_occurred_idx",
+ "columns": [
+ {
+ "expression": "outcome",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "audit_event_actor_role_color_check": {
+ "name": "audit_event_actor_role_color_check",
+ "value": "\"audit_event\".\"actor_role_color\" IS NULL OR \"audit_event\".\"actor_role_color\" ~ '^#[0-9A-Fa-f]{6}$'"
+ },
+ "audit_event_outcome_check": {
+ "name": "audit_event_outcome_check",
+ "value": "\"audit_event\".\"outcome\" IN ('committed', 'partial_external')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.audit_subject": {
+ "name": "audit_subject",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "relation": {
+ "name": "relation",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_label": {
+ "name": "target_label",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_outcome": {
+ "name": "result_outcome",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "position": {
+ "name": "position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ }
+ },
+ "indexes": {
+ "audit_subject_event_position_idx": {
+ "name": "audit_subject_event_position_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "position",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_subject_member_event_idx": {
+ "name": "audit_subject_member_event_idx",
+ "columns": [
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_subject_one_primary_idx": {
+ "name": "audit_subject_one_primary_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"audit_subject\".\"relation\" = 'primary'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_subject_target_event_idx": {
+ "name": "audit_subject_target_event_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "audit_subject_event_id_audit_event_id_fk": {
+ "name": "audit_subject_event_id_audit_event_id_fk",
+ "tableFrom": "audit_subject",
+ "tableTo": "audit_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "audit_subject_relation_check": {
+ "name": "audit_subject_relation_check",
+ "value": "\"audit_subject\".\"relation\" IN ('primary', 'secondary', 'result')"
+ },
+ "audit_subject_result_outcome_check": {
+ "name": "audit_subject_result_outcome_check",
+ "value": "\"audit_subject\".\"result_outcome\" IS NULL OR \"audit_subject\".\"result_outcome\" IN ('succeeded', 'skipped', 'failed_external', 'failed_internal', 'compensated')"
+ },
+ "audit_subject_result_outcome_shape_check": {
+ "name": "audit_subject_result_outcome_shape_check",
+ "value": "(\"audit_subject\".\"relation\" = 'result') = (\"audit_subject\".\"result_outcome\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.auth_account": {
+ "name": "auth_account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_account_id": {
+ "name": "provider_account_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "auth_account_user_id_auth_user_id_fk": {
+ "name": "auth_account_user_id_auth_user_id_fk",
+ "tableFrom": "auth_account",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "auth_account_provider_provider_account_id_pk": {
+ "name": "auth_account_provider_provider_account_id_pk",
+ "columns": ["provider", "provider_account_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_permissions": {
+ "name": "auth_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "auth_permissions_role_id_auth_roles_id_fk": {
+ "name": "auth_permissions_role_id_auth_roles_id_fk",
+ "tableFrom": "auth_permissions",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "auth_permissions_user_id_auth_user_id_fk": {
+ "name": "auth_permissions_user_id_auth_user_id_fk",
+ "tableFrom": "auth_permissions",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_roles": {
+ "name": "auth_roles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "discord_role_id": {
+ "name": "discord_role_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_feedback_excluded": {
+ "name": "event_feedback_excluded",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "email_audience_enabled": {
+ "name": "email_audience_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "issue_reminder_channel": {
+ "name": "issue_reminder_channel",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'1459204271655489567'"
+ },
+ "issue_reminders_enabled": {
+ "name": "issue_reminders_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "team_hexcode_color": {
+ "name": "team_hexcode_color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "auth_roles_discordRoleId_unique": {
+ "name": "auth_roles_discordRoleId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["discord_role_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_session": {
+ "name": "auth_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "session_token": {
+ "name": "session_token",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires": {
+ "name": "expires",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "varchar(1024)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "auth_session_user_id_auth_user_id_fk": {
+ "name": "auth_session_user_id_auth_user_id_fk",
+ "tableFrom": "auth_session",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_user": {
+ "name": "auth_user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "discord_user_id": {
+ "name": "discord_user_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auth_verification": {
+ "name": "auth_verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_club_team": {
+ "name": "knight_hacks_club_team",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "heading": {
+ "name": "heading",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_club_team_display_order_unique": {
+ "name": "knight_hacks_club_team_display_order_unique",
+ "columns": [
+ {
+ "expression": "display_order",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_club_team_kind_unique": {
+ "name": "knight_hacks_club_team_kind_unique",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_club_team\".\"kind\" <> 'team'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_club_team_slug_unique": {
+ "name": "knight_hacks_club_team_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": ["slug"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_club_team_kind_check": {
+ "name": "knight_hacks_club_team_kind_check",
+ "value": "\"knight_hacks_club_team\".\"kind\" IN ('executive', 'director', 'team')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_club_team_role": {
+ "name": "knight_hacks_club_team_role",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rank": {
+ "name": "rank",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "roster_label": {
+ "name": "roster_label",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callout_label": {
+ "name": "callout_label",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_club_team_role_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_club_team_role_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_club_team_role",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_club_team_role_team_id_knight_hacks_club_team_id_fk": {
+ "name": "knight_hacks_club_team_role_team_id_knight_hacks_club_team_id_fk",
+ "tableFrom": "knight_hacks_club_team_role",
+ "tableTo": "knight_hacks_club_team",
+ "columnsFrom": ["team_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_club_team_role_roleId_unique": {
+ "name": "knight_hacks_club_team_role_roleId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["role_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_club_team_role_kind_check": {
+ "name": "knight_hacks_club_team_role_kind_check",
+ "value": "\"knight_hacks_club_team_role\".\"kind\" IN ('executive', 'director', 'team')"
+ },
+ "knight_hacks_club_team_role_team_check": {
+ "name": "knight_hacks_club_team_role_team_check",
+ "value": "\"knight_hacks_club_team_role\".\"kind\" <> 'team' OR \"knight_hacks_club_team_role\".\"team_id\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_discord_config": {
+ "name": "knight_hacks_discord_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "production_id": {
+ "name": "production_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "development_id": {
+ "name": "development_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_discord_config_key_unique": {
+ "name": "knight_hacks_discord_config_key_unique",
+ "nullsNotDistinct": false,
+ "columns": ["key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_discord_config_kind_check": {
+ "name": "knight_hacks_discord_config_kind_check",
+ "value": "\"knight_hacks_discord_config\".\"kind\" IN ('channel', 'guild', 'role')"
+ },
+ "knight_hacks_discord_config_production_id_check": {
+ "name": "knight_hacks_discord_config_production_id_check",
+ "value": "\"knight_hacks_discord_config\".\"production_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_discord_config_development_id_check": {
+ "name": "knight_hacks_discord_config_development_id_check",
+ "value": "\"knight_hacks_discord_config\".\"development_id\" IS NULL OR \"knight_hacks_discord_config\".\"development_id\" ~ '^[0-9]{17,20}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_channel": {
+ "name": "discord_archive_channel",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topic": {
+ "name": "topic",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_thread": {
+ "name": "is_thread",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_private_thread": {
+ "name": "is_private_thread",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "archived": {
+ "name": "archived",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "locked": {
+ "name": "locked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "discovered_at": {
+ "name": "discovered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "discord_updated_at": {
+ "name": "discord_updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "discord_archive_channel_guild_type_idx": {
+ "name": "discord_archive_channel_guild_type_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_channel_parent_idx": {
+ "name": "discord_archive_channel_parent_idx",
+ "columns": [
+ {
+ "expression": "parent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_checkpoint": {
+ "name": "discord_archive_checkpoint",
+ "schema": "",
+ "columns": {
+ "channel_id": {
+ "name": "channel_id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oldest_message_id": {
+ "name": "oldest_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "newest_message_id": {
+ "name": "newest_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backfill_before_message_id": {
+ "name": "backfill_before_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backfill_status": {
+ "name": "backfill_status",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "backfill_completed_at": {
+ "name": "backfill_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_backfill_at": {
+ "name": "last_backfill_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_discovered_at": {
+ "name": "last_discovered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciled_at": {
+ "name": "last_reconciled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "processed_message_count": {
+ "name": "processed_message_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry_count": {
+ "name": "retry_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error_code": {
+ "name": "last_error_code",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_message": {
+ "name": "last_error_message",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "discord_archive_checkpoint_guild_status_updated_idx": {
+ "name": "discord_archive_checkpoint_guild_status_updated_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "backfill_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "discord_archive_checkpoint_channel_id_discord_archive_channel_id_fk": {
+ "name": "discord_archive_checkpoint_channel_id_discord_archive_channel_id_fk",
+ "tableFrom": "discord_archive_checkpoint",
+ "tableTo": "discord_archive_channel",
+ "columnsFrom": ["channel_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "discord_archive_checkpoint_status_check": {
+ "name": "discord_archive_checkpoint_status_check",
+ "value": "\"discord_archive_checkpoint\".\"backfill_status\" IN ('pending', 'running', 'complete', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_message": {
+ "name": "discord_archive_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_discord_user_id": {
+ "name": "author_discord_user_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_label": {
+ "name": "author_label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_avatar_url": {
+ "name": "author_avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "author_is_bot": {
+ "name": "author_is_bot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "webhook_id": {
+ "name": "webhook_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message_type": {
+ "name": "message_type",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "edited_at": {
+ "name": "edited_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reply_to_message_id": {
+ "name": "reply_to_message_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pinned": {
+ "name": "pinned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "flags": {
+ "name": "flags",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "mentions": {
+ "name": "mentions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"everyone\":false,\"roleIds\":[],\"userIds\":[]}'::jsonb"
+ },
+ "embeds": {
+ "name": "embeds",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "attachments": {
+ "name": "attachments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "components": {
+ "name": "components",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "stickers": {
+ "name": "stickers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "poll": {
+ "name": "poll",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ingested_at": {
+ "name": "ingested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_observed_at": {
+ "name": "last_observed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "discord_archive_message_author_created_idx": {
+ "name": "discord_archive_message_author_created_idx",
+ "columns": [
+ {
+ "expression": "author_discord_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_message_channel_created_idx": {
+ "name": "discord_archive_message_channel_created_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_message_guild_created_idx": {
+ "name": "discord_archive_message_guild_created_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "discord_archive_message_non_deleted_created_idx": {
+ "name": "discord_archive_message_non_deleted_created_idx",
+ "columns": [
+ {
+ "expression": "guild_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"discord_archive_message\".\"deleted_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "discord_archive_message_channel_id_discord_archive_channel_id_fk": {
+ "name": "discord_archive_message_channel_id_discord_archive_channel_id_fk",
+ "tableFrom": "discord_archive_message",
+ "tableTo": "discord_archive_channel",
+ "columnsFrom": ["channel_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.discord_archive_state": {
+ "name": "discord_archive_state",
+ "schema": "",
+ "columns": {
+ "guild_id": {
+ "name": "guild_id",
+ "type": "varchar(20)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'idle'"
+ },
+ "last_gateway_event_at": {
+ "name": "last_gateway_event_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_live_write_at": {
+ "name": "last_live_write_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_discovery_started_at": {
+ "name": "last_discovery_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_discovery_completed_at": {
+ "name": "last_discovery_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciliation_started_at": {
+ "name": "last_reconciliation_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciliation_completed_at": {
+ "name": "last_reconciliation_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_backfill_progress_at": {
+ "name": "last_backfill_progress_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_owner": {
+ "name": "lease_owner",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_code": {
+ "name": "last_error_code",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_message": {
+ "name": "last_error_message",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_count": {
+ "name": "failure_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "discord_archive_state_status_check": {
+ "name": "discord_archive_state_status_check",
+ "value": "\"discord_archive_state\".\"status\" IN ('disabled', 'idle', 'healthy', 'degraded', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_alumni_bulletin_post": {
+ "name": "knight_hacks_alumni_bulletin_post",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image_object_name": {
+ "name": "image_object_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image_alt": {
+ "name": "image_alt",
+ "type": "varchar(240)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cta_label": {
+ "name": "cta_label",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_url": {
+ "name": "external_url",
+ "type": "varchar(2048)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "alumni_bulletin_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "publish_at": {
+ "name": "publish_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by_user_id": {
+ "name": "updated_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_alumni_bulletin_state_order_idx": {
+ "name": "knight_hacks_alumni_bulletin_state_order_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "display_order",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_alumni_bulletin_publication_window_idx": {
+ "name": "knight_hacks_alumni_bulletin_publication_window_idx",
+ "columns": [
+ {
+ "expression": "publish_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_alumni_bulletin_post_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_alumni_bulletin_post_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_alumni_bulletin_post",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_alumni_bulletin_post_created_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_alumni_bulletin_post_created_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_alumni_bulletin_post",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_alumni_bulletin_post_updated_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_alumni_bulletin_post_updated_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_alumni_bulletin_post",
+ "tableTo": "auth_user",
+ "columnsFrom": ["updated_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "alumni_bulletin_action_exclusive": {
+ "name": "alumni_bulletin_action_exclusive",
+ "value": "NOT (\"knight_hacks_alumni_bulletin_post\".\"external_url\" IS NOT NULL AND \"knight_hacks_alumni_bulletin_post\".\"form_id\" IS NOT NULL)"
+ },
+ "alumni_bulletin_action_pair": {
+ "name": "alumni_bulletin_action_pair",
+ "value": "((\"knight_hacks_alumni_bulletin_post\".\"external_url\" IS NULL AND \"knight_hacks_alumni_bulletin_post\".\"form_id\" IS NULL) OR \"knight_hacks_alumni_bulletin_post\".\"cta_label\" IS NOT NULL)"
+ },
+ "alumni_bulletin_display_order_nonnegative": {
+ "name": "alumni_bulletin_display_order_nonnegative",
+ "value": "\"knight_hacks_alumni_bulletin_post\".\"display_order\" >= 0"
+ },
+ "alumni_bulletin_image_alt_pair": {
+ "name": "alumni_bulletin_image_alt_pair",
+ "value": "((\"knight_hacks_alumni_bulletin_post\".\"image_object_name\" IS NULL AND \"knight_hacks_alumni_bulletin_post\".\"image_alt\" IS NULL) OR (\"knight_hacks_alumni_bulletin_post\".\"image_object_name\" IS NOT NULL AND \"knight_hacks_alumni_bulletin_post\".\"image_alt\" IS NOT NULL))"
+ },
+ "alumni_bulletin_schedule_order": {
+ "name": "alumni_bulletin_schedule_order",
+ "value": "\"knight_hacks_alumni_bulletin_post\".\"expires_at\" IS NULL OR \"knight_hacks_alumni_bulletin_post\".\"publish_at\" IS NULL OR \"knight_hacks_alumni_bulletin_post\".\"expires_at\" > \"knight_hacks_alumni_bulletin_post\".\"publish_at\""
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_company": {
+ "name": "knight_hacks_company",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_display_name": {
+ "name": "normalized_display_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "legal_name": {
+ "name": "legal_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "varchar(253)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logo_object_name": {
+ "name": "logo_object_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aliases": {
+ "name": "aliases",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "review_state": {
+ "name": "review_state",
+ "type": "company_review_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "merged_into_company_id": {
+ "name": "merged_into_company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_company_review_state_idx": {
+ "name": "knight_hacks_company_review_state_idx",
+ "columns": [
+ {
+ "expression": "review_state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_company_domain_idx": {
+ "name": "knight_hacks_company_domain_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_company_created_by_user_idx": {
+ "name": "knight_hacks_company_created_by_user_idx",
+ "columns": [
+ {
+ "expression": "created_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_company_created_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_company_created_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_company",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_company_merged_into_fk": {
+ "name": "knight_hacks_company_merged_into_fk",
+ "tableFrom": "knight_hacks_company",
+ "tableTo": "knight_hacks_company",
+ "columnsFrom": ["merged_into_company_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_company_normalized_display_name_unique": {
+ "name": "knight_hacks_company_normalized_display_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["normalized_display_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_company_merged_state_consistency": {
+ "name": "knight_hacks_company_merged_state_consistency",
+ "value": "(\"knight_hacks_company\".\"review_state\" = 'merged') = (\"knight_hacks_company\".\"merged_into_company_id\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_dues_configuration": {
+ "name": "knight_hacks_dues_configuration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(32)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "payments_enabled": {
+ "name": "payments_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_dues_configuration_singleton_check": {
+ "name": "knight_hacks_dues_configuration_singleton_check",
+ "value": "\"knight_hacks_dues_configuration\".\"id\" = 'global'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_dues_payment": {
+ "name": "knight_hacks_dues_payment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount": {
+ "name": "amount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payment_date": {
+ "name": "payment_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "year": {
+ "name": "year",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "stripe_payment_intent_id": {
+ "name": "stripe_payment_intent_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_dues_payment_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_dues_payment_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_dues_payment",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_dues_payment_memberId_year_unique": {
+ "name": "knight_hacks_dues_payment_memberId_year_unique",
+ "nullsNotDistinct": false,
+ "columns": ["member_id", "year"]
+ },
+ "knight_hacks_dues_payment_stripe_payment_intent_id_unique": {
+ "name": "knight_hacks_dues_payment_stripe_payment_intent_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["stripe_payment_intent_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_send": {
+ "name": "email_send",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "subject": {
+ "name": "subject",
+ "type": "varchar(200)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "template_revision_id": {
+ "name": "template_revision_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "plain_text_source": {
+ "name": "plain_text_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_html": {
+ "name": "compiled_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_text": {
+ "name": "compiled_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_hash": {
+ "name": "content_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience_definition": {
+ "name": "audience_definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience_version": {
+ "name": "audience_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "audience_hash": {
+ "name": "audience_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "preview_version": {
+ "name": "preview_version",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "preview_expires_at": {
+ "name": "preview_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "raw_match_count": {
+ "name": "raw_match_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "duplicate_count": {
+ "name": "duplicate_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_invalid_count": {
+ "name": "excluded_invalid_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_suppressed_count": {
+ "name": "excluded_suppressed_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_missing_field_count": {
+ "name": "excluded_missing_field_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "excluded_manual_count": {
+ "name": "excluded_manual_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "final_recipient_count": {
+ "name": "final_recipient_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "scheduled_for": {
+ "name": "scheduled_for",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listmonk_list_id": {
+ "name": "listmonk_list_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listmonk_campaign_id": {
+ "name": "listmonk_campaign_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_tag": {
+ "name": "provider_tag",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_may_have_started": {
+ "name": "provider_may_have_started",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "provider_sent_count": {
+ "name": "provider_sent_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "provider_bounce_count": {
+ "name": "provider_bounce_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry_attempt_count": {
+ "name": "retry_attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "retry_lease_expires_at": {
+ "name": "retry_lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "safe_error": {
+ "name": "safe_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confirmed_at": {
+ "name": "confirmed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled_at": {
+ "name": "cancelled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled_by": {
+ "name": "cancelled_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "terminal_at": {
+ "name": "terminal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_send_status_scheduled_for_idx": {
+ "name": "email_send_status_scheduled_for_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scheduled_for",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_send_retry_idx": {
+ "name": "email_send_retry_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_retry_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "retry_lease_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_send_template_revision_id_email_template_revision_id_fk": {
+ "name": "email_send_template_revision_id_email_template_revision_id_fk",
+ "tableFrom": "email_send",
+ "tableTo": "email_template_revision",
+ "columnsFrom": ["template_revision_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_send_created_by_auth_user_id_fk": {
+ "name": "email_send_created_by_auth_user_id_fk",
+ "tableFrom": "email_send",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_send_cancelled_by_auth_user_id_fk": {
+ "name": "email_send_cancelled_by_auth_user_id_fk",
+ "tableFrom": "email_send",
+ "tableTo": "auth_user",
+ "columnsFrom": ["cancelled_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_send_provider_tag_unique": {
+ "name": "email_send_provider_tag_unique",
+ "nullsNotDistinct": false,
+ "columns": ["provider_tag"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_send_event": {
+ "name": "email_send_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "send_id": {
+ "name": "send_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "from_status": {
+ "name": "from_status",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "to_status": {
+ "name": "to_status",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_send_event_send_created_idx": {
+ "name": "email_send_event_send_created_idx",
+ "columns": [
+ {
+ "expression": "send_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_send_event_send_id_email_send_id_fk": {
+ "name": "email_send_event_send_id_email_send_id_fk",
+ "tableFrom": "email_send_event",
+ "tableTo": "email_send",
+ "columnsFrom": ["send_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_send_event_actor_id_auth_user_id_fk": {
+ "name": "email_send_event_actor_id_auth_user_id_fk",
+ "tableFrom": "email_send_event",
+ "tableTo": "auth_user",
+ "columnsFrom": ["actor_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_send_recipient": {
+ "name": "email_send_recipient",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "send_id": {
+ "name": "send_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_email": {
+ "name": "normalized_email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attributes": {
+ "name": "attributes",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "match_reasons": {
+ "name": "match_reasons",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "exclusion_reason": {
+ "name": "exclusion_reason",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listmonk_subscriber_id": {
+ "name": "listmonk_subscriber_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_send_recipient_send_id_idx": {
+ "name": "email_send_recipient_send_id_idx",
+ "columns": [
+ {
+ "expression": "send_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "exclusion_reason",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_send_recipient_send_id_email_send_id_fk": {
+ "name": "email_send_recipient_send_id_email_send_id_fk",
+ "tableFrom": "email_send_recipient",
+ "tableTo": "email_send",
+ "columnsFrom": ["send_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_send_recipient_send_id_normalized_email_unique": {
+ "name": "email_send_recipient_send_id_normalized_email_unique",
+ "nullsNotDistinct": false,
+ "columns": ["send_id", "normalized_email"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template": {
+ "name": "email_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_name": {
+ "name": "normalized_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'club'"
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_archived_updated_idx": {
+ "name": "email_template_archived_updated_idx",
+ "columns": [
+ {
+ "expression": "archived_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_created_by_auth_user_id_fk": {
+ "name": "email_template_created_by_auth_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_template_updated_by_auth_user_id_fk": {
+ "name": "email_template_updated_by_auth_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "auth_user",
+ "columnsFrom": ["updated_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_template_normalized_name_unique": {
+ "name": "email_template_normalized_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["normalized_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template_revision": {
+ "name": "email_template_revision",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visual_document": {
+ "name": "visual_document",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_html": {
+ "name": "compiled_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "compiled_text": {
+ "name": "compiled_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "personalization_contract": {
+ "name": "personalization_contract",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "checksum": {
+ "name": "checksum",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_revision_template_state_idx": {
+ "name": "email_template_revision_template_state_idx",
+ "columns": [
+ {
+ "expression": "template_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "version",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_revision_template_id_email_template_id_fk": {
+ "name": "email_template_revision_template_id_email_template_id_fk",
+ "tableFrom": "email_template_revision",
+ "tableTo": "email_template",
+ "columnsFrom": ["template_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "email_template_revision_created_by_auth_user_id_fk": {
+ "name": "email_template_revision_created_by_auth_user_id_fk",
+ "tableFrom": "email_template_revision",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "email_template_revision_template_version_unique": {
+ "name": "email_template_revision_template_version_unique",
+ "nullsNotDistinct": false,
+ "columns": ["template_id", "version"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_employment": {
+ "name": "knight_hacks_employment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "company_id": {
+ "name": "company_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "experience_type": {
+ "name": "experience_type",
+ "type": "employment_experience_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "employment_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "start_month": {
+ "name": "start_month",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "end_month": {
+ "name": "end_month",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "city_key": {
+ "name": "city_key",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guild_visible": {
+ "name": "guild_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_employment_member_idx": {
+ "name": "knight_hacks_employment_member_idx",
+ "columns": [
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_employment_company_idx": {
+ "name": "knight_hacks_employment_company_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_employment_company_state_idx": {
+ "name": "knight_hacks_employment_company_state_idx",
+ "columns": [
+ {
+ "expression": "company_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_employment_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_employment_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_employment",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_employment_company_id_knight_hacks_company_id_fk": {
+ "name": "knight_hacks_employment_company_id_knight_hacks_company_id_fk",
+ "tableFrom": "knight_hacks_employment",
+ "tableTo": "knight_hacks_company",
+ "columnsFrom": ["company_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_employment_current_has_no_end": {
+ "name": "knight_hacks_employment_current_has_no_end",
+ "value": "\"knight_hacks_employment\".\"state\" <> 'current' OR \"knight_hacks_employment\".\"end_month\" IS NULL"
+ },
+ "knight_hacks_employment_start_month_shape": {
+ "name": "knight_hacks_employment_start_month_shape",
+ "value": "\"knight_hacks_employment\".\"start_month\" IS NULL OR \"knight_hacks_employment\".\"start_month\" ~ '^[0-9]{4}-(0[1-9]|1[0-2])$'"
+ },
+ "knight_hacks_employment_end_month_shape": {
+ "name": "knight_hacks_employment_end_month_shape",
+ "value": "\"knight_hacks_employment\".\"end_month\" IS NULL OR \"knight_hacks_employment\".\"end_month\" ~ '^[0-9]{4}-(0[1-9]|1[0-2])$'"
+ },
+ "knight_hacks_employment_date_order": {
+ "name": "knight_hacks_employment_date_order",
+ "value": "\"knight_hacks_employment\".\"start_month\" IS NULL OR \"knight_hacks_employment\".\"end_month\" IS NULL OR \"knight_hacks_employment\".\"end_month\" >= \"knight_hacks_employment\".\"start_month\""
+ },
+ "knight_hacks_employment_city_key_shape": {
+ "name": "knight_hacks_employment_city_key_shape",
+ "value": "\"knight_hacks_employment\".\"city_key\" IS NULL OR \"knight_hacks_employment\".\"city_key\" ~ '^[0-9]{2}-[0-9]{5}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event": {
+ "name": "knight_hacks_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "discord_id": {
+ "name": "discord_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_id": {
+ "name": "google_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tag": {
+ "name": "tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tag_color": {
+ "name": "tag_color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#0D9488'"
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "start_datetime": {
+ "name": "start_datetime",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_datetime": {
+ "name": "end_datetime",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "location": {
+ "name": "location",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dues_paying": {
+ "name": "dues_paying",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_operations_calendar": {
+ "name": "is_operations_calendar",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "roles": {
+ "name": "roles",
+ "type": "varchar(255)[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "points": {
+ "name": "points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "purpose": {
+ "name": "purpose",
+ "type": "event_purpose",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'event'"
+ },
+ "discord_channel_id": {
+ "name": "discord_channel_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "legacy": {
+ "name": "legacy",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "discord_sync_state": {
+ "name": "discord_sync_state",
+ "type": "event_sync_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'pending'"
+ },
+ "google_sync_state": {
+ "name": "google_sync_state",
+ "type": "event_sync_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'pending'"
+ },
+ "discord_last_error": {
+ "name": "discord_last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_last_error": {
+ "name": "google_last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deletion_intent_at": {
+ "name": "deletion_intent_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_key": {
+ "name": "creation_key",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_payload_hash": {
+ "name": "creation_payload_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_revision": {
+ "name": "sync_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "sync_lease_token": {
+ "name": "sync_lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_lease_expires_at": {
+ "name": "sync_lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_lease_revision": {
+ "name": "sync_lease_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_outbound_attempt_token": {
+ "name": "discord_outbound_attempt_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_outbound_attempted_at": {
+ "name": "discord_outbound_attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_outbound_attempt_revision": {
+ "name": "discord_outbound_attempt_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_outbound_attempt_token": {
+ "name": "google_outbound_attempt_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_outbound_attempted_at": {
+ "name": "google_outbound_attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_outbound_attempt_revision": {
+ "name": "google_outbound_attempt_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_applied_revision": {
+ "name": "discord_applied_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_applied_entity_type": {
+ "name": "discord_applied_entity_type",
+ "type": "event_discord_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_applied_channel_id": {
+ "name": "discord_applied_channel_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_applied_revision": {
+ "name": "google_applied_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_applied_destination": {
+ "name": "google_applied_destination",
+ "type": "event_google_destination",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_applied_calendar_id": {
+ "name": "google_applied_calendar_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_revision": {
+ "name": "visibility_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_dues_paying": {
+ "name": "visibility_dues_paying",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_roles": {
+ "name": "visibility_roles",
+ "type": "varchar(255)[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "visibility_internal": {
+ "name": "visibility_internal",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_no_projection_acknowledged_at": {
+ "name": "discord_no_projection_acknowledged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_no_projection_acknowledged_by": {
+ "name": "discord_no_projection_acknowledged_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_start_datetime_idx": {
+ "name": "knight_hacks_event_start_datetime_idx",
+ "columns": [
+ {
+ "expression": "start_datetime",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_hackathon_id_idx": {
+ "name": "knight_hacks_event_hackathon_id_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_one_primary_per_hackathon": {
+ "name": "knight_hacks_event_one_primary_per_hackathon",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_event\".\"purpose\" = 'primary_check_in'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_event_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_event",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_discord_no_projection_acknowledged_by_auth_user_id_fk": {
+ "name": "knight_hacks_event_discord_no_projection_acknowledged_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_event",
+ "tableTo": "auth_user",
+ "columnsFrom": ["discord_no_projection_acknowledged_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_creationKey_unique": {
+ "name": "knight_hacks_event_creationKey_unique",
+ "nullsNotDistinct": false,
+ "columns": ["creation_key"]
+ },
+ "knight_hacks_event_id_hackathon_unique": {
+ "name": "knight_hacks_event_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_primary_requires_hackathon_check": {
+ "name": "knight_hacks_event_primary_requires_hackathon_check",
+ "value": "\"knight_hacks_event\".\"purpose\" <> 'primary_check_in' OR \"knight_hacks_event\".\"hackathon_id\" IS NOT NULL"
+ },
+ "knight_hacks_event_tag_color_check": {
+ "name": "knight_hacks_event_tag_color_check",
+ "value": "\"knight_hacks_event\".\"tag_color\" ~ '^#[0-9A-Fa-f]{6}$'"
+ },
+ "knight_hacks_event_sync_revision_check": {
+ "name": "knight_hacks_event_sync_revision_check",
+ "value": "\"knight_hacks_event\".\"sync_revision\" >= 0"
+ },
+ "knight_hacks_event_new_points_check": {
+ "name": "knight_hacks_event_new_points_check",
+ "value": "\"knight_hacks_event\".\"legacy\" OR (\"knight_hacks_event\".\"points\" IS NOT NULL AND \"knight_hacks_event\".\"points\" >= 0)"
+ },
+ "knight_hacks_event_nonlegacy_sync_states_check": {
+ "name": "knight_hacks_event_nonlegacy_sync_states_check",
+ "value": "\"knight_hacks_event\".\"legacy\" OR (\"knight_hacks_event\".\"discord_sync_state\" IS NOT NULL AND \"knight_hacks_event\".\"google_sync_state\" IS NOT NULL)"
+ },
+ "knight_hacks_event_creation_identity_pair_check": {
+ "name": "knight_hacks_event_creation_identity_pair_check",
+ "value": "(\"knight_hacks_event\".\"creation_key\" IS NULL) = (\"knight_hacks_event\".\"creation_payload_hash\" IS NULL)"
+ },
+ "knight_hacks_event_new_creation_identity_check": {
+ "name": "knight_hacks_event_new_creation_identity_check",
+ "value": "\"knight_hacks_event\".\"legacy\" OR (\"knight_hacks_event\".\"creation_key\" IS NOT NULL AND \"knight_hacks_event\".\"creation_payload_hash\" IS NOT NULL)"
+ },
+ "knight_hacks_event_sync_lease_set_check": {
+ "name": "knight_hacks_event_sync_lease_set_check",
+ "value": "(\"knight_hacks_event\".\"sync_lease_token\" IS NULL AND \"knight_hacks_event\".\"sync_lease_expires_at\" IS NULL AND \"knight_hacks_event\".\"sync_lease_revision\" IS NULL) OR (\"knight_hacks_event\".\"sync_lease_token\" IS NOT NULL AND \"knight_hacks_event\".\"sync_lease_expires_at\" IS NOT NULL AND \"knight_hacks_event\".\"sync_lease_revision\" IS NOT NULL)"
+ },
+ "knight_hacks_event_discord_attempt_set_check": {
+ "name": "knight_hacks_event_discord_attempt_set_check",
+ "value": "(\"knight_hacks_event\".\"discord_outbound_attempt_token\" IS NULL AND \"knight_hacks_event\".\"discord_outbound_attempted_at\" IS NULL AND \"knight_hacks_event\".\"discord_outbound_attempt_revision\" IS NULL) OR (\"knight_hacks_event\".\"discord_outbound_attempt_token\" IS NOT NULL AND \"knight_hacks_event\".\"discord_outbound_attempted_at\" IS NOT NULL AND \"knight_hacks_event\".\"discord_outbound_attempt_revision\" IS NOT NULL)"
+ },
+ "knight_hacks_event_google_attempt_set_check": {
+ "name": "knight_hacks_event_google_attempt_set_check",
+ "value": "(\"knight_hacks_event\".\"google_outbound_attempt_token\" IS NULL AND \"knight_hacks_event\".\"google_outbound_attempted_at\" IS NULL AND \"knight_hacks_event\".\"google_outbound_attempt_revision\" IS NULL) OR (\"knight_hacks_event\".\"google_outbound_attempt_token\" IS NOT NULL AND \"knight_hacks_event\".\"google_outbound_attempted_at\" IS NOT NULL AND \"knight_hacks_event\".\"google_outbound_attempt_revision\" IS NOT NULL)"
+ },
+ "knight_hacks_event_visibility_set_check": {
+ "name": "knight_hacks_event_visibility_set_check",
+ "value": "(\"knight_hacks_event\".\"visibility_revision\" IS NULL AND \"knight_hacks_event\".\"visibility_dues_paying\" IS NULL AND \"knight_hacks_event\".\"visibility_roles\" IS NULL AND \"knight_hacks_event\".\"visibility_internal\" IS NULL) OR (\"knight_hacks_event\".\"visibility_revision\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_dues_paying\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_roles\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_internal\" IS NOT NULL)"
+ },
+ "knight_hacks_event_visibility_revision_check": {
+ "name": "knight_hacks_event_visibility_revision_check",
+ "value": "\"knight_hacks_event\".\"visibility_revision\" IS NULL OR \"knight_hacks_event\".\"visibility_revision\" <= \"knight_hacks_event\".\"sync_revision\""
+ },
+ "knight_hacks_event_discord_applied_revision_check": {
+ "name": "knight_hacks_event_discord_applied_revision_check",
+ "value": "\"knight_hacks_event\".\"discord_applied_revision\" IS NULL OR \"knight_hacks_event\".\"discord_applied_revision\" <= \"knight_hacks_event\".\"sync_revision\""
+ },
+ "knight_hacks_event_google_applied_revision_check": {
+ "name": "knight_hacks_event_google_applied_revision_check",
+ "value": "\"knight_hacks_event\".\"google_applied_revision\" IS NULL OR \"knight_hacks_event\".\"google_applied_revision\" <= \"knight_hacks_event\".\"sync_revision\""
+ },
+ "knight_hacks_event_discord_synced_state_check": {
+ "name": "knight_hacks_event_discord_synced_state_check",
+ "value": "\"knight_hacks_event\".\"discord_sync_state\" IS DISTINCT FROM 'synced' OR (\"knight_hacks_event\".\"discord_id\" IS NOT NULL AND \"knight_hacks_event\".\"discord_applied_revision\" IS NOT NULL AND \"knight_hacks_event\".\"discord_applied_revision\" = \"knight_hacks_event\".\"sync_revision\" AND \"knight_hacks_event\".\"discord_applied_entity_type\" IS NOT NULL AND ((\"knight_hacks_event\".\"discord_applied_entity_type\" = 'external' AND \"knight_hacks_event\".\"discord_applied_channel_id\" IS NULL) OR (\"knight_hacks_event\".\"discord_applied_entity_type\" IN ('voice', 'stage') AND \"knight_hacks_event\".\"discord_applied_channel_id\" IS NOT NULL)))"
+ },
+ "knight_hacks_event_google_synced_state_check": {
+ "name": "knight_hacks_event_google_synced_state_check",
+ "value": "\"knight_hacks_event\".\"google_sync_state\" IS DISTINCT FROM 'synced' OR (\"knight_hacks_event\".\"google_id\" IS NOT NULL AND \"knight_hacks_event\".\"google_applied_revision\" IS NOT NULL AND \"knight_hacks_event\".\"google_applied_revision\" = \"knight_hacks_event\".\"sync_revision\" AND \"knight_hacks_event\".\"google_applied_destination\" IS NOT NULL AND \"knight_hacks_event\".\"google_applied_calendar_id\" IS NOT NULL)"
+ },
+ "knight_hacks_event_published_visibility_check": {
+ "name": "knight_hacks_event_published_visibility_check",
+ "value": "\"knight_hacks_event\".\"published_at\" IS NULL OR (\"knight_hacks_event\".\"visibility_revision\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_dues_paying\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_roles\" IS NOT NULL AND \"knight_hacks_event\".\"visibility_internal\" IS NOT NULL)"
+ },
+ "knight_hacks_event_discord_acknowledgement_time_check": {
+ "name": "knight_hacks_event_discord_acknowledgement_time_check",
+ "value": "\"knight_hacks_event\".\"discord_no_projection_acknowledged_by\" IS NULL OR \"knight_hacks_event\".\"discord_no_projection_acknowledged_at\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_attendee": {
+ "name": "knight_hacks_event_attendee",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "checked_in_at": {
+ "name": "checked_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_by": {
+ "name": "checked_in_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded_estimated": {
+ "name": "points_awarded_estimated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_attendee_event_member_idx": {
+ "name": "knight_hacks_event_attendee_event_member_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_attendee_member_event_idx": {
+ "name": "knight_hacks_event_attendee_member_event_idx",
+ "columns": [
+ {
+ "expression": "member_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_attendee_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_event_attendee_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_event_attendee",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_attendee_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_attendee_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_attendee",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_attendee_checked_in_by_auth_user_id_fk": {
+ "name": "knight_hacks_event_attendee_checked_in_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_event_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["checked_in_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_feedback": {
+ "name": "knight_hacks_event_feedback",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "overall_event_rating": {
+ "name": "overall_event_rating",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fun_rating": {
+ "name": "fun_rating",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "learned_rating": {
+ "name": "learned_rating",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "heard_about_us": {
+ "name": "heard_about_us",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "additional_feedback": {
+ "name": "additional_feedback",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "similar_event": {
+ "name": "similar_event",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_event_feedback_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_event_feedback_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_event_feedback",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_feedback_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_feedback",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_feedback_config": {
+ "name": "knight_hacks_event_feedback_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "closes_at": {
+ "name": "closes_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reward_points": {
+ "name": "reward_points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 5
+ },
+ "template_revision": {
+ "name": "template_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "custom_questions": {
+ "name": "custom_questions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_event_feedback_config_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_feedback_config_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_config",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_config_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_event_feedback_config_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_config",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_feedback_config_eventId_unique": {
+ "name": "knight_hacks_event_feedback_config_eventId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id"]
+ },
+ "knight_hacks_event_feedback_config_formId_unique": {
+ "name": "knight_hacks_event_feedback_config_formId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["form_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_feedback_config_reward_check": {
+ "name": "knight_hacks_event_feedback_config_reward_check",
+ "value": "\"knight_hacks_event_feedback_config\".\"reward_points\" = 5"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_feedback_reward": {
+ "name": "knight_hacks_event_feedback_reward",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "member_id": {
+ "name": "member_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 5
+ },
+ "awarded_at": {
+ "name": "awarded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_event_feedback_reward_event_id_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_event_feedback_reward_event_id_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_reward",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_reward_member_id_knight_hacks_member_id_fk": {
+ "name": "knight_hacks_event_feedback_reward_member_id_knight_hacks_member_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_reward",
+ "tableTo": "knight_hacks_member",
+ "columnsFrom": ["member_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_feedback_reward_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_event_feedback_reward_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_event_feedback_reward",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_feedback_reward_event_member_unique": {
+ "name": "knight_hacks_event_feedback_reward_event_member_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id", "member_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_feedback_reward_points_check": {
+ "name": "knight_hacks_event_feedback_reward_points_check",
+ "value": "\"knight_hacks_event_feedback_reward\".\"points_awarded\" = 5"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_publication_work": {
+ "name": "knight_hacks_event_publication_work",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "publication_id": {
+ "name": "publication_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "hackathon_event_publication_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_enabled": {
+ "name": "target_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_revision": {
+ "name": "event_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "publication_revision": {
+ "name": "publication_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "event_publication_work_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "last_attempt_at": {
+ "name": "last_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_token": {
+ "name": "lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_publication_work_due_idx": {
+ "name": "knight_hacks_event_publication_work_due_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_publication_work_health_idx": {
+ "name": "knight_hacks_event_publication_work_health_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_publication_work_lease_expiry_idx": {
+ "name": "knight_hacks_event_publication_work_lease_expiry_idx",
+ "columns": [
+ {
+ "expression": "lease_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_publication_work_scoped_publication_fk": {
+ "name": "knight_hacks_event_publication_work_scoped_publication_fk",
+ "tableFrom": "knight_hacks_event_publication_work",
+ "tableTo": "knight_hacks_hackathon_event_publication",
+ "columnsFrom": ["publication_id", "hackathon_id", "provider"],
+ "columnsTo": ["id", "hackathon_id", "provider"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_event_publication_work_scoped_event_fk": {
+ "name": "knight_hacks_event_publication_work_scoped_event_fk",
+ "tableFrom": "knight_hacks_event_publication_work",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_event_publication_work_event_provider_unique": {
+ "name": "knight_hacks_event_publication_work_event_provider_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id", "provider"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_publication_work_attempt_count_check": {
+ "name": "knight_hacks_event_publication_work_attempt_count_check",
+ "value": "\"knight_hacks_event_publication_work\".\"attempt_count\" >= 0"
+ },
+ "knight_hacks_event_publication_work_revisions_check": {
+ "name": "knight_hacks_event_publication_work_revisions_check",
+ "value": "\"knight_hacks_event_publication_work\".\"event_revision\" >= 1 AND \"knight_hacks_event_publication_work\".\"publication_revision\" >= 1"
+ },
+ "knight_hacks_event_publication_work_lease_pair_check": {
+ "name": "knight_hacks_event_publication_work_lease_pair_check",
+ "value": "(\"knight_hacks_event_publication_work\".\"lease_token\" IS NULL) = (\"knight_hacks_event_publication_work\".\"lease_expires_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_event_tag": {
+ "name": "knight_hacks_event_tag",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_name": {
+ "name": "normalized_name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "default_points": {
+ "name": "default_points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "color": {
+ "name": "color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_event_tag_club_normalized_name_unique": {
+ "name": "knight_hacks_event_tag_club_normalized_name_unique",
+ "columns": [
+ {
+ "expression": "normalized_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_event_tag\".\"hackathon_id\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_tag_hackathon_normalized_name_unique": {
+ "name": "knight_hacks_event_tag_hackathon_normalized_name_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "normalized_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_event_tag\".\"hackathon_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_event_tag_hackathon_idx": {
+ "name": "knight_hacks_event_tag_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_event_tag_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_event_tag_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_event_tag",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_event_tag_default_points_check": {
+ "name": "knight_hacks_event_tag_default_points_check",
+ "value": "\"knight_hacks_event_tag\".\"default_points\" >= 0"
+ },
+ "knight_hacks_event_tag_color_check": {
+ "name": "knight_hacks_event_tag_color_check",
+ "value": "\"knight_hacks_event_tag\".\"color\" ~ '^#[0-9A-Fa-f]{6}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_attachment": {
+ "name": "knight_hacks_form_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "object_name": {
+ "name": "object_name",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_name": {
+ "name": "file_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_type": {
+ "name": "content_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size": {
+ "name": "size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "purpose": {
+ "name": "purpose",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'response'"
+ },
+ "finalized_at": {
+ "name": "finalized_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_form_attachment_form_idx": {
+ "name": "knight_hacks_form_attachment_form_idx",
+ "columns": [
+ {
+ "expression": "form_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_form_attachment_response_idx": {
+ "name": "knight_hacks_form_attachment_response_idx",
+ "columns": [
+ {
+ "expression": "response_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_form_attachment_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_attachment_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_attachment",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_attachment_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_form_attachment_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_form_attachment",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_attachment_owner_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_form_attachment_owner_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_form_attachment",
+ "tableTo": "auth_user",
+ "columnsFrom": ["owner_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_attachment_objectName_unique": {
+ "name": "knight_hacks_form_attachment_objectName_unique",
+ "nullsNotDistinct": false,
+ "columns": ["object_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_form_attachment_purpose_check": {
+ "name": "knight_hacks_form_attachment_purpose_check",
+ "value": "\"knight_hacks_form_attachment\".\"purpose\" IN ('banner', 'instruction', 'response')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_callback_configuration": {
+ "name": "knight_hacks_form_callback_configuration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "callback_slug": {
+ "name": "callback_slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "mappings": {
+ "name": "mappings",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_callback_configuration_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_callback_configuration_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_callback_configuration",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_callback_configuration_form_slug_unique": {
+ "name": "knight_hacks_form_callback_configuration_form_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": ["form_id", "callback_slug"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_callback_execution": {
+ "name": "knight_hacks_form_callback_execution",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "configuration_id": {
+ "name": "configuration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callback_slug": {
+ "name": "callback_slug",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input": {
+ "name": "input",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "form_callback_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "available_at": {
+ "name": "available_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "lease_token": {
+ "name": "lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "succeeded_at": {
+ "name": "succeeded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_form_callback_execution_status_available_idx": {
+ "name": "knight_hacks_form_callback_execution_status_available_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "available_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_form_callback_execution_configuration_id_knight_hacks_form_callback_configuration_id_fk": {
+ "name": "knight_hacks_form_callback_execution_configuration_id_knight_hacks_form_callback_configuration_id_fk",
+ "tableFrom": "knight_hacks_form_callback_execution",
+ "tableTo": "knight_hacks_form_callback_configuration",
+ "columnsFrom": ["configuration_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_callback_execution_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_form_callback_execution_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_form_callback_execution",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_response": {
+ "name": "knight_hacks_form_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form": {
+ "name": "form",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_data": {
+ "name": "response_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "form_revision": {
+ "name": "form_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "response_snapshot": {
+ "name": "response_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "edited_at": {
+ "name": "edited_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_form_response_form_created_idx": {
+ "name": "knight_hacks_form_response_form_created_idx",
+ "columns": [
+ {
+ "expression": "form",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_form_response_user_created_idx": {
+ "name": "knight_hacks_form_response_user_created_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_form_response_form_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_response_form_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_response",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_response_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_form_response_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_form_response",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_response_roles": {
+ "name": "knight_hacks_form_response_roles",
+ "schema": "",
+ "columns": {
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_response_roles_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_response_roles_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_response_roles",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_response_roles_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_response_roles_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_response_roles",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_response_roles_form_id_role_id_pk": {
+ "name": "knight_hacks_form_response_roles_form_id_role_id_pk",
+ "columns": ["form_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_section_edit_role": {
+ "name": "knight_hacks_form_section_edit_role",
+ "schema": "",
+ "columns": {
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_section_edit_role_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_section_edit_role_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_section_edit_role",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_section_edit_role_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_section_edit_role_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_section_edit_role",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_section_edit_role_section_id_role_id_pk": {
+ "name": "knight_hacks_form_section_edit_role_section_id_role_id_pk",
+ "columns": ["section_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_section_roles": {
+ "name": "knight_hacks_form_section_roles",
+ "schema": "",
+ "columns": {
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_section_roles_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_section_roles_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_section_roles",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_section_roles_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_section_roles_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_section_roles",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_section_roles_section_id_role_id_pk": {
+ "name": "knight_hacks_form_section_roles_section_id_role_id_pk",
+ "columns": ["section_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_section_view_role": {
+ "name": "knight_hacks_form_section_view_role",
+ "schema": "",
+ "columns": {
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_section_view_role_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_section_view_role_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_section_view_role",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_section_view_role_role_id_auth_roles_id_fk": {
+ "name": "knight_hacks_form_section_view_role_role_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_form_section_view_role",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_section_view_role_section_id_role_id_pk": {
+ "name": "knight_hacks_form_section_view_role_section_id_role_id_pk",
+ "columns": ["section_id", "role_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_sections": {
+ "name": "knight_hacks_form_sections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "order": {
+ "name": "order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_sections_name_unique": {
+ "name": "knight_hacks_form_sections_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_single_response_claim": {
+ "name": "knight_hacks_form_single_response_claim",
+ "schema": "",
+ "columns": {
+ "form_id": {
+ "name": "form_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_id": {
+ "name": "response_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_single_response_claim_form_id_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_form_single_response_claim_form_id_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_form_single_response_claim",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_single_response_claim_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_form_single_response_claim_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_form_single_response_claim",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_form_single_response_claim_response_id_knight_hacks_form_response_id_fk": {
+ "name": "knight_hacks_form_single_response_claim_response_id_knight_hacks_form_response_id_fk",
+ "tableFrom": "knight_hacks_form_single_response_claim",
+ "tableTo": "knight_hacks_form_response",
+ "columnsFrom": ["response_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_form_single_response_claim_form_id_user_id_pk": {
+ "name": "knight_hacks_form_single_response_claim_form_id_user_id_pk",
+ "columns": ["form_id", "user_id"]
+ }
+ },
+ "uniqueConstraints": {
+ "knight_hacks_form_single_response_claim_responseId_unique": {
+ "name": "knight_hacks_form_single_response_claim_responseId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["response_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_form_schemas": {
+ "name": "knight_hacks_form_schemas",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug_name": {
+ "name": "slug_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "kind": {
+ "name": "kind",
+ "type": "form_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'general'"
+ },
+ "state": {
+ "name": "state",
+ "type": "form_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "opens_at": {
+ "name": "opens_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "closes_at": {
+ "name": "closes_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "manually_closed": {
+ "name": "manually_closed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "response_mode": {
+ "name": "response_mode",
+ "type": "form_response_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'single_locked'"
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "dues_only": {
+ "name": "dues_only",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "allow_resubmission": {
+ "name": "allow_resubmission",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "allow_edit": {
+ "name": "allow_edit",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "form_data": {
+ "name": "form_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "form_validator_json": {
+ "name": "form_validator_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "section": {
+ "name": "section",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'General'"
+ },
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_closed": {
+ "name": "is_closed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_form_schemas_section_id_knight_hacks_form_sections_id_fk": {
+ "name": "knight_hacks_form_schemas_section_id_knight_hacks_form_sections_id_fk",
+ "tableFrom": "knight_hacks_form_schemas",
+ "tableTo": "knight_hacks_form_sections",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_form_schemas_slugName_unique": {
+ "name": "knight_hacks_form_schemas_slugName_unique",
+ "nullsNotDistinct": false,
+ "columns": ["slug_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_guest_judge_session": {
+ "name": "knight_hacks_guest_judge_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_link_id": {
+ "name": "access_link_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "char(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_seen_at": {
+ "name": "last_seen_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_by_user_id": {
+ "name": "revoked_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revocation_reason": {
+ "name": "revocation_reason",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_guest_judge_session_access_link_idx": {
+ "name": "knight_hacks_guest_judge_session_access_link_idx",
+ "columns": [
+ {
+ "expression": "access_link_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_guest_judge_session_revoked_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_guest_judge_session_revoked_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_guest_judge_session",
+ "tableTo": "auth_user",
+ "columnsFrom": ["revoked_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_guest_judge_session_access_link_scope_fk": {
+ "name": "knight_hacks_guest_judge_session_access_link_scope_fk",
+ "tableFrom": "knight_hacks_guest_judge_session",
+ "tableTo": "knight_hacks_judging_room_access_link",
+ "columnsFrom": ["access_link_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_guest_judge_session_judge_scope_fk": {
+ "name": "knight_hacks_guest_judge_session_judge_scope_fk",
+ "tableFrom": "knight_hacks_guest_judge_session",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_guest_judge_session_token_hash_unique": {
+ "name": "knight_hacks_guest_judge_session_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon": {
+ "name": "knight_hacks_hackathon",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "theme": {
+ "name": "theme",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'America/New_York'"
+ },
+ "application_url": {
+ "name": "application_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "application_background_enabled": {
+ "name": "application_background_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "application_background_key": {
+ "name": "application_background_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_template_enabled": {
+ "name": "email_template_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "email_template_key": {
+ "name": "email_template_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "application_open": {
+ "name": "application_open",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "application_deadline": {
+ "name": "application_deadline",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "confirmation_deadline": {
+ "name": "confirmation_deadline",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "start_date": {
+ "name": "start_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_date": {
+ "name": "end_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confirmation_capacity": {
+ "name": "confirmation_capacity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "general_hacker_discord_role_id": {
+ "name": "general_hacker_discord_role_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_announcement_channel_id": {
+ "name": "event_announcement_channel_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_name_unique": {
+ "name": "knight_hacks_hackathon_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_event_announcement_channel_id_check": {
+ "name": "knight_hacks_hackathon_event_announcement_channel_id_check",
+ "value": "\"knight_hacks_hackathon\".\"event_announcement_channel_id\" IS NULL OR \"knight_hacks_hackathon\".\"event_announcement_channel_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_general_hacker_discord_role_id_check": {
+ "name": "knight_hacks_hackathon_general_hacker_discord_role_id_check",
+ "value": "\"knight_hacks_hackathon\".\"general_hacker_discord_role_id\" IS NULL OR \"knight_hacks_hackathon\".\"general_hacker_discord_role_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_confirmation_capacity_check": {
+ "name": "knight_hacks_hackathon_confirmation_capacity_check",
+ "value": "\"knight_hacks_hackathon\".\"confirmation_capacity\" IS NULL OR \"knight_hacks_hackathon\".\"confirmation_capacity\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_agreement_definition": {
+ "name": "knight_hacks_hackathon_agreement_definition",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stage": {
+ "name": "stage",
+ "type": "hacker_agreement_stage",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "legal_text": {
+ "name": "legal_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_agreement_definition_active_unique": {
+ "name": "knight_hacks_hackathon_agreement_definition_active_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "stage",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hackathon_agreement_definition\".\"active\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_agreement_definition_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_agreement_definition_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_agreement_definition",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_agreement_definition_created_by_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_agreement_definition_created_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_agreement_definition",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_agreement_definition_version_unique": {
+ "name": "knight_hacks_hackathon_agreement_definition_version_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "stage", "key", "version"]
+ },
+ "knight_hacks_hackathon_agreement_definition_id_hackathon_unique": {
+ "name": "knight_hacks_hackathon_agreement_definition_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_agreement_definition_content_check": {
+ "name": "knight_hacks_hackathon_agreement_definition_content_check",
+ "value": "\"knight_hacks_hackathon_agreement_definition\".\"legal_text\" IS NOT NULL OR \"knight_hacks_hackathon_agreement_definition\".\"url\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_class": {
+ "name": "knight_hacks_hackathon_class",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_role_id": {
+ "name": "discord_role_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "color": {
+ "name": "color",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_class_hackathon_idx": {
+ "name": "knight_hacks_hackathon_class_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_class_one_vip_per_hackathon": {
+ "name": "knight_hacks_hackathon_class_one_vip_per_hackathon",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hackathon_class\".\"kind\" = 'vip'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_class_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_class_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_class",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_class_id_hackathon_unique": {
+ "name": "knight_hacks_hackathon_class_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_class_discord_role_id_check": {
+ "name": "knight_hacks_hackathon_class_discord_role_id_check",
+ "value": "\"knight_hacks_hackathon_class\".\"discord_role_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_class_color_check": {
+ "name": "knight_hacks_hackathon_class_color_check",
+ "value": "\"knight_hacks_hackathon_class\".\"color\" ~ '^#[0-9a-fA-F]{6}$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_event_publication": {
+ "name": "knight_hacks_hackathon_event_publication",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "hackathon_event_publication_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "desired_enabled": {
+ "name": "desired_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "requested_at": {
+ "name": "requested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "requested_by": {
+ "name": "requested_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_reconciled_at": {
+ "name": "last_reconciled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_converged_at": {
+ "name": "last_converged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_event_publication_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_event_publication_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_event_publication",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_event_publication_requested_by_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_event_publication_requested_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_event_publication",
+ "tableTo": "auth_user",
+ "columnsFrom": ["requested_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_event_publication_provider_unique": {
+ "name": "knight_hacks_hackathon_event_publication_provider_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "provider"]
+ },
+ "knight_hacks_hackathon_event_publication_id_scope_unique": {
+ "name": "knight_hacks_hackathon_event_publication_id_scope_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id", "provider"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_event_publication_revision_check": {
+ "name": "knight_hacks_hackathon_event_publication_revision_check",
+ "value": "\"knight_hacks_hackathon_event_publication\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_event_reminder_delivery": {
+ "name": "knight_hacks_hackathon_event_reminder_delivery",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_start_at": {
+ "name": "event_start_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reminder_key": {
+ "name": "reminder_key",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "destination_channel_id_snapshot": {
+ "name": "destination_channel_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id_snapshot": {
+ "name": "role_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_event_id_snapshot": {
+ "name": "discord_event_id_snapshot",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "content_snapshot": {
+ "name": "content_snapshot",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "hackathon_event_reminder_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "locked_at": {
+ "name": "locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_event_reminder_delivery_pending_idx": {
+ "name": "knight_hacks_hackathon_event_reminder_delivery_pending_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_event_reminder_scoped_event_fk": {
+ "name": "knight_hacks_hackathon_event_reminder_scoped_event_fk",
+ "tableFrom": "knight_hacks_hackathon_event_reminder_delivery",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_event_reminder_delivery_identity_unique": {
+ "name": "knight_hacks_hackathon_event_reminder_delivery_identity_unique",
+ "nullsNotDistinct": false,
+ "columns": ["event_id", "reminder_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_event_reminder_channel_id_check": {
+ "name": "knight_hacks_hackathon_event_reminder_channel_id_check",
+ "value": "\"knight_hacks_hackathon_event_reminder_delivery\".\"destination_channel_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_event_reminder_role_id_check": {
+ "name": "knight_hacks_hackathon_event_reminder_role_id_check",
+ "value": "\"knight_hacks_hackathon_event_reminder_delivery\".\"role_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hackathon_event_reminder_attempt_count_check": {
+ "name": "knight_hacks_hackathon_event_reminder_attempt_count_check",
+ "value": "\"knight_hacks_hackathon_event_reminder_delivery\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_judging_configuration": {
+ "name": "knight_hacks_hackathon_judging_configuration",
+ "schema": "",
+ "columns": {
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "project_inventory_locked_at": {
+ "name": "project_inventory_locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_inventory_locked_by_user_id": {
+ "name": "project_inventory_locked_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "judging_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "display_all_results_to_members": {
+ "name": "display_all_results_to_members",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "judging_comms_channel_id": {
+ "name": "judging_comms_channel_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "opened_at": {
+ "name": "opened_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "closed_at": {
+ "name": "closed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_judging_configuration_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_judging_configuration_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_judging_configuration",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_judging_configuration_project_inventory_locked_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_judging_configuration_project_inventory_locked_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_judging_configuration",
+ "tableTo": "auth_user",
+ "columnsFrom": ["project_inventory_locked_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_authorization_code": {
+ "name": "knight_hacks_hackathon_portal_authorization_code",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "portal_client_id": {
+ "name": "portal_client_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "better_auth_session_id": {
+ "name": "better_auth_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code_hash": {
+ "name": "code_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code_challenge": {
+ "name": "code_challenge",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code_challenge_method": {
+ "name": "code_challenge_method",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'S256'"
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_portal_authorization_code_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_expiry_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_portal_authorization_code_client_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_client_expiry_idx",
+ "columns": [
+ {
+ "expression": "portal_client_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_authorization_code_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_authorization_code",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_authorization_code_better_auth_session_id_auth_session_id_fk": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_better_auth_session_id_auth_session_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_authorization_code",
+ "tableTo": "auth_session",
+ "columnsFrom": ["better_auth_session_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_authorization_code_scoped_client_fk": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_scoped_client_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_authorization_code",
+ "tableTo": "knight_hacks_hackathon_portal_client",
+ "columnsFrom": ["portal_client_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_authorization_code_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["code_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_authorization_code_hash_check": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_authorization_code\".\"code_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_authorization_code_method_check": {
+ "name": "knight_hacks_hackathon_portal_authorization_code_method_check",
+ "value": "\"knight_hacks_hackathon_portal_authorization_code\".\"code_challenge_method\" = 'S256'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_client": {
+ "name": "knight_hacks_hackathon_portal_client",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_id": {
+ "name": "client_id",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "production_origin": {
+ "name": "production_origin",
+ "type": "varchar(2048)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_client_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_portal_client_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_client",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_client_created_by_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_portal_client_created_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_client",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_client_client_id_unique": {
+ "name": "knight_hacks_hackathon_portal_client_client_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["client_id"]
+ },
+ "knight_hacks_hackathon_portal_client_hackathon_unique": {
+ "name": "knight_hacks_hackathon_portal_client_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id"]
+ },
+ "knight_hacks_hackathon_portal_client_origin_unique": {
+ "name": "knight_hacks_hackathon_portal_client_origin_unique",
+ "nullsNotDistinct": false,
+ "columns": ["production_origin"]
+ },
+ "knight_hacks_hackathon_portal_client_id_hackathon_unique": {
+ "name": "knight_hacks_hackathon_portal_client_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_client_origin_check": {
+ "name": "knight_hacks_hackathon_portal_client_origin_check",
+ "value": "\"knight_hacks_hackathon_portal_client\".\"production_origin\" ~ '^https://[a-z0-9-]+([.][a-z0-9-]+)*[.]knighthacks[.]org$'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_session": {
+ "name": "knight_hacks_hackathon_portal_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "portal_client_id": {
+ "name": "portal_client_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "better_auth_session_id": {
+ "name": "better_auth_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token_hash": {
+ "name": "access_token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_hash": {
+ "name": "refresh_token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_expires_at": {
+ "name": "access_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_expires_at": {
+ "name": "refresh_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_version": {
+ "name": "refresh_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_portal_session_access_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_session_access_expiry_idx",
+ "columns": [
+ {
+ "expression": "access_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "revoked_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_portal_session_refresh_expiry_idx": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_expiry_idx",
+ "columns": [
+ {
+ "expression": "refresh_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hackathon_portal_session_user_client_idx": {
+ "name": "knight_hacks_hackathon_portal_session_user_client_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "portal_client_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_session_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hackathon_portal_session_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_session_better_auth_session_id_auth_session_id_fk": {
+ "name": "knight_hacks_hackathon_portal_session_better_auth_session_id_auth_session_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session",
+ "tableTo": "auth_session",
+ "columnsFrom": ["better_auth_session_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_portal_session_scoped_client_fk": {
+ "name": "knight_hacks_hackathon_portal_session_scoped_client_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session",
+ "tableTo": "knight_hacks_hackathon_portal_client",
+ "columnsFrom": ["portal_client_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_session_access_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_session_access_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["access_token_hash"]
+ },
+ "knight_hacks_hackathon_portal_session_refresh_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["refresh_token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_session_access_hash_check": {
+ "name": "knight_hacks_hackathon_portal_session_access_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_session\".\"access_token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_session_refresh_hash_check": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_session\".\"refresh_token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_session_refresh_version_check": {
+ "name": "knight_hacks_hackathon_portal_session_refresh_version_check",
+ "value": "\"knight_hacks_hackathon_portal_session\".\"refresh_version\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_portal_session_credential": {
+ "name": "knight_hacks_hackathon_portal_session_credential",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "portal_session_id": {
+ "name": "portal_session_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_kind": {
+ "name": "token_kind",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rotated_at": {
+ "name": "rotated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hackathon_portal_session_credential_session_idx": {
+ "name": "knight_hacks_hackathon_portal_session_credential_session_idx",
+ "columns": [
+ {
+ "expression": "portal_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hackathon_portal_session_credential_portal_session_id_knight_hacks_hackathon_portal_session_id_fk": {
+ "name": "knight_hacks_hackathon_portal_session_credential_portal_session_id_knight_hacks_hackathon_portal_session_id_fk",
+ "tableFrom": "knight_hacks_hackathon_portal_session_credential",
+ "tableTo": "knight_hacks_hackathon_portal_session",
+ "columnsFrom": ["portal_session_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_portal_session_credential_hash_unique": {
+ "name": "knight_hacks_hackathon_portal_session_credential_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_portal_session_credential_hash_check": {
+ "name": "knight_hacks_hackathon_portal_session_credential_hash_check",
+ "value": "\"knight_hacks_hackathon_portal_session_credential\".\"token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hackathon_portal_session_credential_kind_check": {
+ "name": "knight_hacks_hackathon_portal_session_credential_kind_check",
+ "value": "\"knight_hacks_hackathon_portal_session_credential\".\"token_kind\" IN ('access', 'refresh')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_sponsor": {
+ "name": "knight_hacks_hackathon_sponsor",
+ "schema": "",
+ "columns": {
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sponsor_id": {
+ "name": "sponsor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tier": {
+ "name": "tier",
+ "type": "sponsor_tier",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_sponsor_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_sponsor_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_sponsor",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_sponsor_sponsor_id_knight_hacks_sponsor_id_fk": {
+ "name": "knight_hacks_hackathon_sponsor_sponsor_id_knight_hacks_sponsor_id_fk",
+ "tableFrom": "knight_hacks_hackathon_sponsor",
+ "tableTo": "knight_hacks_sponsor",
+ "columnsFrom": ["sponsor_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hackathon_status_email": {
+ "name": "knight_hacks_hackathon_status_email",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "varchar(200)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hackathon_status_email_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hackathon_status_email_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hackathon_status_email",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hackathon_status_email_template_id_email_template_id_fk": {
+ "name": "knight_hacks_hackathon_status_email_template_id_email_template_id_fk",
+ "tableFrom": "knight_hacks_hackathon_status_email",
+ "tableTo": "email_template",
+ "columnsFrom": ["template_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hackathon_status_email_hackathon_status_unique": {
+ "name": "knight_hacks_hackathon_status_email_hackathon_status_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "status"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hackathon_status_email_status_check": {
+ "name": "knight_hacks_hackathon_status_email_status_check",
+ "value": "\"knight_hacks_hackathon_status_email\".\"status\" <> 'checkedin'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker": {
+ "name": "knight_hacks_hacker",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "age": {
+ "name": "age",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'United States of America'"
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Computer Science'"
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "survey_1": {
+ "name": "survey_1",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "survey_2": {
+ "name": "survey_2",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_first_time": {
+ "name": "is_first_time",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "food_allergies": {
+ "name": "food_allergies",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agrees_to_receive_emails_from_mlh": {
+ "name": "agrees_to_receive_emails_from_mlh",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "agrees_to_mlh_code_of_conduct": {
+ "name": "agrees_to_mlh_code_of_conduct",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "agrees_to_mlh_data_sharing": {
+ "name": "agrees_to_mlh_data_sharing",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "date_created": {
+ "name": "date_created",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "time_created": {
+ "name": "time_created",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hacker_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_agreement_acceptance": {
+ "name": "knight_hacks_hacker_agreement_acceptance",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "attendee_id": {
+ "name": "attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agreement_definition_id": {
+ "name": "agreement_definition_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "accepted": {
+ "name": "accepted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provenance": {
+ "name": "provenance",
+ "type": "hacker_agreement_provenance",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'explicit'"
+ },
+ "accepted_at": {
+ "name": "accepted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "recorded_at": {
+ "name": "recorded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_agreement_acceptance_attendee_idx": {
+ "name": "knight_hacks_hacker_agreement_acceptance_attendee_idx",
+ "columns": [
+ {
+ "expression": "attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_agreement_acceptance_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_agreement_acceptance_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_agreement_acceptance",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_agreement_acceptance_scoped_definition_fk": {
+ "name": "knight_hacks_hacker_agreement_acceptance_scoped_definition_fk",
+ "tableFrom": "knight_hacks_hacker_agreement_acceptance",
+ "tableTo": "knight_hacks_hackathon_agreement_definition",
+ "columnsFrom": ["agreement_definition_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_agreement_acceptance_attendee_definition_unique": {
+ "name": "knight_hacks_hacker_agreement_acceptance_attendee_definition_unique",
+ "nullsNotDistinct": false,
+ "columns": ["attendee_id", "agreement_definition_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_agreement_acceptance_timestamp_check": {
+ "name": "knight_hacks_hacker_agreement_acceptance_timestamp_check",
+ "value": "(\"knight_hacks_hacker_agreement_acceptance\".\"accepted\" = true AND \"knight_hacks_hacker_agreement_acceptance\".\"accepted_at\" IS NOT NULL) OR (\"knight_hacks_hacker_agreement_acceptance\".\"accepted\" = false AND \"knight_hacks_hacker_agreement_acceptance\".\"accepted_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_attendee": {
+ "name": "knight_hacks_hacker_attendee",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hacker_id": {
+ "name": "hacker_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_revision_id": {
+ "name": "profile_revision_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "time_applied": {
+ "name": "time_applied",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "time_confirmed": {
+ "name": "time_confirmed",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points": {
+ "name": "points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "class_id": {
+ "name": "class_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_vip": {
+ "name": "is_vip",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_first_time": {
+ "name": "is_first_time",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "survey1": {
+ "name": "survey1",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "survey2": {
+ "name": "survey2",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_at": {
+ "name": "checked_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_by": {
+ "name": "checked_in_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class": {
+ "name": "class",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": null
+ },
+ "blacklisted_at": {
+ "name": "blacklisted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "blacklisted_by": {
+ "name": "blacklisted_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "blacklist_reason": {
+ "name": "blacklist_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_status_send_id": {
+ "name": "last_status_send_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_attendee_class_idx": {
+ "name": "knight_hacks_hacker_attendee_class_idx",
+ "columns": [
+ {
+ "expression": "class_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_hackathon_class_idx": {
+ "name": "knight_hacks_hacker_attendee_hackathon_class_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "class_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_checked_in_by_idx": {
+ "name": "knight_hacks_hacker_attendee_checked_in_by_idx",
+ "columns": [
+ {
+ "expression": "checked_in_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_hackathon_checked_in_idx": {
+ "name": "knight_hacks_hacker_attendee_hackathon_checked_in_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_profile_hackathon_unique": {
+ "name": "knight_hacks_hacker_attendee_profile_hackathon_unique",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hacker_attendee\".\"profile_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_profile_idx": {
+ "name": "knight_hacks_hacker_attendee_profile_idx",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_profile_revision_idx": {
+ "name": "knight_hacks_hacker_attendee_profile_revision_idx",
+ "columns": [
+ {
+ "expression": "profile_revision_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_hackathon_status_idx": {
+ "name": "knight_hacks_hacker_attendee_hackathon_status_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_blacklisted_by_idx": {
+ "name": "knight_hacks_hacker_attendee_blacklisted_by_idx",
+ "columns": [
+ {
+ "expression": "blacklisted_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_attendee_last_status_send_idx": {
+ "name": "knight_hacks_hacker_attendee_last_status_send_idx",
+ "columns": [
+ {
+ "expression": "last_status_send_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_attendee_hacker_id_knight_hacks_hacker_id_fk": {
+ "name": "knight_hacks_hacker_attendee_hacker_id_knight_hacks_hacker_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hacker",
+ "columnsFrom": ["hacker_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_profile_id_knight_hacks_hacker_profile_id_fk": {
+ "name": "knight_hacks_hacker_attendee_profile_id_knight_hacks_hacker_profile_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hacker_profile",
+ "columnsFrom": ["profile_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hacker_attendee_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_checked_in_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_attendee_checked_in_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["checked_in_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_blacklisted_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_attendee_blacklisted_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["blacklisted_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_last_status_send_id_email_send_id_fk": {
+ "name": "knight_hacks_hacker_attendee_last_status_send_id_email_send_id_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "email_send",
+ "columnsFrom": ["last_status_send_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_scoped_class_fk": {
+ "name": "knight_hacks_hacker_attendee_scoped_class_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hackathon_class",
+ "columnsFrom": ["class_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_attendee_scoped_profile_revision_fk": {
+ "name": "knight_hacks_hacker_attendee_scoped_profile_revision_fk",
+ "tableFrom": "knight_hacks_hacker_attendee",
+ "tableTo": "knight_hacks_hacker_profile_revision",
+ "columnsFrom": ["profile_revision_id", "profile_id"],
+ "columnsTo": ["id", "profile_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_attendee_hacker_hackathon_unique": {
+ "name": "knight_hacks_hacker_attendee_hacker_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hacker_id", "hackathon_id"]
+ },
+ "knight_hacks_hacker_attendee_id_hackathon_unique": {
+ "name": "knight_hacks_hacker_attendee_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_attendee_blacklist_reason_check": {
+ "name": "knight_hacks_hacker_attendee_blacklist_reason_check",
+ "value": "(\"knight_hacks_hacker_attendee\".\"blacklisted_at\" is null and \"knight_hacks_hacker_attendee\".\"blacklist_reason\" is null)\n or (\"knight_hacks_hacker_attendee\".\"blacklisted_at\" is not null and \"knight_hacks_hacker_attendee\".\"blacklist_reason\" is not null)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_check_in_attempt": {
+ "name": "knight_hacks_hacker_check_in_attempt",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hacker_attendee_id": {
+ "name": "hacker_attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attendance_id": {
+ "name": "attendance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "operator_id": {
+ "name": "operator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "operator_display_name_snapshot": {
+ "name": "operator_display_name_snapshot",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mode": {
+ "name": "mode",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "hacker_check_in_outcome",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_purpose": {
+ "name": "event_purpose",
+ "type": "event_purpose",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_name_snapshot": {
+ "name": "event_name_snapshot",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hacker_name_snapshot": {
+ "name": "hacker_name_snapshot",
+ "type": "varchar(511)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class_id": {
+ "name": "class_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class_name_snapshot": {
+ "name": "class_name_snapshot",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "class_color_snapshot": {
+ "name": "class_color_snapshot",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_vip_snapshot": {
+ "name": "is_vip_snapshot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "was_minor_at_attempt": {
+ "name": "was_minor_at_attempt",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_repeat_occurrence": {
+ "name": "is_repeat_occurrence",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "attempted_at": {
+ "name": "attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_check_in_attempt_hackathon_history_idx": {
+ "name": "knight_hacks_hacker_check_in_attempt_hackathon_history_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "attempted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_check_in_attempt_attendee_history_idx": {
+ "name": "knight_hacks_hacker_check_in_attempt_attendee_history_idx",
+ "columns": [
+ {
+ "expression": "hacker_attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "attempted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_check_in_attempt_expiring_idx": {
+ "name": "knight_hacks_hacker_check_in_attempt_expiring_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"knight_hacks_hacker_check_in_attempt\".\"expires_at\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_check_in_attempt_operator_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_operator_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "auth_user",
+ "columnsFrom": ["operator_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["hacker_attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_attendance_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_attendance_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_hacker_event_attendee",
+ "columnsFrom": [
+ "attendance_id",
+ "hackathon_id",
+ "event_id",
+ "hacker_attendee_id"
+ ],
+ "columnsTo": ["id", "hackathon_id", "event_id", "hacker_att_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_class_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_class_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_hackathon_class",
+ "columnsFrom": ["class_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_check_in_attempt_scoped_event_fk": {
+ "name": "knight_hacks_hacker_check_in_attempt_scoped_event_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_attempt",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_check_in_attempt_attendanceId_unique": {
+ "name": "knight_hacks_hacker_check_in_attempt_attendanceId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["attendance_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_check_in_attempt_mode_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_mode_check",
+ "value": "\"knight_hacks_hacker_check_in_attempt\".\"mode\" IN ('scanner', 'manual')"
+ },
+ "knight_hacks_hacker_check_in_attempt_class_color_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_class_color_check",
+ "value": "\"knight_hacks_hacker_check_in_attempt\".\"class_color_snapshot\" IS NULL OR \"knight_hacks_hacker_check_in_attempt\".\"class_color_snapshot\" ~ '^#[0-9A-Fa-f]{6}$'"
+ },
+ "knight_hacks_hacker_check_in_attempt_points_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_points_check",
+ "value": "\"knight_hacks_hacker_check_in_attempt\".\"points_awarded\" >= 0"
+ },
+ "knight_hacks_hacker_check_in_attempt_success_retention_check": {
+ "name": "knight_hacks_hacker_check_in_attempt_success_retention_check",
+ "value": "(\"knight_hacks_hacker_check_in_attempt\".\"outcome\" = 'checked_in' AND \"knight_hacks_hacker_check_in_attempt\".\"attendance_id\" IS NOT NULL AND \"knight_hacks_hacker_check_in_attempt\".\"expires_at\" IS NULL) OR (\"knight_hacks_hacker_check_in_attempt\".\"outcome\" <> 'checked_in' AND \"knight_hacks_hacker_check_in_attempt\".\"attendance_id\" IS NULL AND \"knight_hacks_hacker_check_in_attempt\".\"expires_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_check_in_pass": {
+ "name": "knight_hacks_hacker_check_in_pass",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "attendee_id": {
+ "name": "attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "issued_at": {
+ "name": "issued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_check_in_pass_active_attendee_unique": {
+ "name": "knight_hacks_hacker_check_in_pass_active_attendee_unique",
+ "columns": [
+ {
+ "expression": "attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hacker_check_in_pass\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_check_in_pass_attendee_idx": {
+ "name": "knight_hacks_hacker_check_in_pass_attendee_idx",
+ "columns": [
+ {
+ "expression": "attendee_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_check_in_pass_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_check_in_pass_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_check_in_pass",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_check_in_pass_token_hash_unique": {
+ "name": "knight_hacks_hacker_check_in_pass_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_check_in_pass_token_hash_check": {
+ "name": "knight_hacks_hacker_check_in_pass_token_hash_check",
+ "value": "\"knight_hacks_hacker_check_in_pass\".\"token_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hacker_check_in_pass_version_check": {
+ "name": "knight_hacks_hacker_check_in_pass_version_check",
+ "value": "\"knight_hacks_hacker_check_in_pass\".\"version\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_discord_role_grant": {
+ "name": "knight_hacks_hacker_discord_role_grant",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hacker_attendee_id": {
+ "name": "hacker_attendee_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_attendance_id": {
+ "name": "source_attendance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_event_id": {
+ "name": "source_event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "hacker_discord_role_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "desired_role_id": {
+ "name": "desired_role_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "hacker_discord_role_grant_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_attempt_at": {
+ "name": "last_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "succeeded_at": {
+ "name": "succeeded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_token": {
+ "name": "lease_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_expires_at": {
+ "name": "lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_discord_role_grant_repair_queue_idx": {
+ "name": "knight_hacks_hacker_discord_role_grant_repair_queue_idx",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "lease_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_discord_role_grant_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["hacker_attendee_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_discord_role_grant_scoped_attendance_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_scoped_attendance_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant",
+ "tableTo": "knight_hacks_hacker_event_attendee",
+ "columnsFrom": [
+ "source_attendance_id",
+ "hackathon_id",
+ "source_event_id",
+ "hacker_attendee_id"
+ ],
+ "columnsTo": ["id", "hackathon_id", "event_id", "hacker_att_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_discord_role_grant_logical_unique": {
+ "name": "knight_hacks_hacker_discord_role_grant_logical_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hacker_attendee_id", "kind"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_discord_role_grant_role_id_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_role_id_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant\".\"desired_role_id\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_count_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_count_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant\".\"attempt_count\" >= 0"
+ },
+ "knight_hacks_hacker_discord_role_grant_lease_pair_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_lease_pair_check",
+ "value": "(\"knight_hacks_hacker_discord_role_grant\".\"lease_token\" IS NULL) = (\"knight_hacks_hacker_discord_role_grant\".\"lease_expires_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_discord_role_grant_attempt": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "grant_id": {
+ "name": "grant_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempt_token": {
+ "name": "attempt_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id_snapshot": {
+ "name": "role_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user_id_snapshot": {
+ "name": "discord_user_id_snapshot",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempted_by": {
+ "name": "attempted_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "hacker_discord_role_attempt_outcome",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "error": {
+ "name": "error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_discord_role_grant_attempt_history_idx": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_history_idx",
+ "columns": [
+ {
+ "expression": "grant_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_discord_role_grant_attempt_grant_id_knight_hacks_hacker_discord_role_grant_id_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_grant_id_knight_hacks_hacker_discord_role_grant_id_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant_attempt",
+ "tableTo": "knight_hacks_hacker_discord_role_grant",
+ "columnsFrom": ["grant_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_attempted_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_attempted_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_discord_role_grant_attempt",
+ "tableTo": "auth_user",
+ "columnsFrom": ["attempted_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_discord_role_grant_attempt_attemptToken_unique": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_attemptToken_unique",
+ "nullsNotDistinct": false,
+ "columns": ["attempt_token"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_discord_role_grant_attempt_role_id_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_role_id_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant_attempt\".\"role_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_user_id_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_user_id_check",
+ "value": "\"knight_hacks_hacker_discord_role_grant_attempt\".\"discord_user_id_snapshot\" ~ '^[0-9]{17,20}$'"
+ },
+ "knight_hacks_hacker_discord_role_grant_attempt_completion_check": {
+ "name": "knight_hacks_hacker_discord_role_grant_attempt_completion_check",
+ "value": "(\"knight_hacks_hacker_discord_role_grant_attempt\".\"outcome\" = 'pending' AND \"knight_hacks_hacker_discord_role_grant_attempt\".\"finished_at\" IS NULL) OR (\"knight_hacks_hacker_discord_role_grant_attempt\".\"outcome\" <> 'pending' AND \"knight_hacks_hacker_discord_role_grant_attempt\".\"finished_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_event_attendee": {
+ "name": "knight_hacks_hacker_event_attendee",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hacker_att_id": {
+ "name": "hacker_att_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "checked_in_at": {
+ "name": "checked_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checked_in_by": {
+ "name": "checked_in_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "points_awarded": {
+ "name": "points_awarded",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_initial_attendance": {
+ "name": "is_initial_attendance",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "voided_at": {
+ "name": "voided_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "voided_by": {
+ "name": "voided_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "void_reason": {
+ "name": "void_reason",
+ "type": "varchar(300)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_event_attendee_event_attendee_history_idx": {
+ "name": "knight_hacks_hacker_event_attendee_event_attendee_history_idx",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "hacker_att_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_attendee_event_history_idx": {
+ "name": "knight_hacks_hacker_event_attendee_attendee_event_history_idx",
+ "columns": [
+ {
+ "expression": "hacker_att_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_hackathon_history_idx": {
+ "name": "knight_hacks_hacker_event_attendee_hackathon_history_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "checked_in_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_checked_in_by_idx": {
+ "name": "knight_hacks_hacker_event_attendee_checked_in_by_idx",
+ "columns": [
+ {
+ "expression": "checked_in_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_voided_by_idx": {
+ "name": "knight_hacks_hacker_event_attendee_voided_by_idx",
+ "columns": [
+ {
+ "expression": "voided_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_hacker_event_attendee_one_active_initial": {
+ "name": "knight_hacks_hacker_event_attendee_one_active_initial",
+ "columns": [
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "hacker_att_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_hacker_event_attendee\".\"is_initial_attendance\" = true AND \"knight_hacks_hacker_event_attendee\".\"voided_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_event_attendee_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hacker_event_attendee_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_checked_in_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_event_attendee_checked_in_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["checked_in_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_voided_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_event_attendee_voided_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "auth_user",
+ "columnsFrom": ["voided_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_scoped_attendee_fk": {
+ "name": "knight_hacks_hacker_event_attendee_scoped_attendee_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "knight_hacks_hacker_attendee",
+ "columnsFrom": ["hacker_att_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_event_attendee_scoped_event_fk": {
+ "name": "knight_hacks_hacker_event_attendee_scoped_event_fk",
+ "tableFrom": "knight_hacks_hacker_event_attendee",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_event_attendee_attempt_scope_unique": {
+ "name": "knight_hacks_hacker_event_attendee_attempt_scope_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id", "event_id", "hacker_att_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_event_attendee_points_awarded_check": {
+ "name": "knight_hacks_hacker_event_attendee_points_awarded_check",
+ "value": "\"knight_hacks_hacker_event_attendee\".\"points_awarded\" IS NULL OR \"knight_hacks_hacker_event_attendee\".\"points_awarded\" >= 0"
+ },
+ "knight_hacks_hacker_event_attendee_void_metadata_check": {
+ "name": "knight_hacks_hacker_event_attendee_void_metadata_check",
+ "value": "(\"knight_hacks_hacker_event_attendee\".\"voided_at\" IS NULL AND \"knight_hacks_hacker_event_attendee\".\"void_reason\" IS NULL) OR (\"knight_hacks_hacker_event_attendee\".\"voided_at\" IS NOT NULL AND \"knight_hacks_hacker_event_attendee\".\"void_reason\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_participant_command": {
+ "name": "knight_hacks_hacker_participant_command",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "operation": {
+ "name": "operation",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload_hash": {
+ "name": "payload_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "hacker_participant_command_state",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'started'"
+ },
+ "result": {
+ "name": "result",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "safe_error_code": {
+ "name": "safe_error_code",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_participant_command_expiry_idx": {
+ "name": "knight_hacks_hacker_participant_command_expiry_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_participant_command_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_participant_command_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_participant_command",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_participant_command_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_hacker_participant_command_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_hacker_participant_command",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_participant_command_identity_unique": {
+ "name": "knight_hacks_hacker_participant_command_identity_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "hackathon_id", "operation", "idempotency_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_participant_command_payload_hash_check": {
+ "name": "knight_hacks_hacker_participant_command_payload_hash_check",
+ "value": "\"knight_hacks_hacker_participant_command\".\"payload_hash\" ~ '^[0-9a-f]{64}$'"
+ },
+ "knight_hacks_hacker_participant_command_completion_check": {
+ "name": "knight_hacks_hacker_participant_command_completion_check",
+ "value": "(\"knight_hacks_hacker_participant_command\".\"state\" = 'started' AND \"knight_hacks_hacker_participant_command\".\"completed_at\" IS NULL) OR (\"knight_hacks_hacker_participant_command\".\"state\" <> 'started' AND \"knight_hacks_hacker_participant_command\".\"completed_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_profile": {
+ "name": "knight_hacks_hacker_profile",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "food_allergies": {
+ "name": "food_allergies",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_hacker_profile_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_profile_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_profile_user_unique": {
+ "name": "knight_hacks_hacker_profile_user_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_profile_revision_check": {
+ "name": "knight_hacks_hacker_profile_revision_check",
+ "value": "\"knight_hacks_hacker_profile\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_hacker_profile_revision": {
+ "name": "knight_hacks_hacker_profile_revision",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "legacy_hacker_id": {
+ "name": "legacy_hacker_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "food_allergies": {
+ "name": "food_allergies",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_hacker_profile_revision_profile_created_idx": {
+ "name": "knight_hacks_hacker_profile_revision_profile_created_idx",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_hacker_profile_revision_profile_id_knight_hacks_hacker_profile_id_fk": {
+ "name": "knight_hacks_hacker_profile_revision_profile_id_knight_hacks_hacker_profile_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile_revision",
+ "tableTo": "knight_hacks_hacker_profile",
+ "columnsFrom": ["profile_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_profile_revision_legacy_hacker_id_knight_hacks_hacker_id_fk": {
+ "name": "knight_hacks_hacker_profile_revision_legacy_hacker_id_knight_hacks_hacker_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile_revision",
+ "tableTo": "knight_hacks_hacker",
+ "columnsFrom": ["legacy_hacker_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_hacker_profile_revision_created_by_auth_user_id_fk": {
+ "name": "knight_hacks_hacker_profile_revision_created_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_hacker_profile_revision",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_hacker_profile_revision_legacyHackerId_unique": {
+ "name": "knight_hacks_hacker_profile_revision_legacyHackerId_unique",
+ "nullsNotDistinct": false,
+ "columns": ["legacy_hacker_id"]
+ },
+ "knight_hacks_hacker_profile_revision_profile_version_unique": {
+ "name": "knight_hacks_hacker_profile_revision_profile_version_unique",
+ "nullsNotDistinct": false,
+ "columns": ["profile_id", "revision"]
+ },
+ "knight_hacks_hacker_profile_revision_id_profile_unique": {
+ "name": "knight_hacks_hacker_profile_revision_id_profile_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "profile_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_hacker_profile_revision_version_check": {
+ "name": "knight_hacks_hacker_profile_revision_version_check",
+ "value": "\"knight_hacks_hacker_profile_revision\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue": {
+ "name": "knight_hacks_issue",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "status": {
+ "name": "status",
+ "type": "issue_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "links": {
+ "name": "links",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event": {
+ "name": "event",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discord_thread_id": {
+ "name": "discord_thread_id",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "date": {
+ "name": "date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "due_at": {
+ "name": "due_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "issue_priority",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team": {
+ "name": "team",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator": {
+ "name": "creator",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "parent": {
+ "name": "parent",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_by": {
+ "name": "archived_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archive_batch_id": {
+ "name": "archive_batch_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_key": {
+ "name": "creation_key",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creation_hash": {
+ "name": "creation_hash",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_team_idx": {
+ "name": "issue_team_idx",
+ "columns": [
+ {
+ "expression": "team",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_creator_idx": {
+ "name": "issue_creator_idx",
+ "columns": [
+ {
+ "expression": "creator",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_status_idx": {
+ "name": "issue_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_date_idx": {
+ "name": "issue_date_idx",
+ "columns": [
+ {
+ "expression": "date",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_due_at_idx": {
+ "name": "issue_due_at_idx",
+ "columns": [
+ {
+ "expression": "due_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_archive_idx": {
+ "name": "issue_archive_idx",
+ "columns": [
+ {
+ "expression": "archived_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "archive_batch_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_parent_idx": {
+ "name": "issue_parent_idx",
+ "columns": [
+ {
+ "expression": "parent",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "issue_priority_idx": {
+ "name": "issue_priority_idx",
+ "columns": [
+ {
+ "expression": "priority",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_event_knight_hacks_event_id_fk": {
+ "name": "knight_hacks_issue_event_knight_hacks_event_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "knight_hacks_event",
+ "columnsFrom": ["event"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_team_auth_roles_id_fk": {
+ "name": "knight_hacks_issue_team_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["team"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_creator_auth_user_id_fk": {
+ "name": "knight_hacks_issue_creator_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "auth_user",
+ "columnsFrom": ["creator"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_archived_by_auth_user_id_fk": {
+ "name": "knight_hacks_issue_archived_by_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "auth_user",
+ "columnsFrom": ["archived_by"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "issue_parent_fk": {
+ "name": "issue_parent_fk",
+ "tableFrom": "knight_hacks_issue",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["parent"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_issue_creation_key_unique": {
+ "name": "knight_hacks_issue_creation_key_unique",
+ "nullsNotDistinct": false,
+ "columns": ["creation_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_attachment": {
+ "name": "knight_hacks_issue_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "draft_key": {
+ "name": "draft_key",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "object_name": {
+ "name": "object_name",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_name": {
+ "name": "file_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_type": {
+ "name": "content_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size": {
+ "name": "size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "finalized_at": {
+ "name": "finalized_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_issue_attachment_issue_idx": {
+ "name": "knight_hacks_issue_attachment_issue_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_issue_attachment_draft_idx": {
+ "name": "knight_hacks_issue_attachment_draft_idx",
+ "columns": [
+ {
+ "expression": "draft_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_attachment_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_attachment_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_attachment_team_id_auth_roles_id_fk": {
+ "name": "knight_hacks_issue_attachment_team_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["team_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_attachment_owner_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_issue_attachment_owner_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment",
+ "tableTo": "auth_user",
+ "columnsFrom": ["owner_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_issue_attachment_objectName_unique": {
+ "name": "knight_hacks_issue_attachment_objectName_unique",
+ "nullsNotDistinct": false,
+ "columns": ["object_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_issue_attachment_owner_check": {
+ "name": "knight_hacks_issue_attachment_owner_check",
+ "value": "NOT (\"knight_hacks_issue_attachment\".\"issue_id\" IS NOT NULL AND \"knight_hacks_issue_attachment\".\"draft_key\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_attachment_reference": {
+ "name": "knight_hacks_issue_attachment_reference",
+ "schema": "",
+ "columns": {
+ "attachment_id": {
+ "name": "attachment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "issue_attachment_reference_issue_idx": {
+ "name": "issue_attachment_reference_issue_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_attachment_reference_attachment_id_knight_hacks_issue_attachment_id_fk": {
+ "name": "knight_hacks_issue_attachment_reference_attachment_id_knight_hacks_issue_attachment_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment_reference",
+ "tableTo": "knight_hacks_issue_attachment",
+ "columnsFrom": ["attachment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_attachment_reference_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_attachment_reference_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_attachment_reference",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_issue_attachment_reference_attachment_id_issue_id_pk": {
+ "name": "knight_hacks_issue_attachment_reference_attachment_id_issue_id_pk",
+ "columns": ["attachment_id", "issue_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_history": {
+ "name": "knight_hacks_issue_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_display_name": {
+ "name": "actor_display_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "changed_fields": {
+ "name": "changed_fields",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "before": {
+ "name": "before",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "after": {
+ "name": "after",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_history_issue_created_idx": {
+ "name": "issue_history_issue_created_idx",
+ "columns": [
+ {
+ "expression": "issue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_history_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_history_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_history",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issue_history_actor_id_auth_user_id_fk": {
+ "name": "knight_hacks_issue_history_actor_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issue_history",
+ "tableTo": "auth_user",
+ "columnsFrom": ["actor_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issue_reminder_delivery": {
+ "name": "knight_hacks_issue_reminder_delivery",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "due_at": {
+ "name": "due_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reminder_key": {
+ "name": "reminder_key",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "destination_snapshot": {
+ "name": "destination_snapshot",
+ "type": "varchar(32)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_snapshot": {
+ "name": "content_snapshot",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar(24)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "locked_at": {
+ "name": "locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "issue_reminder_delivery_pending_idx": {
+ "name": "issue_reminder_delivery_pending_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_issue_reminder_delivery_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issue_reminder_delivery_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issue_reminder_delivery",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "issue_reminder_delivery_identity_unique": {
+ "name": "issue_reminder_delivery_identity_unique",
+ "nullsNotDistinct": false,
+ "columns": ["issue_id", "due_at", "reminder_key"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issues_to_teams_visibility": {
+ "name": "knight_hacks_issues_to_teams_visibility",
+ "schema": "",
+ "columns": {
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_issues_to_teams_visibility_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issues_to_teams_visibility_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issues_to_teams_visibility",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issues_to_teams_visibility_team_id_auth_roles_id_fk": {
+ "name": "knight_hacks_issues_to_teams_visibility_team_id_auth_roles_id_fk",
+ "tableFrom": "knight_hacks_issues_to_teams_visibility",
+ "tableTo": "auth_roles",
+ "columnsFrom": ["team_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_issues_to_teams_visibility_issue_id_team_id_pk": {
+ "name": "knight_hacks_issues_to_teams_visibility_issue_id_team_id_pk",
+ "columns": ["issue_id", "team_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_issues_to_users_assignment": {
+ "name": "knight_hacks_issues_to_users_assignment",
+ "schema": "",
+ "columns": {
+ "issue_id": {
+ "name": "issue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_issues_to_users_assignment_issue_id_knight_hacks_issue_id_fk": {
+ "name": "knight_hacks_issues_to_users_assignment_issue_id_knight_hacks_issue_id_fk",
+ "tableFrom": "knight_hacks_issues_to_users_assignment",
+ "tableTo": "knight_hacks_issue",
+ "columnsFrom": ["issue_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_issues_to_users_assignment_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_issues_to_users_assignment_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_issues_to_users_assignment",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_issues_to_users_assignment_issue_id_user_id_pk": {
+ "name": "knight_hacks_issues_to_users_assignment_issue_id_user_id_pk",
+ "columns": ["issue_id", "user_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judge": {
+ "name": "knight_hacks_judge",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "judge_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_judge_member_unique": {
+ "name": "knight_hacks_judge_member_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judge\".\"user_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judge_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_judge_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_judge",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judge_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judge_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judge",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judge_id_hackathon_unique": {
+ "name": "knight_hacks_judge_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judge_kind_identity_check": {
+ "name": "knight_hacks_judge_kind_identity_check",
+ "value": "\"knight_hacks_judge\".\"kind\" = 'member' OR (\"knight_hacks_judge\".\"kind\" = 'guest' AND \"knight_hacks_judge\".\"user_id\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judge_deliberation_entry": {
+ "name": "knight_hacks_judge_deliberation_entry",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "section_id": {
+ "name": "section_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_judge_deliberation_entry_section_scope_fk": {
+ "name": "knight_hacks_judge_deliberation_entry_section_scope_fk",
+ "tableFrom": "knight_hacks_judge_deliberation_entry",
+ "tableTo": "knight_hacks_judge_deliberation_section",
+ "columnsFrom": ["section_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judge_deliberation_entry_project_scope_fk": {
+ "name": "knight_hacks_judge_deliberation_entry_project_scope_fk",
+ "tableFrom": "knight_hacks_judge_deliberation_entry",
+ "tableTo": "knight_hacks_project",
+ "columnsFrom": ["project_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judge_deliberation_entry_section_project_unique": {
+ "name": "knight_hacks_judge_deliberation_entry_section_project_unique",
+ "nullsNotDistinct": false,
+ "columns": ["section_id", "project_id"]
+ },
+ "knight_hacks_judge_deliberation_entry_section_order_unique": {
+ "name": "knight_hacks_judge_deliberation_entry_section_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["section_id", "display_order"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judge_deliberation_entry_order_check": {
+ "name": "knight_hacks_judge_deliberation_entry_order_check",
+ "value": "\"knight_hacks_judge_deliberation_entry\".\"display_order\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judge_deliberation_section": {
+ "name": "knight_hacks_judge_deliberation_section",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_judge_deliberation_section_judge_scope_fk": {
+ "name": "knight_hacks_judge_deliberation_section_judge_scope_fk",
+ "tableFrom": "knight_hacks_judge_deliberation_section",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judge_deliberation_section_judge_order_unique": {
+ "name": "knight_hacks_judge_deliberation_section_judge_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["judge_id", "display_order"]
+ },
+ "knight_hacks_judge_deliberation_section_id_hackathon_unique": {
+ "name": "knight_hacks_judge_deliberation_section_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judge_deliberation_section_order_check": {
+ "name": "knight_hacks_judge_deliberation_section_order_check",
+ "value": "\"knight_hacks_judge_deliberation_section\".\"display_order\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_announcement": {
+ "name": "knight_hacks_judging_announcement",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "room_id": {
+ "name": "room_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message": {
+ "name": "message",
+ "type": "varchar(1000)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "include_guests": {
+ "name": "include_guests",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_urgent": {
+ "name": "is_urgent",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "published_by_user_id": {
+ "name": "published_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "cleared_at": {
+ "name": "cleared_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cleared_by_user_id": {
+ "name": "cleared_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_judging_announcement_current_global_unique": {
+ "name": "knight_hacks_judging_announcement_current_global_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_announcement\".\"room_id\" IS NULL AND \"knight_hacks_judging_announcement\".\"cleared_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_judging_announcement_current_room_unique": {
+ "name": "knight_hacks_judging_announcement_current_room_unique",
+ "columns": [
+ {
+ "expression": "room_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_announcement\".\"room_id\" IS NOT NULL AND \"knight_hacks_judging_announcement\".\"cleared_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_judging_announcement_current_lookup_idx": {
+ "name": "knight_hacks_judging_announcement_current_lookup_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "room_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "cleared_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judging_announcement_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_judging_announcement_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_judging_announcement",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_announcement_published_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_announcement_published_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_announcement",
+ "tableTo": "auth_user",
+ "columnsFrom": ["published_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_announcement_cleared_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_announcement_cleared_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_announcement",
+ "tableTo": "auth_user",
+ "columnsFrom": ["cleared_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_announcement_room_scope_fk": {
+ "name": "knight_hacks_judging_announcement_room_scope_fk",
+ "tableFrom": "knight_hacks_judging_announcement",
+ "tableTo": "knight_hacks_judging_room",
+ "columnsFrom": ["room_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judging_announcement_message_not_blank_check": {
+ "name": "knight_hacks_judging_announcement_message_not_blank_check",
+ "value": "\"knight_hacks_judging_announcement\".\"message\" ~ '[^[:space:]]'"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_room": {
+ "name": "knight_hacks_judging_room",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "challenge_id": {
+ "name": "challenge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "discord_thread_id": {
+ "name": "discord_thread_id",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_at": {
+ "name": "archived_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "archived_by_user_id": {
+ "name": "archived_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_judging_room_active_name_unique": {
+ "name": "knight_hacks_judging_room_active_name_unique",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_room\".\"archived_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_judging_room_hackathon_idx": {
+ "name": "knight_hacks_judging_room_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "display_order",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judging_room_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_judging_room_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_judging_room",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_archived_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_room_archived_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_room",
+ "tableTo": "auth_user",
+ "columnsFrom": ["archived_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_challenge_scope_fk": {
+ "name": "knight_hacks_judging_room_challenge_scope_fk",
+ "tableFrom": "knight_hacks_judging_room",
+ "tableTo": "knight_hacks_project_challenge",
+ "columnsFrom": ["challenge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judging_room_id_hackathon_unique": {
+ "name": "knight_hacks_judging_room_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_room_access_link": {
+ "name": "knight_hacks_judging_room_access_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "room_id": {
+ "name": "room_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_by_user_id": {
+ "name": "revoked_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revocation_reason": {
+ "name": "revocation_reason",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_judging_room_access_link_active_room_unique": {
+ "name": "knight_hacks_judging_room_access_link_active_room_unique",
+ "columns": [
+ {
+ "expression": "room_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_room_access_link\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judging_room_access_link_created_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_room_access_link_created_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_room_access_link",
+ "tableTo": "auth_user",
+ "columnsFrom": ["created_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_access_link_revoked_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_judging_room_access_link_revoked_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_judging_room_access_link",
+ "tableTo": "auth_user",
+ "columnsFrom": ["revoked_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_access_link_room_scope_fk": {
+ "name": "knight_hacks_judging_room_access_link_room_scope_fk",
+ "tableFrom": "knight_hacks_judging_room_access_link",
+ "tableTo": "knight_hacks_judging_room",
+ "columnsFrom": ["room_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judging_room_access_link_id_hackathon_unique": {
+ "name": "knight_hacks_judging_room_access_link_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_room_presence": {
+ "name": "knight_hacks_judging_room_presence",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "room_id": {
+ "name": "room_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "joined_at": {
+ "name": "joined_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_seen_at": {
+ "name": "last_seen_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "left_at": {
+ "name": "left_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "leave_reason": {
+ "name": "leave_reason",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "knight_hacks_judging_room_presence_active_judge_unique": {
+ "name": "knight_hacks_judging_room_presence_active_judge_unique",
+ "columns": [
+ {
+ "expression": "judge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"knight_hacks_judging_room_presence\".\"left_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_judging_room_presence_room_idx": {
+ "name": "knight_hacks_judging_room_presence_room_idx",
+ "columns": [
+ {
+ "expression": "room_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "left_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_judging_room_presence_room_scope_fk": {
+ "name": "knight_hacks_judging_room_presence_room_scope_fk",
+ "tableFrom": "knight_hacks_judging_room_presence",
+ "tableTo": "knight_hacks_judging_room",
+ "columnsFrom": ["room_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_judging_room_presence_judge_scope_fk": {
+ "name": "knight_hacks_judging_room_presence_judge_scope_fk",
+ "tableFrom": "knight_hacks_judging_room_presence",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_judging_rubric_item": {
+ "name": "knight_hacks_judging_rubric_item",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "judging_rubric_item_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(120)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "member_visibility_policy": {
+ "name": "member_visibility_policy",
+ "type": "judging_response_visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guest_visibility_policy": {
+ "name": "guest_visibility_policy",
+ "type": "judging_response_visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_judging_rubric_item_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_judging_rubric_item_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_judging_rubric_item",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_judging_rubric_item_hackathon_order_unique": {
+ "name": "knight_hacks_judging_rubric_item_hackathon_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "display_order"]
+ },
+ "knight_hacks_judging_rubric_item_id_hackathon_unique": {
+ "name": "knight_hacks_judging_rubric_item_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_judging_rubric_item_display_order_check": {
+ "name": "knight_hacks_judging_rubric_item_display_order_check",
+ "value": "\"knight_hacks_judging_rubric_item\".\"display_order\" >= 0"
+ },
+ "knight_hacks_judging_rubric_item_visibility_check": {
+ "name": "knight_hacks_judging_rubric_item_visibility_check",
+ "value": "(\"knight_hacks_judging_rubric_item\".\"kind\" = 'rating' AND \"knight_hacks_judging_rubric_item\".\"required\" = true AND \"knight_hacks_judging_rubric_item\".\"member_visibility_policy\" IS NULL AND \"knight_hacks_judging_rubric_item\".\"guest_visibility_policy\" IS NULL) OR (\"knight_hacks_judging_rubric_item\".\"kind\" = 'short_response' AND \"knight_hacks_judging_rubric_item\".\"member_visibility_policy\" IS NOT NULL AND \"knight_hacks_judging_rubric_item\".\"guest_visibility_policy\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_member": {
+ "name": "knight_hacks_member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discord_user": {
+ "name": "discord_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "age": {
+ "name": "age",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone_number": {
+ "name": "phone_number",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "school": {
+ "name": "school",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level_of_study": {
+ "name": "level_of_study",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "major": {
+ "name": "major",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Computer Science'"
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "type": "race_or_ethnicity",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Prefer not to answer'"
+ },
+ "guild_profile_visible": {
+ "name": "guild_profile_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "guild_resume_visible": {
+ "name": "guild_resume_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "guild_opportunity_statuses": {
+ "name": "guild_opportunity_statuses",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "tagline": {
+ "name": "tagline",
+ "type": "varchar(80)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "about": {
+ "name": "about",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_picture_url": {
+ "name": "profile_picture_url",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "shirt_size": {
+ "name": "shirt_size",
+ "type": "shirt_size",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_profile_url": {
+ "name": "linkedin_profile_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resume_url": {
+ "name": "resume_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dob": {
+ "name": "dob",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grad_date": {
+ "name": "grad_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "alumni_confirmed_at": {
+ "name": "alumni_confirmed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "company": {
+ "name": "company",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_city_key": {
+ "name": "current_city_key",
+ "type": "varchar(8)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "guild_location_visible": {
+ "name": "guild_location_visible",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "points": {
+ "name": "points",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "date_created": {
+ "name": "date_created",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "time_created": {
+ "name": "time_created",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_member_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_member_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_member",
+ "tableTo": "auth_user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_member_email_unique": {
+ "name": "knight_hacks_member_email_unique",
+ "nullsNotDistinct": false,
+ "columns": ["email"]
+ },
+ "knight_hacks_member_phoneNumber_unique": {
+ "name": "knight_hacks_member_phoneNumber_unique",
+ "nullsNotDistinct": false,
+ "columns": ["phone_number"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_member_valid_guild_opportunity_statuses": {
+ "name": "knight_hacks_member_valid_guild_opportunity_statuses",
+ "value": "\"knight_hacks_member\".\"guild_opportunity_statuses\" <@ ARRAY['internships', 'full-time', 'freelance-contract', 'project-collaboration', 'offering-mentorship', 'seeking-mentorship']::text[] AND cardinality(\"knight_hacks_member\".\"guild_opportunity_statuses\") <= 3"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_companies": {
+ "name": "knight_hacks_companies",
+ "schema": "",
+ "columns": {
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": true,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project": {
+ "name": "knight_hacks_project",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "submission_url": {
+ "name": "submission_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "demo_links": {
+ "name": "demo_links",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "video_url": {
+ "name": "video_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "technologies": {
+ "name": "technologies",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "universities": {
+ "name": "universities",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "prize_categories": {
+ "name": "prize_categories",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "ARRAY[]::text[]"
+ },
+ "participant_count": {
+ "name": "participant_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_created_at": {
+ "name": "project_created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "submitted_at": {
+ "name": "submitted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_by_user_id": {
+ "name": "deleted_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_hackathon_idx": {
+ "name": "knight_hacks_project_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_project_hackathon_deleted_idx": {
+ "name": "knight_hacks_project_hackathon_deleted_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deleted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_project_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_project",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_deleted_by_user_id_auth_user_id_fk": {
+ "name": "knight_hacks_project_deleted_by_user_id_auth_user_id_fk",
+ "tableFrom": "knight_hacks_project",
+ "tableTo": "auth_user",
+ "columnsFrom": ["deleted_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_hackathon_submission_url_unique": {
+ "name": "knight_hacks_project_hackathon_submission_url_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "submission_url"]
+ },
+ "knight_hacks_project_id_hackathon_unique": {
+ "name": "knight_hacks_project_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_participant_count_check": {
+ "name": "knight_hacks_project_participant_count_check",
+ "value": "\"knight_hacks_project\".\"participant_count\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_challenge": {
+ "name": "knight_hacks_project_challenge",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_challenge_hackathon_idx": {
+ "name": "knight_hacks_project_challenge_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_challenge_hackathon_id_knight_hacks_hackathon_id_fk": {
+ "name": "knight_hacks_project_challenge_hackathon_id_knight_hacks_hackathon_id_fk",
+ "tableFrom": "knight_hacks_project_challenge",
+ "tableTo": "knight_hacks_hackathon",
+ "columnsFrom": ["hackathon_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_challenge_hackathon_label_unique": {
+ "name": "knight_hacks_project_challenge_hackathon_label_unique",
+ "nullsNotDistinct": false,
+ "columns": ["hackathon_id", "label"]
+ },
+ "knight_hacks_project_challenge_id_hackathon_unique": {
+ "name": "knight_hacks_project_challenge_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation": {
+ "name": "knight_hacks_project_evaluation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "challenge_id": {
+ "name": "challenge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "judge_id": {
+ "name": "judge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_evaluation_project_challenge_idx": {
+ "name": "knight_hacks_project_evaluation_project_challenge_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "challenge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_project_evaluation_judge_idx": {
+ "name": "knight_hacks_project_evaluation_judge_idx",
+ "columns": [
+ {
+ "expression": "judge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_project_challenge_scope_fk": {
+ "name": "knight_hacks_project_evaluation_project_challenge_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation",
+ "tableTo": "knight_hacks_project_to_challenge",
+ "columnsFrom": ["project_id", "challenge_id", "hackathon_id"],
+ "columnsTo": ["project_id", "challenge_id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_evaluation_judge_scope_fk": {
+ "name": "knight_hacks_project_evaluation_judge_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation",
+ "tableTo": "knight_hacks_judge",
+ "columnsFrom": ["judge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_evaluation_judge_project_challenge_unique": {
+ "name": "knight_hacks_project_evaluation_judge_project_challenge_unique",
+ "nullsNotDistinct": false,
+ "columns": ["judge_id", "project_id", "challenge_id"]
+ },
+ "knight_hacks_project_evaluation_id_hackathon_unique": {
+ "name": "knight_hacks_project_evaluation_id_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation_rating": {
+ "name": "knight_hacks_project_evaluation_rating",
+ "schema": "",
+ "columns": {
+ "evaluation_id": {
+ "name": "evaluation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rubric_item_id": {
+ "name": "rubric_item_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_rating_evaluation_scope_fk": {
+ "name": "knight_hacks_project_evaluation_rating_evaluation_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_rating",
+ "tableTo": "knight_hacks_project_evaluation",
+ "columnsFrom": ["evaluation_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_evaluation_rating_rubric_scope_fk": {
+ "name": "knight_hacks_project_evaluation_rating_rubric_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_rating",
+ "tableTo": "knight_hacks_judging_rubric_item",
+ "columnsFrom": ["rubric_item_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_project_evaluation_rating_evaluation_id_rubric_item_id_pk": {
+ "name": "knight_hacks_project_evaluation_rating_evaluation_id_rubric_item_id_pk",
+ "columns": ["evaluation_id", "rubric_item_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_evaluation_rating_value_check": {
+ "name": "knight_hacks_project_evaluation_rating_value_check",
+ "value": "\"knight_hacks_project_evaluation_rating\".\"value\" BETWEEN 1 AND 5"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation_response": {
+ "name": "knight_hacks_project_evaluation_response",
+ "schema": "",
+ "columns": {
+ "evaluation_id": {
+ "name": "evaluation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rubric_item_id": {
+ "name": "rubric_item_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_response_evaluation_scope_fk": {
+ "name": "knight_hacks_project_evaluation_response_evaluation_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_response",
+ "tableTo": "knight_hacks_project_evaluation",
+ "columnsFrom": ["evaluation_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_evaluation_response_rubric_scope_fk": {
+ "name": "knight_hacks_project_evaluation_response_rubric_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_response",
+ "tableTo": "knight_hacks_judging_rubric_item",
+ "columnsFrom": ["rubric_item_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_project_evaluation_response_evaluation_id_rubric_item_id_pk": {
+ "name": "knight_hacks_project_evaluation_response_evaluation_id_rubric_item_id_pk",
+ "columns": ["evaluation_id", "rubric_item_id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_evaluation_revision": {
+ "name": "knight_hacks_project_evaluation_revision",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "evaluation_id": {
+ "name": "evaluation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_kind": {
+ "name": "actor_kind",
+ "type": "judge_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rating_answers": {
+ "name": "rating_answers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "response_answers": {
+ "name": "response_answers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_project_evaluation_revision_evaluation_scope_fk": {
+ "name": "knight_hacks_project_evaluation_revision_evaluation_scope_fk",
+ "tableFrom": "knight_hacks_project_evaluation_revision",
+ "tableTo": "knight_hacks_project_evaluation",
+ "columnsFrom": ["evaluation_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_evaluation_revision_unique": {
+ "name": "knight_hacks_project_evaluation_revision_unique",
+ "nullsNotDistinct": false,
+ "columns": ["evaluation_id", "revision"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_evaluation_revision_revision_check": {
+ "name": "knight_hacks_project_evaluation_revision_revision_check",
+ "value": "\"knight_hacks_project_evaluation_revision\".\"revision\" >= 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_member": {
+ "name": "knight_hacks_project_member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(320)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_member_project_idx": {
+ "name": "knight_hacks_project_member_project_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_member_project_id_knight_hacks_project_id_fk": {
+ "name": "knight_hacks_project_member_project_id_knight_hacks_project_id_fk",
+ "tableFrom": "knight_hacks_project_member",
+ "tableTo": "knight_hacks_project",
+ "columnsFrom": ["project_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_project_member_project_order_unique": {
+ "name": "knight_hacks_project_member_project_order_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "display_order"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "knight_hacks_project_member_display_order_check": {
+ "name": "knight_hacks_project_member_display_order_check",
+ "value": "\"knight_hacks_project_member\".\"display_order\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_project_to_challenge": {
+ "name": "knight_hacks_project_to_challenge",
+ "schema": "",
+ "columns": {
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "challenge_id": {
+ "name": "challenge_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hackathon_id": {
+ "name": "hackathon_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "knight_hacks_project_to_challenge_challenge_idx": {
+ "name": "knight_hacks_project_to_challenge_challenge_idx",
+ "columns": [
+ {
+ "expression": "challenge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "knight_hacks_project_to_challenge_hackathon_idx": {
+ "name": "knight_hacks_project_to_challenge_hackathon_idx",
+ "columns": [
+ {
+ "expression": "hackathon_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "knight_hacks_project_to_challenge_project_scope_fk": {
+ "name": "knight_hacks_project_to_challenge_project_scope_fk",
+ "tableFrom": "knight_hacks_project_to_challenge",
+ "tableTo": "knight_hacks_project",
+ "columnsFrom": ["project_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "knight_hacks_project_to_challenge_challenge_scope_fk": {
+ "name": "knight_hacks_project_to_challenge_challenge_scope_fk",
+ "tableFrom": "knight_hacks_project_to_challenge",
+ "tableTo": "knight_hacks_project_challenge",
+ "columnsFrom": ["challenge_id", "hackathon_id"],
+ "columnsTo": ["id", "hackathon_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "knight_hacks_project_to_challenge_project_id_challenge_id_pk": {
+ "name": "knight_hacks_project_to_challenge_project_id_challenge_id_pk",
+ "columns": ["project_id", "challenge_id"]
+ }
+ },
+ "uniqueConstraints": {
+ "knight_hacks_project_to_challenge_project_challenge_hackathon_unique": {
+ "name": "knight_hacks_project_to_challenge_project_challenge_hackathon_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "challenge_id", "hackathon_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_sponsor": {
+ "name": "knight_hacks_sponsor",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo_url": {
+ "name": "logo_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "website_url": {
+ "name": "website_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_template": {
+ "name": "knight_hacks_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_name": {
+ "name": "normalized_name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "body": {
+ "name": "body",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "disabled_at": {
+ "name": "disabled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "disabled_reason": {
+ "name": "disabled_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "knight_hacks_template_normalized_name_unique": {
+ "name": "knight_hacks_template_normalized_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["normalized_name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.knight_hacks_trpc_form_connection": {
+ "name": "knight_hacks_trpc_form_connection",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "form": {
+ "name": "form",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proc": {
+ "name": "proc",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connections": {
+ "name": "connections",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "knight_hacks_trpc_form_connection_form_knight_hacks_form_schemas_id_fk": {
+ "name": "knight_hacks_trpc_form_connection_form_knight_hacks_form_schemas_id_fk",
+ "tableFrom": "knight_hacks_trpc_form_connection",
+ "tableTo": "knight_hacks_form_schemas",
+ "columnsFrom": ["form"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.alumni_bulletin_state": {
+ "name": "alumni_bulletin_state",
+ "schema": "public",
+ "values": ["draft", "published", "archived"]
+ },
+ "public.company_review_state": {
+ "name": "company_review_state",
+ "schema": "public",
+ "values": ["pending", "approved", "rejected", "merged"]
+ },
+ "public.employment_experience_type": {
+ "name": "employment_experience_type",
+ "schema": "public",
+ "values": [
+ "internship",
+ "full_time",
+ "part_time",
+ "co_op",
+ "contract",
+ "fellowship",
+ "self_employed",
+ "other"
+ ]
+ },
+ "public.employment_state": {
+ "name": "employment_state",
+ "schema": "public",
+ "values": ["current", "past", "unknown"]
+ },
+ "public.event_discord_entity_type": {
+ "name": "event_discord_entity_type",
+ "schema": "public",
+ "values": ["external", "voice", "stage"]
+ },
+ "public.event_google_destination": {
+ "name": "event_google_destination",
+ "schema": "public",
+ "values": ["public", "internal"]
+ },
+ "public.event_publication_work_state": {
+ "name": "event_publication_work_state",
+ "schema": "public",
+ "values": ["pending", "processing", "succeeded", "failed", "blocked"]
+ },
+ "public.event_purpose": {
+ "name": "event_purpose",
+ "schema": "public",
+ "values": ["event", "primary_check_in"]
+ },
+ "public.event_sync_state": {
+ "name": "event_sync_state",
+ "schema": "public",
+ "values": ["disabled", "pending", "synced", "error", "unknown"]
+ },
+ "public.form_callback_status": {
+ "name": "form_callback_status",
+ "schema": "public",
+ "values": ["pending", "running", "succeeded", "failed", "cancelled"]
+ },
+ "public.form_kind": {
+ "name": "form_kind",
+ "schema": "public",
+ "values": ["general", "event_feedback", "system"]
+ },
+ "public.form_response_mode": {
+ "name": "form_response_mode",
+ "schema": "public",
+ "values": ["single_locked", "single_editable", "multiple_locked"]
+ },
+ "public.form_state": {
+ "name": "form_state",
+ "schema": "public",
+ "values": ["draft", "published", "archived"]
+ },
+ "public.gender": {
+ "name": "gender",
+ "schema": "public",
+ "values": [
+ "Man",
+ "Woman",
+ "Non-binary",
+ "Prefer to self-describe",
+ "Prefer not to answer"
+ ]
+ },
+ "public.hackathon_application_state": {
+ "name": "hackathon_application_state",
+ "schema": "public",
+ "values": [
+ "withdrawn",
+ "pending",
+ "accepted",
+ "waitlisted",
+ "checkedin",
+ "confirmed",
+ "denied"
+ ]
+ },
+ "public.hackathon_event_publication_provider": {
+ "name": "hackathon_event_publication_provider",
+ "schema": "public",
+ "values": ["discord", "google"]
+ },
+ "public.hackathon_event_reminder_state": {
+ "name": "hackathon_event_reminder_state",
+ "schema": "public",
+ "values": ["pending", "delivering", "delivered", "failed", "unknown"]
+ },
+ "public.hacker_agreement_provenance": {
+ "name": "hacker_agreement_provenance",
+ "schema": "public",
+ "values": ["explicit", "legacy_unversioned"]
+ },
+ "public.hacker_agreement_stage": {
+ "name": "hacker_agreement_stage",
+ "schema": "public",
+ "values": ["application", "confirmation"]
+ },
+ "public.hacker_check_in_outcome": {
+ "name": "hacker_check_in_outcome",
+ "schema": "public",
+ "values": [
+ "checked_in",
+ "already_checked_in",
+ "invalid_qr",
+ "hacker_not_found",
+ "wrong_status",
+ "not_checked_in",
+ "wrong_class",
+ "not_ready"
+ ]
+ },
+ "public.hacker_discord_role_attempt_outcome": {
+ "name": "hacker_discord_role_attempt_outcome",
+ "schema": "public",
+ "values": ["pending", "succeeded", "failed", "unknown"]
+ },
+ "public.hacker_discord_role_grant_state": {
+ "name": "hacker_discord_role_grant_state",
+ "schema": "public",
+ "values": ["pending", "succeeded", "failed", "unknown"]
+ },
+ "public.hacker_discord_role_kind": {
+ "name": "hacker_discord_role_kind",
+ "schema": "public",
+ "values": ["general", "class", "vip"]
+ },
+ "public.hacker_participant_command_state": {
+ "name": "hacker_participant_command_state",
+ "schema": "public",
+ "values": ["started", "completed"]
+ },
+ "public.issue_priority": {
+ "name": "issue_priority",
+ "schema": "public",
+ "values": ["Lowest", "Low", "Medium", "High", "Highest"]
+ },
+ "public.issue_status": {
+ "name": "issue_status",
+ "schema": "public",
+ "values": ["Backlog", "Planning", "In Progress", "Finished"]
+ },
+ "public.judge_kind": {
+ "name": "judge_kind",
+ "schema": "public",
+ "values": ["member", "guest"]
+ },
+ "public.judging_response_visibility": {
+ "name": "judging_response_visibility",
+ "schema": "public",
+ "values": ["public", "public_optional", "private"]
+ },
+ "public.judging_rubric_item_kind": {
+ "name": "judging_rubric_item_kind",
+ "schema": "public",
+ "values": ["rating", "short_response"]
+ },
+ "public.judging_state": {
+ "name": "judging_state",
+ "schema": "public",
+ "values": ["draft", "open", "closed"]
+ },
+ "public.race_or_ethnicity": {
+ "name": "race_or_ethnicity",
+ "schema": "public",
+ "values": [
+ "White",
+ "Black or African American",
+ "Hispanic / Latino / Spanish Origin",
+ "Asian",
+ "Native Hawaiian or Other Pacific Islander",
+ "Native American or Alaskan Native",
+ "Middle Eastern",
+ "Prefer not to answer",
+ "Other"
+ ]
+ },
+ "public.shirt_size": {
+ "name": "shirt_size",
+ "schema": "public",
+ "values": ["XS", "S", "M", "L", "XL", "2XL", "3XL"]
+ },
+ "public.sponsor_tier": {
+ "name": "sponsor_tier",
+ "schema": "public",
+ "values": ["gold", "silver", "bronze", "other"]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json
index 1a138a733..f57afb4f2 100644
--- a/packages/db/drizzle/meta/_journal.json
+++ b/packages/db/drizzle/meta/_journal.json
@@ -337,6 +337,20 @@
"when": 1788650755175,
"tag": "0047_fluffy_alex_power",
"breakpoints": true
+ },
+ {
+ "idx": 48,
+ "version": "7",
+ "when": 1788662145431,
+ "tag": "0048_little_tarot",
+ "breakpoints": true
+ },
+ {
+ "idx": 49,
+ "version": "7",
+ "when": 1788665684207,
+ "tag": "0049_real_redwing",
+ "breakpoints": true
}
]
}
diff --git a/packages/db/scripts/dev-db-backup-sanitizer.ts b/packages/db/scripts/dev-db-backup-sanitizer.ts
index cf6a78c33..637101a3e 100644
--- a/packages/db/scripts/dev-db-backup-sanitizer.ts
+++ b/packages/db/scripts/dev-db-backup-sanitizer.ts
@@ -126,6 +126,7 @@ export const TABLES_TO_DROP = [
"knight_hacks_hackathon_judging_configuration",
"knight_hacks_guest_judge_session",
"knight_hacks_judge",
+ "knight_hacks_judging_announcement",
"knight_hacks_judging_room_access_link",
"knight_hacks_judging_room_presence",
"knight_hacks_judging_room",
diff --git a/packages/db/src/schemas/knight-hacks.ts b/packages/db/src/schemas/knight-hacks.ts
index 43554af99..58b9f47f0 100644
--- a/packages/db/src/schemas/knight-hacks.ts
+++ b/packages/db/src/schemas/knight-hacks.ts
@@ -2259,6 +2259,7 @@ export const HackathonJudgingConfiguration = createTable(
.references(() => User.id, { onDelete: "set null" }),
state: judgingStateEnum().notNull().default("draft"),
displayAllResultsToMembers: t.boolean().notNull().default(false),
+ judgingCommsChannelId: t.varchar({ length: 20 }),
openedAt: t.timestamp({ withTimezone: true }),
closedAt: t.timestamp({ withTimezone: true }),
createdAt: t.timestamp({ withTimezone: true }).notNull().defaultNow(),
@@ -2281,6 +2282,7 @@ export const JudgingRoom = createTable(
challengeId: t.uuid().notNull(),
name: t.varchar({ length: 120 }).notNull(),
displayOrder: t.integer().notNull().default(0),
+ discordThreadId: t.varchar({ length: 20 }),
archivedAt: t.timestamp({ withTimezone: true }),
archivedByUserId: t
.uuid()
@@ -2313,6 +2315,57 @@ export const JudgingRoom = createTable(
}),
);
+export const JudgingAnnouncement = createTable(
+ "judging_announcement",
+ (t) => ({
+ id: t.uuid().notNull().primaryKey().defaultRandom(),
+ hackathonId: t
+ .uuid()
+ .notNull()
+ .references(() => Hackathon.id, { onDelete: "cascade" }),
+ roomId: t.uuid(),
+ message: t.varchar({ length: 1000 }).notNull(),
+ includeGuests: t.boolean().notNull().default(false),
+ isUrgent: t.boolean().notNull().default(false),
+ publishedByUserId: t
+ .uuid()
+ .notNull()
+ .references(() => User.id, { onDelete: "restrict" }),
+ publishedAt: t.timestamp({ withTimezone: true }).notNull().defaultNow(),
+ clearedAt: t.timestamp({ withTimezone: true }),
+ clearedByUserId: t
+ .uuid()
+ .references(() => User.id, { onDelete: "set null" }),
+ }),
+ (table) => ({
+ roomScopeFk: foreignKey({
+ columns: [table.roomId, table.hackathonId],
+ foreignColumns: [JudgingRoom.id, JudgingRoom.hackathonId],
+ name: "knight_hacks_judging_announcement_room_scope_fk",
+ }).onDelete("cascade"),
+ oneCurrentGlobal: uniqueIndex(
+ "knight_hacks_judging_announcement_current_global_unique",
+ )
+ .on(table.hackathonId)
+ .where(sql`${table.roomId} IS NULL AND ${table.clearedAt} IS NULL`),
+ oneCurrentPerRoom: uniqueIndex(
+ "knight_hacks_judging_announcement_current_room_unique",
+ )
+ .on(table.roomId)
+ .where(sql`${table.roomId} IS NOT NULL AND ${table.clearedAt} IS NULL`),
+ currentLookup: index(
+ "knight_hacks_judging_announcement_current_lookup_idx",
+ ).on(table.hackathonId, table.roomId, table.clearedAt),
+ messageNotBlank: check(
+ "knight_hacks_judging_announcement_message_not_blank_check",
+ sql`${table.message} ~ '[^[:space:]]'`,
+ ),
+ }),
+);
+
+export type InsertJudgingAnnouncement = typeof JudgingAnnouncement.$inferInsert;
+export type SelectJudgingAnnouncement = typeof JudgingAnnouncement.$inferSelect;
+
export const Judge = createTable(
"judge",
(t) => ({
diff --git a/packages/db/src/tests/dev-db-backup-sanitizer.test.ts b/packages/db/src/tests/dev-db-backup-sanitizer.test.ts
index 7f7b2656e..91b606d3f 100644
--- a/packages/db/src/tests/dev-db-backup-sanitizer.test.ts
+++ b/packages/db/src/tests/dev-db-backup-sanitizer.test.ts
@@ -148,6 +148,7 @@ describe("development database backup sanitizer", () => {
"knight_hacks_guest_judge_session",
"knight_hacks_hackathon_judging_configuration",
"knight_hacks_judge",
+ "knight_hacks_judging_announcement",
"knight_hacks_judging_room",
"knight_hacks_judging_room_access_link",
"knight_hacks_judging_room_presence",
diff --git a/packages/db/src/tests/judging-schema.test.ts b/packages/db/src/tests/judging-schema.test.ts
index 386da4851..48843b1f4 100644
--- a/packages/db/src/tests/judging-schema.test.ts
+++ b/packages/db/src/tests/judging-schema.test.ts
@@ -7,6 +7,7 @@ import {
Judge,
JudgeDeliberationEntry,
JudgeDeliberationSection,
+ JudgingAnnouncement,
JudgingRoom,
JudgingRoomAccessLink,
JudgingRoomPresence,
@@ -164,4 +165,17 @@ describe("judging room storage", () => {
'"knight_hacks_judging_rubric_item"."required" = true',
);
});
+
+ it("rejects announcements made only of whitespace", () => {
+ const messageCheck = getTableConfig(JudgingAnnouncement).checks.find(
+ (constraint) =>
+ constraint.name ===
+ "knight_hacks_judging_announcement_message_not_blank_check",
+ );
+ expect(messageCheck).toBeDefined();
+ if (!messageCheck) throw new Error("Missing announcement message check.");
+ expect(new PgDialect().sqlToQuery(messageCheck.value).sql).toContain(
+ "~ '[^[:space:]]'",
+ );
+ });
});
diff --git a/packages/db/src/tests/migration-lineage.test.ts b/packages/db/src/tests/migration-lineage.test.ts
index f2917c9ef..5014d3c68 100644
--- a/packages/db/src/tests/migration-lineage.test.ts
+++ b/packages/db/src/tests/migration-lineage.test.ts
@@ -106,15 +106,15 @@ describe("canonical production migration lineage", () => {
await readFile(new URL("_journal.json", metadataDirectory), "utf8"),
) as { entries: JournalEntry[] };
- expect(journal.entries).toHaveLength(48);
+ expect(journal.entries).toHaveLength(50);
expect(journal.entries[10]).toMatchObject({
idx: 10,
tag: "0010_wooden_supreme_intelligence",
when: PRODUCTION_MAIN_MIGRATION.createdAt,
});
expect(journal.entries.at(-1)).toMatchObject({
- idx: 47,
- tag: "0047_fluffy_alex_power",
+ idx: 49,
+ tag: "0049_real_redwing",
});
for (const [position, entry] of journal.entries.entries()) {
diff --git a/packages/ui/src/dialog.tsx b/packages/ui/src/dialog.tsx
index 1035954b2..e653d21c1 100644
--- a/packages/ui/src/dialog.tsx
+++ b/packages/ui/src/dialog.tsx
@@ -32,29 +32,38 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef & {
+ portalled?: boolean;
showCloseButton?: boolean;
}
->(({ className, children, showCloseButton = true, ...props }, ref) => (
-
-
-
- {children}
- {showCloseButton ? (
-
-
- Close
-
- ) : null}
-
-
-));
+>(
+ (
+ { className, children, portalled = true, showCloseButton = true, ...props },
+ ref,
+ ) => {
+ const content = (
+ <>
+
+
+ {children}
+ {showCloseButton ? (
+
+
+ Close
+
+ ) : null}
+
+ >
+ );
+ return portalled ? {content} : content;
+ },
+);
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
diff --git a/packages/validators/src/audit.ts b/packages/validators/src/audit.ts
index 483ed78a7..99984fd99 100644
--- a/packages/validators/src/audit.ts
+++ b/packages/validators/src/audit.ts
@@ -492,6 +492,29 @@ export const AUDIT_ACTION_CATALOG = {
"guestAccessRevoked",
]),
"judging.room.archived": policy("hackathons", "Archived judging room"),
+ "judging.comms.updated": policy(
+ "hackathons",
+ "Updated judging communications",
+ ["channelId"],
+ ),
+ "judging.comms.threads_provisioned": policy(
+ "hackathons",
+ "Provisioned judging room threads",
+ ["failedRoomCount", "provisionedCount"],
+ ),
+ "judging.announcement.published": policy(
+ "hackathons",
+ "Published judging announcement",
+ ["includeGuests", "isUrgent", "scope"],
+ ),
+ "judging.announcement.cleared": policy(
+ "hackathons",
+ "Cleared judging announcement",
+ ["scope"],
+ ),
+ "judging.room_qr.sent": policy("hackathons", "Sent judging room QR", [
+ "discordDelivery",
+ ]),
"judging.room_link.generated": policy(
"hackathons",
"Generated judging room QR",
diff --git a/packages/validators/src/judging.ts b/packages/validators/src/judging.ts
index 694d32e6f..1deeb36fe 100644
--- a/packages/validators/src/judging.ts
+++ b/packages/validators/src/judging.ts
@@ -159,6 +159,34 @@ export const judgingRoomIdSchema = z.object({
roomId: z.string().uuid(),
});
+const discordSnowflakeSchema = z.string().regex(/^\d{17,20}$/);
+
+export const judgingCommsChannelSchema = judgingHackathonIdSchema.extend({
+ channelId: discordSnowflakeSchema.nullable(),
+});
+
+const judgingAnnouncementMessageSchema = z
+ .string()
+ .trim()
+ .min(1)
+ .max(1000)
+ .refine((value) => !/\p{Cc}/u.test(value), {
+ message: "Announcement cannot contain control characters.",
+ });
+
+export const judgingAnnouncementPublishSchema = judgingHackathonIdSchema.extend(
+ {
+ includeGuests: z.boolean().default(false),
+ isUrgent: z.boolean().default(false),
+ message: judgingAnnouncementMessageSchema,
+ roomId: uuidSchema.nullable(),
+ },
+);
+
+export const judgingAnnouncementClearSchema = z.object({
+ announcementId: uuidSchema,
+});
+
export const judgingRoomCreateSchema = judgingHackathonIdSchema.extend({
challengeId: z.string().uuid(),
name: roomNameSchema,
@@ -181,7 +209,7 @@ export const guestJudgeNameSchema = z.object({
.min(2)
.max(100)
.refine((value) => !/\p{Cc}/u.test(value), {
- message: "Name cannot contain control characters.",
+ message: "Full name cannot contain control characters.",
}),
});
diff --git a/packages/validators/src/tests/judging.test.ts b/packages/validators/src/tests/judging.test.ts
index 6572962e2..454d621b9 100644
--- a/packages/validators/src/tests/judging.test.ts
+++ b/packages/validators/src/tests/judging.test.ts
@@ -2,6 +2,8 @@ import { describe, expect, it } from "vitest";
import {
guestJudgeNameSchema,
+ judgingAnnouncementPublishSchema,
+ judgingCommsChannelSchema,
judgingEvaluationSaveSchema,
judgingReorderSchema,
judgingRoomCreateSchema,
@@ -16,6 +18,57 @@ const responseId = "00000000-0000-4000-8000-000000000004";
const projectId = "00000000-0000-4000-8000-000000000005";
describe("judging inputs", () => {
+ it("accepts an optional judging communications channel", () => {
+ expect(
+ judgingCommsChannelSchema.safeParse({
+ channelId: null,
+ hackathonId,
+ }).success,
+ ).toBe(true);
+ expect(
+ judgingCommsChannelSchema.safeParse({
+ channelId: "1306042070686896230",
+ hackathonId,
+ }).success,
+ ).toBe(true);
+ expect(
+ judgingCommsChannelSchema.safeParse({
+ channelId: "not-a-channel",
+ hackathonId,
+ }).success,
+ ).toBe(false);
+ });
+
+ it("validates judging announcements and defaults their audience", () => {
+ expect(
+ judgingAnnouncementPublishSchema.parse({
+ hackathonId,
+ message: " Pitches pause at 4:30 PM. ",
+ roomId: null,
+ }),
+ ).toMatchObject({
+ includeGuests: false,
+ isUrgent: false,
+ message: "Pitches pause at 4:30 PM.",
+ roomId: null,
+ });
+ for (const message of [
+ " ",
+ "\t",
+ "\n",
+ "x".repeat(1001),
+ "Unsafe\u0007copy",
+ ]) {
+ expect(
+ judgingAnnouncementPublishSchema.safeParse({
+ hackathonId,
+ message,
+ roomId: null,
+ }).success,
+ ).toBe(false);
+ }
+ });
+
it("trims room and guest judge names", () => {
expect(
judgingRoomCreateSchema.parse({