diff --git a/.forge/features/career-discord-config/spec.md b/.forge/features/career-discord-config/spec.md new file mode 100644 index 000000000..d58271971 --- /dev/null +++ b/.forge/features/career-discord-config/spec.md @@ -0,0 +1,62 @@ +# Career Discord Configuration Spec + +Status: implemented and locally verified; [PR #664](https://github.com/KnightHacks/forge/pull/664) closed unmerged. Not republished. + +## Product promise + +Officers can configure Career posting destinations and digest notification roles +in Blade's existing `/admin/roles/config` screen. Reorganizing Discord should +not require a developer to change destination IDs in source code. + +This implements configuration for [#648](https://github.com/KnightHacks/forge/issues/648), +part of [Career #633](https://github.com/KnightHacks/forge/issues/633). + +## Settings and people + +| Setting | Purpose | +| --------------------------- | ----------------------------------------------- | +| Internship posting channel | Internship posts and digests | +| New-grad posting channel | New-grad posts and digests | +| Internship digest role | Opt-in audience mentioned by internship digests | +| New-grad digest role | Opt-in audience mentioned by new-grad digests | +| Source-health alert channel | Optional destination for source-failure alerts | + +Officers edit settings. Notification roles choose recipients and do not grant +access to officer settings. The existing Officers role remains separate. +Members' role subscriptions and actual delivery are owned by later tickets. + +## Settled behavior + +- Use the existing editor, with production and development ID fields and Save. +- Digests share their track's posting channel; no extra digest channel is added. +- The failure channel starts **Not configured**. An officer can set or clear it + later. Other production IDs remain required. +- All five settings display **Unused** until real consumers are added. +- Ordinary edits preserve the existing officer gate, audit history, and cache + invalidation. Reloading shows the persisted value. +- Adding configuration does not send messages or assign Discord roles. + +The user supplied the four production destinations/audiences and explicitly +requested leaving the failure destination unset for officer setup later. +Consolidated-channel IDs were recorded as context, not extra settings. + +## Acceptance + +1. Officers see all five settings under the correct channel/role groups. +2. Valid edits persist; non-officers cannot read or edit officer configuration. +3. Existing settings and officer edits survive migration and seed replay. +4. The unset failure entry is visible, editable, and accurately labelled. +5. Cron/API callers can resolve required IDs or explicitly read an optional ID. +6. Missing migration rows remain errors rather than silently disabling delivery. + +## Scope limits and remaining setup + +No job polling, posting, digest scheduling, subscription management, new Discord +objects, channel picker, or new settings page. Downstream #651/#653/#654 own +consumers. They must skip only an unset failure notification while retaining +normal work and error logging. + +Development destinations remain unknown. Resolve them or explicitly accept the +existing production fallback before real non-production delivery is connected. +Coordinate the five-key contract with downstream owners and confirm deployment +sequencing before merge. diff --git a/.forge/features/career-discord-config/srd.md b/.forge/features/career-discord-config/srd.md new file mode 100644 index 000000000..aee0d797f --- /dev/null +++ b/.forge/features/career-discord-config/srd.md @@ -0,0 +1,132 @@ +# Career Discord Configuration SRD + +Status: implementation complete; full local checks and focused review passed. +Base: upstream main `b88d4de35ac47f7cb1555658741778a792351c90`. + +## Contract and provenance + +| Key | Kind | Production ID | Development ID | +| ------------------------------- | ------- | --------------------- | -------------- | +| `career_internship_channel` | channel | `1537949209867583499` | null | +| `career_new_grad_channel` | channel | `1537949230742900827` | null | +| `career_internship_digest_role` | role | `1537949002031562862` | null | +| `career_new_grad_digest_role` | role | `1537948941348503683` | null | +| `career_source_health_channel` | channel | null | null | + +Source: user-supplied production IDs on 2026-11-02. These were not independently +verified against Discord. The user confirmed the failure channel stays unset +and an officer fills it later. Null development values retain the existing +production-fallback behavior; no Career delivery consumer is enabled here. +Development routing must be resolved before live non-production delivery. + +The five names follow the parent #633 automated proposal and the user's accepted +track-specific plan. The child #648 automated proposal suggested three generic +keys instead; it is not the implemented contract. Coordinate with downstream +owners before merge. Schema changes were explained before the user authorized +Phase 4; Phase 5 was explicitly requested for optional editing/reading behavior. + +Additional user inventory, excluded from new routing keys: + +- Internship consolidated: `1546636298188488704`. +- New-grad consolidated: `1546636299891384390`. +- Officers role: `486629374758748180`, matching the existing officer-role seed; + it is not a digest audience. + +## Ownership + +- `@forge/consts`: supported keys, optional-key metadata, consumer labels. +- `@forge/db`: table, environment resolver, migrations and storage constraints. +- `@forge/validators`: trimmed ID input and key-specific required-value checks. +- `@forge/utils/discord-config`: shared reads and process-local snapshot cache. +- Existing `@forge/api` router: officer access, writes, auditing and invalidation. +- Blade: existing console, field display and controlled edit dialog. + +No procedure, authorization rule, app, dependency, secret, deployment workflow, +or actual delivery consumer is added. All new consumer arrays stay empty. +The source scanner recognizes optional as well as strict reader calls so future +consumers must update usage metadata honestly. + +## Storage and reads + +One row is one supported setting; officers edit its values, not its key/kind. +Production uses productionId. Other environments use developmentId, falling +back to productionId when developmentId is null. + +Only career_source_health_channel may have a null productionId. A named +key-specific database check keeps every other setting required. Existing +17–20 digit snowflake checks remain intact. There are no placeholder IDs. + +DISCORD.OPTIONAL_CONFIG_KEYS contains only the failure key. Shared input +validation normalizes blank/whitespace to null, then rejects a missing production +ID for other keys with a productionId field error. Unknown keys/fields and +malformed snowflakes remain invalid. Validation does not verify Discord object +existence, guild membership or bot permissions. + +getDiscordConfigId retains Promise: missing rows and unresolved nulls +throw. getOptionalDiscordConfigId accepts only OptionalConfigKey and returns +string or null for intentional unset state. Missing rows and database errors +still throw. Both share the existing whole-table cache and 60-second TTL. + +The unchanged update procedure locks the stored row, writes the update and audit +in one transaction, then invalidates the writing process's cache after commit. +Other processes converge within the existing TTL. No row creation is exposed. + +## Blade behavior + +Null production/resolved fields show Not configured. A null development field +shows Reuses production only if production exists; otherwise Not configured. +The same rules apply to desktop tables and mobile cards. + +The optional editor permits configuring, clearing, and label-only edits while +unset. Other production fields remain required and disable Save when blank. +Existing guild-repoint confirmation, mutation feedback and officer gates remain. + +## Migrations and rollout + +- 0064_career_discord_config: relax production_id NOT NULL, add + knight_hacks_discord_config_production_id_required_check, then seed the five + rows with their final descriptions using ON CONFLICT (key) DO NOTHING to + preserve officer edits. +- One generated snapshot and journal entry. Historical 0026 SQL, provenance + fixture and locked migration hashes remain unchanged. + +Deploy compatible code, drain old Blade instances, then apply the migrations. +Old code cannot map new keys to consumer labels and cannot correctly present +an unset production ID. Confirm this sequence with the maintainer: CI's optional +migrate_prod job waits for build, but has no code-deployment/drain dependency. +The repository variable and external deployment timing remain unverified. + +Prefer a forward fix. Do not roll code back across registry support while the +new rows remain visible. Restoring NOT NULL also requires first configuring or +removing the unset failure entry. Never rewrite an applied migration. + +## Proof and limits + +The migration suites cover fresh installs, the previous schema, preservation of +existing edits, repeated seeds, exact IDs/kinds, required-key null rejection, +snowflake checks, and historical lineage. API tests cover null/value audit diffs, +SQL persistence, list results and immediate read invalidation. UI tests cover +both responsive renderings and editor guards; utils tests cover reads and cache. + +Real Chromium verified officer save/reload/clear against a disposable database, +SQL confirmation, required-field Save behavior, desktop and 320px mobile display, +and dialog bounds without document overflow. Screenshots were inspected. +Existing E2E auth mode skips audit writes; separate API integration tests run +with that mode off and supply real audit proof. Test databases were dropped. +No live Discord call, private environment, running Blade database or production +deployment was used. Final check receipts live in status.md and the local review +packet, not a published PR. + +## PR setup note + +An officer must configure the source-health channel in Blade before Discord +failure alerts are enabled. Missing failure routing must not disable ordinary +Career work or error logging. Resolve development routing before live testing. + +## Description copy + +Match the existing short purpose statements: channels for internship/new-grad +posts and digests, roles pinged for each digest, and an optional channel for job +source failure alerts. Remove repeated change warnings and officer-access +explanations from row descriptions. Keep setup and rollout guidance in the +editor/help and PR notes. diff --git a/.forge/features/career-discord-config/status.md b/.forge/features/career-discord-config/status.md new file mode 100644 index 000000000..bb66cac9e --- /dev/null +++ b/.forge/features/career-discord-config/status.md @@ -0,0 +1,193 @@ +# Career Discord Configuration Status + +Current phase: migrations squashed into one on a local branch; not published. +PR #664 was closed unmerged on 2026-11-03. Pushing, a new PR or reopening #664 +each need the user's explicit approval. + +## Workspace and scope + +- Task checkout: KH Forge/forge, branch stevin/career-discord-config-648-squashed, + created from c49d8eac (stevin/career-discord-config-648, kept unchanged). +- Base: b88d4de35ac47f7cb1555658741778a792351c90. Upstream main still matched it + on 2026-11-03. +- The original stevin/dev-team-application checkout and running Blade/database + were preserved. No private environment file, dependency, auth policy, CI + workflow or production service was changed. + +## Phase map + +1. Understand settings versus stored IDs: complete. +2. Agree on five settings and record production IDs: complete; development IDs remain unknown. +3. Register keys and consumer/test contract: complete. +4. Implement optional storage, generated migrations and PostgreSQL proof: complete. +5. Implement/verify optional editing, display, shared reads and audit/cache behavior: complete. +6. Full local checks, focused reviews and local PR/review artifacts: complete. + +## Settled decisions and authorization + +- Reuse the officer-only console and ID editor; no channel picker or job worker. +- Separate internship/new-grad posting channels and digest audiences. Digests + share the track's posting channel. User-supplied production IDs are in srd.md. +- Seed failure routing unset so an officer can configure it later; explicitly + requested and reaffirmed by the user. Other production values stay required. +- The schema change was explained before the user requested Phase 4. Phase 5 + explicitly authorized the optional editor/read path. Phase 6 explicitly + authorizes preparation for personal review, not PR publication. +- The five names follow the parent #633 automated proposal and accepted user + plan, differing from the child #648 automated three-key suggestion. +- Keep all five keys Unused until downstream consumers land. Extend the source + scanner for optional reads without adding a fake production consumer. + +## Phase 6 baseline validation (before copy follow-up) + +All commands used Node 24.22.1 and installed locked dependencies. Public +.env.example values were loaded at runtime; no .env was copied or changed. + +| Check | Result | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| pnpm verify:precommit | Passed: React analysis, 24 format tasks, 31 lint/dependency-build tasks, 33 typecheck/dependency-build tasks | +| Full workspace tests | Passed: 3,147 tests in 421 files; 29 Turbo tasks | +| Full workspace build | Passed: 21 Turbo tasks | +| pnpm db:generate | Passed: no schema drift or new migration; historical metadata unchanged | +| Scoped Markdown formatting and git diff --check | Passed | +| Focused standard review | Three independent read-only scopes; no actionable findings | +| Real officer browser proof | Passed: save/reload/clear with SQL confirmation, required guard, desktop and 320px mobile | + +Tests used `pnpm exec turbo run test --env-mode=loose --concurrency=2 -- --maxWorkers=2`; +build used the same build task as pnpm build with loose runtime-env forwarding +and concurrency 2. Both ran against freshly migrated, uniquely named disposable +local databases, which were dropped. Full tests had no reported skipped cases. +Build used public placeholders and unreachable local API origins; it verifies +compilation/prerendering, not production integrations. Lint retains size/style +warnings but no errors. + +The earlier standalone API HeadersInit errors disappeared after the normal +workspace dependency builds produced SDK declarations. Full workspace typecheck +and API build now pass; no SDK source change was made. + +Additional evidence: fresh real Drizzle migrator applied all 66 entries twice +without changing rows on the second application; DB upgrade/replay/constraints, +historical hashes, optional set/clear auditing and immediate read invalidation +were tested. UI E2E auth skips audit writes; normal API integration tests supply +real audit proof. No Discord notification or production migration was executed. + +The first Phase 5 browser attempt used a host mismatch and was corrected in the +test harness. Phase 6's first test invocation used pnpm options in the wrong +position; corrected to the explicit Turbo command above. Neither required an +application change or weakened checks. + +## Reviews and handoff + +Standard review scopes: schema/migrations/storage tests; validation/shared +readers/API/audit/scanner; Blade editor/display/tests/screenshots. No actionable +defect found. Reviewers did not rerun tests; the full test receipts are separate. +No review claims about live Discord object validity or production infrastructure. + +Local artifacts are under KH Forge/artifacts/career-discord-config/phase6: +review-guide.md, pr-draft.md, verification.md, check logs and review patches. +Fresh screenshots are under phase6/screenshots; phase5 captures remain historical. +The PR draft uses the fresh images. Database checklist remains unchecked until +the generated migration files are committed. + +## Operational follow-ups + +- Before merge: coordinate key names with downstream owners and have the + maintainer confirm code deployment/draining before applying 0064. The + optional CI migration job has no code-deployment/drain dependency; production + variable state and external deployment order remain unverified. +- Before live development delivery: obtain safe development IDs or explicitly + accept production fallback. Null development IDs retain that existing rule. +- Before enabling Discord failure alerts: an officer enters the destination in + Blade. Downstream workers must skip only that notification when null. +- Publication is not approved. Ask before pushing, opening or reopening a PR, + marking it ready or requesting review. + +## Links + +[#648](https://github.com/KnightHacks/forge/issues/648), +[parent #633](https://github.com/KnightHacks/forge/issues/633), +[#651](https://github.com/KnightHacks/forge/issues/651), +[#653](https://github.com/KnightHacks/forge/issues/653), +[#654](https://github.com/KnightHacks/forge/issues/654). + +## Copy follow-up — 2026-11-03 (migrations superseded by the squash below) + +The user requested shorter Career descriptions matching the existing settings, +using the humanizer skill. All five now state only their purpose in one short +sentence. Labels, keys, IDs and optional behavior are unchanged. + +The local journal already contained migration 0065, so its SQL was preserved. +Generated registered custom 0066_career_discord_config_copy, with an unchanged +schema snapshot. It updates only exact original seed wording, preserving officer +description edits and every routing value. Local migration application shortened +all five descriptions; a before/after comparison verified other fields unchanged. + +Checks after this follow-up: 20 PostgreSQL migration/lineage tests passed, DB +typecheck and scoped lint passed, snapshot comparison confirmed no schema change, +and whitespace review passed. The full workspace gates above predate this copy +update and have not been rerun for it. The journal now contains 67 entries. +Review patches/inventory were refreshed. Six later screenshots show the current +copy under phase6/screenshots; phase5 captures retain the earlier copy. +No PR, commit or push was made. + +## Submission preparation — 2026-11-03 + +Updated row-count comments to describe behavior without fixed counts, and +corrected screenshot locations. Final `pnpm verify:precommit` passed after this +cleanup: React analysis, 24 format tasks, 31 lint/build tasks and 33 typecheck/build +tasks. The 20 PostgreSQL migration/lineage tests passed again. Public example +environment values were used; application behavior and migrations were unchanged. + +A read-only check confirmed all five Career rows and all three migration hashes +in the local database. Production has not been migrated. The four supplied +production IDs are seed values; the failure channel is deliberately unset. +Career cron delivery is still owned by the downstream tickets. + +Upstream main was fetched and still matches the implementation base. The user +has now authorized committing, pushing, creating the PR and uploading the six +review screenshots. The publication result will be recorded after creation. + +## Publication — 2026-11-03 (PR later closed unmerged) + +[PR #664](https://github.com/KnightHacks/forge/pull/664) was opened from +StevScripts:stevin/career-discord-config-648 into KnightHacks/forge main. +Implementation commit: 8d39eabb. Assignee: StevScripts. Labels: Blade, API, +Database, Feature and Major. The SQL, snapshots and journal are committed. +The PR was created as a draft while the six screenshot attachments are added; +its live page records the current readiness and CI state. + +The first commit attempt failed because root ESLint exceeded Node's default +4 GB heap. Retrying with a command-scoped 8 GB heap passed every commit hook: +formatting, staged lint and staged typecheck. No hook was skipped and no +persistent environment or tooling configuration was changed. + +Merge coordination remains unchanged: confirm the five-key contract, agree on +development routing, and deploy compatible code before migrations. Production +has not been migrated and Career delivery remains outside this ticket. + +## Migration squash — 2026-11-03 + +The user approved squashing the three unreleased migrations into one to remove +two redundant ~15k-line schema snapshots. Drizzle Kit 0.x writes a full snapshot +for every migration, including data-only ones. The feature diff against base +fell from 46,661 to 16,444 added lines. + +- 0064–0066 were replaced by `0064_career_discord_config`: the generated schema + change plus the five seed rows with their final descriptions. Its snapshot has + the same schema as the old 0066 snapshot. +- The journal entry keeps the old 0064 timestamp (1793667755542). Drizzle 0.45 + applies only migrations newer than the newest recorded one, and the local + database already records 0066 (1793717650323), so it skips the new file. + If upstream adds migrations before merge, regenerate after them and decide + how to mark the new file as applied locally. +- Tests: the Career migration suite reads one file and replays only its INSERT; + the lineage test expects 65 entries ending at 0064. + +Checks: `pnpm db:generate` reported no schema changes. A fresh disposable +database ran the real migrator twice with unchanged rows, 65 ledger entries, the +required-ID check and optional set/clear; it was dropped. `pnpm db:migrate` on +the local database left its 67 ledger rows and all 19 config rows unchanged. +All 167 `@forge/db` tests and 50 `@forge/api` config tests passed on real +PostgreSQL. `pnpm verify:precommit` passed (24 format, 31 lint, 33 typecheck +tasks; command-scoped 8 GB heap). Full workspace tests and build were not rerun; +no application code changed. diff --git a/.forge/features/career-discord-config/test-cases.md b/.forge/features/career-discord-config/test-cases.md new file mode 100644 index 000000000..c1dab5a1c --- /dev/null +++ b/.forge/features/career-discord-config/test-cases.md @@ -0,0 +1,89 @@ +# Career Discord Configuration Test Cases + +Status: implemented cases verified; Phase 6 full workspace checks and focused reviews passed. +Job collection, delivery, subscriptions and scheduling are outside this ticket. + +## TC-648-001 — Fresh installation + +Apply all migrations to disposable PostgreSQL. Expect exactly the registry's 19 +keys, including all five Career settings with correct kinds, supplied IDs, and short +descriptions after the copy correction. +Only the failure row starts with null production/development IDs. Labels and +descriptions must be non-empty. Real Drizzle application twice must preserve rows. + +## TC-648-002 — Upgrade and seed replay + +Start from the previous required-production schema and officer-edited existing +settings. Apply the new schema/seed. Expect old rows unchanged and missing Career +rows inserted. Replay after a Career edit: preserve values, labels, customized descriptions, development +IDs and unset optional state; never duplicate keys. Keep the original 14-row +provenance fixture and historical SQL hashes frozen. + +## TC-648-003 — Officer list and usage + +List shuffled rows through the API and render the console. Expect groups in +server/channel/role order, correct Career placement, and empty consumer arrays +for all new keys. Scanner-derived live keys must match the consumer catalog; +strict and optional read calls must both be detected, including computed-key +violations. Tests do not count as production consumers. + +## TC-648-004 — Save, audit and shared read + +Warm the optional reader, save a failure ID through the officer API, list/read +again, then clear it. Expect SQL persistence, null/value audit diffs with the +correct key/kind, and immediate writing-process cache invalidation after commit. +The row count and key set stay fixed. Existing audit rollback tests must pass. + +## TC-648-005 — Environment selection + +Use explicit test production/development values. Expect production in production, +development elsewhere, and documented production fallback for a null development +value. A development-only failure destination must resolve null in production. +Intentional unset returns null only through the optional reader; strict reads +throw. Both readers share cache and normal TTL/invalidation behavior. + +## TC-648-006 — Access and validation + +Signed-out API requests remain UNAUTHORIZED; non-officers, including holders of +CONFIGURE_ROLES alone, remain FORBIDDEN. Optional blank, whitespace and null inputs +normalize to null. Malformed IDs, omitted production input and extra fields fail. +Every other production setting rejects blank/null with a productionId error and +no saved/audit changes. Direct SQL constraints independently reject invalid data. + +## TC-648-007 — Missing rows and regressions + +A missing migration row is not an optional unset value. Both read paths must +throw a diagnosable error; update must return NOT_FOUND and insert nothing. +Restore fixtures. Use registry-relative counts. Existing guild-repoint +confirmation, required string reads and cache failure tests remain intact. + +## TC-648-008 — Real officer screen + +Use an isolated Blade E2E instance and synthetic officer on migrated disposable +PostgreSQL. Verify save/reload/SQL persistence, clear/reload/SQL null, and required +Save disabled when empty. Inspect real desktop and 320px mobile screenshots; +check document overflow and dialog bounds. Restore/clear test values and drop +the database. E2E mode skips audits; TC-648-004 supplies separate real audit proof. + +## TC-648-009 — Optional presentation and editor + +Both responsive renderings show Not configured for null production/resolved +values. A null development ID says Reuses production only with production set. +Optional configure, clear and label-only edits are available. Other required +production fields cannot be cleared. The existing dialog, officer permission +checks, guild confirmation and save feedback stay in place. + +## Evidence locations + +- DB: discord-config-migration, career-discord-config-migration and migration-lineage suites. +- API: discord-config, discord-config-validation, club-teams and platform-config integration suites. +- Utils: discord-config cache/environment tests and consumer scanner. +- Blade: admin-config-console suite and local Chromium screenshot evidence. +- Full workspace commands and results: status.md and local Phase 6 verification receipt. + +## Downstream acceptance + +The future source worker must skip only an unset Discord failure notification, +retaining normal work and ordinary error logging/recovery. Delivery tickets own +that test; configuration tests cannot prove future worker behavior. Development +routing and maintainer rollout coordination remain operational prerequisites. diff --git a/apps/blade/src/app/_components/admin/roles/discord-config-dialog.tsx b/apps/blade/src/app/_components/admin/roles/discord-config-dialog.tsx index 904e16d24..ae16a426f 100644 --- a/apps/blade/src/app/_components/admin/roles/discord-config-dialog.tsx +++ b/apps/blade/src/app/_components/admin/roles/discord-config-dialog.tsx @@ -4,6 +4,7 @@ import { useState } from "react"; import { AlertTriangle, Loader2, Save } from "lucide-react"; import type { RouterOutputs } from "@forge/api"; +import { DISCORD } from "@forge/consts"; import { Button } from "@forge/ui/button"; import { Dialog, @@ -27,6 +28,7 @@ export interface DiscordConfigDraft { productionId: string; } +/** Copies a setting into editable draft fields; empty IDs become `""` for the inputs. */ function seed(row: DiscordConfigRow): DiscordConfigDraft { return { description: row.description, @@ -35,10 +37,11 @@ function seed(row: DiscordConfigRow): DiscordConfigDraft { // coerces it back on the way in. developmentId: row.developmentId ?? "", label: row.label, - productionId: row.productionId, + productionId: row.productionId ?? "", }; } +/** Officer dialog for editing one setting's IDs, label and description. */ export function DiscordConfigDialog({ onCancel, onSave, @@ -65,7 +68,13 @@ export function DiscordConfigDialog({ // `label` and `description` are `notNull` with no fallback behind them, so // blank is a mistake rather than a meaning. Disabling Save here means schema // rejection is not the first feedback an officer gets. - const complete = draft.label.trim() !== "" && draft.description.trim() !== ""; + const optionalDestination = DISCORD.OPTIONAL_CONFIG_KEYS.some( + (key) => key === row.key, + ); + const complete = + draft.label.trim() !== "" && + draft.description.trim() !== "" && + (optionalDestination || draft.productionId.trim() !== ""); // Only a snowflake change repoints anything. Relabelling the guild row // changes nothing any consumer resolves, so it saves like every other row. const repointsGuild = @@ -105,12 +114,26 @@ export function DiscordConfigDialog({
update({ productionId: event.target.value })} + required={!optionalDestination} value={draft.productionId} /> + {optionalDestination && ( +

+ Leave empty to disable Discord failure alerts in production. +

+ )}
@@ -125,6 +148,8 @@ export function DiscordConfigDialog({ />

Leave this empty to reuse the production ID outside production. + {optionalDestination && + " If both IDs are empty, no failure-alert destination is configured."}

diff --git a/apps/blade/src/app/_components/admin/roles/discord-config-section.tsx b/apps/blade/src/app/_components/admin/roles/discord-config-section.tsx index 5ab075261..550f1b029 100644 --- a/apps/blade/src/app/_components/admin/roles/discord-config-section.tsx +++ b/apps/blade/src/app/_components/admin/roles/discord-config-section.tsx @@ -73,9 +73,8 @@ function UsageBadge({ row }: { row: DiscordConfigRow }) { } /** - * The whole reason this column exists: ten of the fourteen keys are read by - * nothing, and the `description` text reads identically for an inert row and a - * live one. The badge carries the distinction, the list carries the stakes. + * Descriptions explain a setting even when nothing reads it yet. The badge + * distinguishes unused settings; the list identifies their active consumers. */ function UsageDetail({ row }: { row: DiscordConfigRow }) { if (row.readBy.length === 0) { @@ -96,10 +95,19 @@ function UsageDetail({ row }: { row: DiscordConfigRow }) { ); } -function SnowflakeValue({ value }: { value: string | null }) { +/** Shows an ID, or "Reuses production" / "Not configured" when it is empty. */ +function SnowflakeValue({ + value, + reuseProduction = false, +}: { + value: string | null; + reuseProduction?: boolean; +}) { if (value === null) { return ( - Reuses production + + {reuseProduction ? "Reuses production" : "Not configured"} + ); } return {value}; @@ -134,6 +142,7 @@ function EditButton({ ); } +/** Lists every Discord setting (table on desktop, cards on mobile) with its editor. */ export function DiscordConfigSection({ environment, onSaved, @@ -144,8 +153,8 @@ export function DiscordConfigSection({ rows: DiscordConfigRow[]; }) { const [editingKey, setEditingKey] = useState(null); - // One mutation object is shared by all fourteen rows, so `isPending` would - // spin every one of them. The pending row is tracked by its own key. + // All rows share one mutation. Track the pending key so only the row being + // saved shows a loading indicator. const [savingKey, setSavingKey] = useState(null); const update = api.discordConfig.update.useMutation({ onSuccess() { @@ -221,7 +230,10 @@ export function DiscordConfigSection({ - + @@ -286,7 +298,10 @@ export function DiscordConfigSection({
Development ID
- +
diff --git a/apps/blade/src/tests/admin/admin-config-console.test.tsx b/apps/blade/src/tests/admin/admin-config-console.test.tsx index ffa068336..51dcba1c2 100644 --- a/apps/blade/src/tests/admin/admin-config-console.test.tsx +++ b/apps/blade/src/tests/admin/admin-config-console.test.tsx @@ -110,7 +110,13 @@ type DiscordRow = DiscordConfig["rows"][number]; const SHARED_DESCRIPTION = "Discord snowflake used by the Knight Hacks platform."; -const CHANNEL_KEYS = new Set(["log_channel", "recruiting_channel"]); +const CHANNEL_KEYS = new Set([ + "log_channel", + "recruiting_channel", + "career_internship_channel", + "career_new_grad_channel", + "career_source_health_channel", +]); function kindOf(key: DISCORD.ConfigKey): DiscordRow["kind"] { if (key === "guild") return "guild"; @@ -225,12 +231,12 @@ function present(value: T | null | undefined): T { return value; } -function renderDiscordSection() { +function renderDiscordSection(rows: DiscordRow[] = discordRows) { return render( , ); } @@ -241,6 +247,109 @@ beforeEach(() => { mocks.captured.discord = null; }); +describe("TC-648-009: unconfigured Career failure destination", () => { + function failureRows(productionId: string | null = null) { + return discordRows + .filter(({ key }) => key === "career_source_health_channel") + .map((row) => ({ + ...row, + developmentId: null, + productionId, + resolvedId: productionId, + })); + } + + it("shows Not configured for unset values in desktop rows and mobile cards", () => { + const { container } = renderDiscordSection(failureRows()); + const { cards, tables } = renderings(container); + for (const rendering of [first(tables), first(cards)]) { + expect(within(rendering).getAllByText("Not configured")).toHaveLength(3); + expect(within(rendering).queryByText("Reuses production")).toBeNull(); + } + }); + + it("shows Reuses production only for a missing development ID with production configured", () => { + const { container } = renderDiscordSection( + failureRows("999999999999999999"), + ); + const { cards, tables } = renderings(container); + for (const rendering of [first(tables), first(cards)]) { + expect(within(rendering).getAllByText("Reuses production")).toHaveLength( + 1, + ); + expect(within(rendering).getAllByText("999999999999999999")).toHaveLength( + 2, + ); + } + }); + + it.each([null, "999999999999999999"])( + "lets officers configure or clear a failure destination from %s", + async (original) => { + const user = userEvent.setup(); + renderDiscordSection(failureRows(original)); + await user.click( + first( + screen.getAllByRole("button", { + name: "Edit career source health channel", + }), + ), + ); + const production = screen.getByLabelText("Production ID"); + expect(production).toHaveValue(original ?? ""); + expect( + screen.getByText( + "Leave empty to disable Discord failure alerts in production.", + ), + ).toBeInTheDocument(); + const next = original === null ? "888888888888888888" : ""; + await user.clear(production); + if (next) await user.type(production, next); + await user.click(screen.getByRole("button", { name: /save setting/i })); + expect(mocks.discordMutate).toHaveBeenCalledWith( + expect.objectContaining({ + key: "career_source_health_channel", + productionId: next, + }), + ); + }, + ); + + it("allows a label-only edit while the optional destination stays empty", async () => { + const user = userEvent.setup(); + renderDiscordSection(failureRows()); + await user.click( + first( + screen.getAllByRole("button", { + name: "Edit career source health channel", + }), + ), + ); + await user.type(screen.getByLabelText("Label"), " updated"); + await user.click(screen.getByRole("button", { name: /save setting/i })); + expect(mocks.discordMutate).toHaveBeenCalledWith( + expect.objectContaining({ productionId: "" }), + ); + }); + + it("keeps Save disabled when an officer clears a required channel", async () => { + const user = userEvent.setup(); + renderDiscordSection(); + await user.click( + first( + screen.getAllByRole("button", { + name: "Edit career internship channel", + }), + ), + ); + await user.clear(screen.getByLabelText("Production ID")); + expect( + screen.getByRole("button", { name: /save setting/i }), + ).toBeDisabled(); + expect(mocks.discordMutate).not.toHaveBeenCalled(); + }); +}); + describe("TC-004: inert keys are structurally distinct from live keys", () => { it("marks inert keys with a rendered badge rather than the description", () => { const { container } = renderDiscordSection(); @@ -261,10 +370,15 @@ describe("TC-004: inert keys are structurally distinct from live keys", () => { } expect(DISCORD.INERT_CONFIG_KEYS).toEqual([ + "career_internship_channel", + "career_new_grad_channel", + "career_source_health_channel", "officer_role", "admin_role", "volunteer_role", "vip_role", + "career_internship_digest_role", + "career_new_grad_digest_role", ]); expect([...DISCORD.LIVE_CONFIG_KEYS]).toEqual([ "guild", @@ -295,7 +409,7 @@ describe("TC-004: inert keys are structurally distinct from live keys", () => { }); describe("TC-031: the three kind groups are labelled regions in both renderings", () => { - it("partitions fourteen rows across guild, channel and role in server order", () => { + it("TC-648-003: partitions settings, including Career, by kind in server order", () => { const { container } = renderDiscordSection(); const { cards, tables } = renderings(container); @@ -327,8 +441,11 @@ describe("TC-031: the three kind groups are labelled regions in both renderings" expect(edits(channels)).toEqual([ "Edit log channel", "Edit recruiting channel", + "Edit career internship channel", + "Edit career new grad channel", + "Edit career source health channel", ]); - expect(edits(roleGroup)).toHaveLength(11); + expect(edits(roleGroup)).toHaveLength(13); // Declaration order, not alphabetical: `admin role` follows // `officer role`. A convenience sort on the client would flip these. expect(edits(roleGroup).slice(0, 3)).toEqual([ @@ -642,8 +759,9 @@ describe("TC-018: no create or delete affordance exists in the console", () => { ); expect(names.length).toBeGreaterThan(30); for (const name of names) { + // "New grad" names an audience; a leading "New" labels a creation action. expect(name).not.toMatch( - /\b(add|create|new|delete|remove|unclassify|reset)\b/i, + /\b(add|create|delete|remove|unclassify|reset)\b|^\s*new\b/i, ); } expect(container.querySelector("form")).toBeNull(); diff --git a/docs/DATABASE-USAGE.md b/docs/DATABASE-USAGE.md index 0148c97e5..ff1c6600b 100644 --- a/docs/DATABASE-USAGE.md +++ b/docs/DATABASE-USAGE.md @@ -153,6 +153,13 @@ Notes: - One row is one _setting_, not one value. `production_id` and `development_id` are the two environment values of the same setting, replacing the `PROD_`/`DEV_` constant pairs that fed an `IS_PROD` ternary. - `development_id` is nullable and means "reuse `production_id`", which is how `alumni_role` and the six `*_director_role` rows behave. +- `production_id` is required except for `career_source_health_channel`. That + optional failure-alert destination starts with both IDs null, so an officer + can configure it later. The database enforces the exception by key; other + settings cannot omit their production ID. Low-level environment resolution + can return null; `getDiscordConfigId` remains strict and throws for an unset + destination. `getOptionalDiscordConfigId` accepts only optional keys and + returns null for an unset value; a missing row still throws a migration error. - `key` is a code contract enumerated by `DISCORD.CONFIG_KEYS`, not something an officer invents; renaming one needs a data migration. `kind`, `label`, and `description` exist so a future admin UI can present a row without the editor guessing. - Check constraints enforce that both ID columns are 17-20 digit snowflakes, so a pasted role mention or a trailing space fails at write time rather than as a Discord 404 inside a cron job. - The read path caches the whole table for 60 seconds per process. There is no admin UI yet, so writes happen in SQL; a writer inside the app must call `invalidateDiscordConfigCache()`, and other processes converge within the TTL. diff --git a/packages/api/src/tests/config/discord-config-validation.test.ts b/packages/api/src/tests/config/discord-config-validation.test.ts new file mode 100644 index 000000000..418d7edd0 --- /dev/null +++ b/packages/api/src/tests/config/discord-config-validation.test.ts @@ -0,0 +1,80 @@ +import { describe, expect, it } from "vitest"; + +import { DISCORD } from "@forge/consts"; +import { discordConfigUpdateSchema } from "@forge/validators"; + +const draft = { + description: "Optional Career source-failure alerts.", + developmentId: "", + key: "career_source_health_channel", + label: "Career source-health alert channel", + productionId: "", +}; + +describe("TC-648-009: optional Career configuration validation", () => { + it.each(["", " ", null])( + "normalizes an unset failure destination (%s) to null", + (productionId) => { + expect( + discordConfigUpdateSchema.parse({ ...draft, productionId }), + ).toMatchObject({ + productionId: null, + developmentId: null, + }); + }, + ); + + it("trims real IDs and allows a development-only failure destination", () => { + expect( + discordConfigUpdateSchema.parse({ + ...draft, + productionId: " 999999999999999999 ", + }).productionId, + ).toBe("999999999999999999"); + expect( + discordConfigUpdateSchema.parse({ + ...draft, + developmentId: " 888888888888888888 ", + }), + ).toMatchObject({ + productionId: null, + developmentId: "888888888888888888", + }); + }); + + it("rejects empty production IDs for every required setting with a field error", () => { + for (const key of DISCORD.CONFIG_KEYS.filter( + (key) => key !== "career_source_health_channel", + )) { + for (const productionId of ["", " ", null]) { + const parsed = discordConfigUpdateSchema.safeParse({ + ...draft, + key, + productionId, + }); + expect(parsed.success, key).toBe(false); + if (!parsed.success) + expect(parsed.error.issues[0]?.path).toEqual(["productionId"]); + } + } + }); + + it.each(["123", "<@&1537949002031562862>", "1537949209867583499x"])( + "rejects malformed optional IDs (%s)", + (productionId) => { + expect( + discordConfigUpdateSchema.safeParse({ ...draft, productionId }).success, + ).toBe(false); + }, + ); + + it("rejects omitted fields and attempts to change the key's kind", () => { + const { productionId: _productionId, ...missingProduction } = draft; + expect(discordConfigUpdateSchema.safeParse(missingProduction).success).toBe( + false, + ); + expect( + discordConfigUpdateSchema.safeParse({ ...draft, kind: "role" }).success, + ).toBe(false); + }); +}); diff --git a/packages/api/src/tests/config/discord-config.test.ts b/packages/api/src/tests/config/discord-config.test.ts index a9de3bc04..f68f2f491 100644 --- a/packages/api/src/tests/config/discord-config.test.ts +++ b/packages/api/src/tests/config/discord-config.test.ts @@ -33,10 +33,15 @@ function createCaller(session: Session | null = SESSION) { }); } -/** The fourteen rows migration 0025 seeds, with their real `kind` values. */ +/** Explicit kinds for supported settings, including the pending Career rows. */ const KIND_BY_KEY: Record = { admin_role: "role", alumni_role: "role", + career_internship_channel: "channel", + career_internship_digest_role: "role", + career_new_grad_channel: "channel", + career_new_grad_digest_role: "role", + career_source_health_channel: "channel", design_director_role: "role", development_director_role: "role", guild: "guild", @@ -143,15 +148,62 @@ describe("discordConfig procedures (TC-002, TC-019)", () => { const result = await createCaller().list(); - expect(result.rows.map((row) => row.key)).toEqual([...DISCORD.CONFIG_KEYS]); + // TC-648-003: the editor must include Career entries in their intended groups. + expect(result.rows.map((row) => row.key)).toEqual([ + "guild", + "log_channel", + "recruiting_channel", + "career_internship_channel", + "career_new_grad_channel", + "career_source_health_channel", + "officer_role", + "admin_role", + "volunteer_role", + "alumni_role", + "vip_role", + "outreach_director_role", + "design_director_role", + "development_director_role", + "sponsorship_director_role", + "workshops_director_role", + "projects_mentorship_director_role", + "career_internship_digest_role", + "career_new_grad_digest_role", + ]); expect(result.rows.map((row) => row.kind)).toEqual([ "guild", - "channel", - "channel", - ...Array.from({ length: 11 }, () => "role"), + ...Array.from({ length: 5 }, () => "channel"), + ...Array.from({ length: 13 }, () => "role"), ]); }); + it("keeps an unset failure channel visible and editable only by officers", async () => { + mocks.permissionRows = [{ permissions: permissionBitstring("IS_OFFICER") }]; + mocks.configRows = [ + configRow("career_source_health_channel", { productionId: null }), + ]; + expect((await createCaller().list()).rows[0]).toMatchObject({ + key: "career_source_health_channel", + productionId: null, + developmentId: null, + resolvedId: null, + readBy: [], + }); + mocks.permissionRows = [ + { permissions: permissionBitstring("CONFIGURE_ROLES") }, + ]; + await expect( + createCaller().update({ + key: "career_source_health_channel", + productionId: "", + developmentId: "", + label: "Failure channel", + description: "Optional alerts", + }), + ).rejects.toMatchObject({ code: "FORBIDDEN" }); + expect(mocks.db.transaction).not.toHaveBeenCalled(); + }); + it("groups by the kind column rather than the key text", async () => { mocks.permissionRows = [{ permissions: permissionBitstring("IS_OFFICER") }]; // `vip_role` is keyed `*_role` but claims to be a channel. Grouping must @@ -163,11 +215,20 @@ describe("discordConfig procedures (TC-002, TC-019)", () => { const result = await createCaller().list(); const kinds = result.rows.map((row) => row.kind); - expect(result.rows[3]?.key).toBe("vip_role"); - expect(kinds.indexOf("role")).toBe(4); + expect( + result.rows.filter((row) => row.kind === "channel").map((row) => row.key), + ).toEqual([ + "log_channel", + "recruiting_channel", + "career_internship_channel", + "career_new_grad_channel", + "career_source_health_channel", + "vip_role", + ]); + expect(result.rows[kinds.indexOf("role")]?.key).toBe("officer_role"); }); - it("marks exactly the four inert keys with an empty consumer list", async () => { + it("marks the Career and other unread keys with an empty consumer list", async () => { mocks.permissionRows = [{ permissions: permissionBitstring("IS_OFFICER") }]; const result = await createCaller().list(); @@ -175,7 +236,17 @@ describe("discordConfig procedures (TC-002, TC-019)", () => { .filter((row) => row.readBy.length === 0) .map((row) => row.key); - expect(inert).toHaveLength(4); + expect(inert).toEqual([ + "career_internship_channel", + "career_new_grad_channel", + "career_source_health_channel", + "officer_role", + "admin_role", + "volunteer_role", + "vip_role", + "career_internship_digest_role", + "career_new_grad_digest_role", + ]); expect( result.rows .filter((row) => row.readBy.length > 0) diff --git a/packages/api/src/tests/integration/platform-config.test.ts b/packages/api/src/tests/integration/platform-config.test.ts index 8d2e1b14b..7837c11e7 100644 --- a/packages/api/src/tests/integration/platform-config.test.ts +++ b/packages/api/src/tests/integration/platform-config.test.ts @@ -75,6 +75,7 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { let audit: AuditSchemas; let console_: Awaited>; let getKnightHacksGuildId: typeof import("@forge/utils/discord-config").getKnightHacksGuildId; + let getOptionalDiscordConfigId: typeof import("@forge/utils/discord-config").getOptionalDiscordConfigId; let getVisiblePublicClubRoster: typeof import("../../utils/guild/club-roster").getVisiblePublicClubRoster; async function officerCaller() { @@ -169,7 +170,8 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { clubTeam = await import("@forge/db/schemas/club-team"); discordConfig = await import("@forge/db/schemas/discord-config"); audit = await import("@forge/db/schemas/audit"); - ({ getKnightHacksGuildId } = await import("@forge/utils/discord-config")); + ({ getKnightHacksGuildId, getOptionalDiscordConfigId } = + await import("@forge/utils/discord-config")); ({ getVisiblePublicClubRoster } = await import("../../utils/guild/club-roster")); const knightHacks = await import("@forge/db/schemas/knight-hacks"); @@ -218,6 +220,77 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { }, 30_000); describe("discordConfig.update", () => { + it("TC-648-009: persists, audits and immediately resolves setting and clearing failure alerts", async () => { + const key = "career_source_health_channel"; + const before = await configRow(key); + expect(await getOptionalDiscordConfigId(key)).toBeNull(); + const draft = { + key, + label: before.label, + description: before.description, + developmentId: "", + } as const; + const id = "990000000000000901"; + + const configured = await console_.discordConfig.update({ + ...draft, + productionId: ` ${id} `, + }); + expect(configured).toMatchObject({ + productionId: id, + resolvedId: id, + readBy: [], + }); + expect((await configRow(key)).productionId).toBe(id); + expect( + (await console_.discordConfig.list()).rows.find( + (row) => row.key === key, + )?.resolvedId, + ).toBe(id); + expect(await getOptionalDiscordConfigId(key)).toBe(id); + expect( + (await auditEventsFor("discord_config.updated")).at(-1), + ).toMatchObject({ + changes: [{ before: null, after: id, field: "productionId" }], + metadata: { configKey: key, configKind: "channel", isInert: true }, + }); + + const cleared = await console_.discordConfig.update({ + ...draft, + productionId: " ", + }); + expect(cleared).toMatchObject({ + productionId: null, + developmentId: null, + resolvedId: null, + }); + expect((await configRow(key)).productionId).toBeNull(); + expect(await getOptionalDiscordConfigId(key)).toBeNull(); + expect( + (await auditEventsFor("discord_config.updated")).at(-1)?.changes, + ).toEqual([{ before: id, after: null, field: "productionId" }]); + }); + + it("TC-648-006: rejects clearing a required Career channel before writing", async () => { + const key = "career_internship_channel"; + const before = await configRow(key); + const auditCount = (await auditEventsFor("discord_config.updated")) + .length; + await expect( + console_.discordConfig.update({ + key, + label: before.label, + description: before.description, + developmentId: "", + productionId: "", + }), + ).rejects.toMatchObject({ code: "BAD_REQUEST" }); + expect(await configRow(key)).toEqual(before); + expect(await auditEventsFor("discord_config.updated")).toHaveLength( + auditCount, + ); + }); + it("TC-NEG-006: stores a cleared development snowflake as NULL", async () => { const before = await configRow("vip_role"); @@ -265,7 +338,7 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { developmentId: before.developmentId ?? "", key: "admin_role", label: "Renamed admin role", - productionId: before.productionId, + productionId: before.productionId ?? "", }); const [event] = await auditEventsFor("discord_config.updated").then( @@ -308,7 +381,7 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { developmentId: "990000000000000301", key: "guild", label: before.label, - productionId: before.productionId, + productionId: before.productionId ?? "", }), ).rejects.toMatchObject({ code: "PRECONDITION_FAILED" }); @@ -329,7 +402,7 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { developmentId: before.developmentId ?? "", key: "guild", label: "Knight Hacks server", - productionId: before.productionId, + productionId: before.productionId ?? "", }); expect(updated.label).toBe("Knight Hacks server"); @@ -346,7 +419,7 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { developmentId: "990000000000000401", key: "guild", label: before.label, - productionId: before.productionId, + productionId: before.productionId ?? "", }); // No clock advance. The sixty-second TTL is not waited on. @@ -424,12 +497,12 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { developmentId: "", key: "volunteer_role", label: row.label, - productionId: row.productionId, + productionId: row.productionId ?? "", }), ).rejects.toMatchObject({ code: "NOT_FOUND" }); expect( await client.select().from(discordConfig.DiscordConfig), - ).toHaveLength(13); + ).toHaveLength(DISCORD.CONFIG_KEYS.length - 1); } finally { await client.insert(discordConfig.DiscordConfig).values(row); } @@ -455,7 +528,7 @@ describe.runIf(canRunDatabaseTests())("platform configuration console", () => { developmentId: before.developmentId ?? "", key: "officer_role", label: "Never committed", - productionId: before.productionId, + productionId: before.productionId ?? "", }), ).rejects.toThrow(); } finally { diff --git a/packages/consts/src/discord.ts b/packages/consts/src/discord.ts index b9c25b65a..d659ccead 100644 --- a/packages/consts/src/discord.ts +++ b/packages/consts/src/discord.ts @@ -22,11 +22,11 @@ export const CONFIG_KINDS = ["channel", "guild", "role"] as const; export type ConfigKind = (typeof CONFIG_KINDS)[number]; /** - * Every setting the platform reads out of `knight_hacks_discord_config`. + * Every setting the platform supports in `knight_hacks_discord_config`. * - * This is a contract, not organizational state: adding a key means adding code - * that reads it, so it belongs in the codebase. The *values* behind the keys are - * organizational state and belong to the table. + * This is a code contract. A key can be registered before its delivery consumer + * lands; CONFIG_KEY_CONSUMERS records whether anything reads it yet. The values + * behind the keys are organizational state and belong to the table. * * Keys are stable identifiers — renaming one is a breaking change that needs a * data migration, exactly like renaming a column. @@ -35,6 +35,10 @@ export const CONFIG_KEYS = [ "guild", "log_channel", "recruiting_channel", + // Career routing (#648); API and cron consumers land in #651, #653, and #654. + "career_internship_channel", + "career_new_grad_channel", + "career_source_health_channel", "officer_role", "admin_role", "volunteer_role", @@ -46,14 +50,22 @@ export const CONFIG_KEYS = [ "sponsorship_director_role", "workshops_director_role", "projects_mentorship_director_role", + "career_internship_digest_role", + "career_new_grad_digest_role", ] as const; export type ConfigKey = (typeof CONFIG_KEYS)[number]; +/** Settings that can intentionally have no Discord destination. */ +export const OPTIONAL_CONFIG_KEYS = [ + "career_source_health_channel", +] as const satisfies readonly ConfigKey[]; + +export type OptionalConfigKey = (typeof OPTIONAL_CONFIG_KEYS)[number]; + /** * What reads each setting, in officer-facing terms. An empty array means the - * platform reads this key nowhere: the row exists because the constant it - * replaced existed, not because anything resolves it. + * platform does not resolve this key yet. * * `satisfies Record` is the point. Adding a key to * {@link CONFIG_KEYS} without answering "what reads it?" is a type error, which @@ -73,6 +85,11 @@ export const CONFIG_KEY_CONSUMERS = { ], log_channel: ["Blade admin action log embeds"], recruiting_channel: ["Recruiting notifications posted by form callbacks"], + career_internship_channel: [], + career_new_grad_channel: [], + career_source_health_channel: [], + career_internship_digest_role: [], + career_new_grad_digest_role: [], alumni_role: ["Nightly alumni grant/revoke cron"], officer_role: [], admin_role: [], diff --git a/packages/db/drizzle/0064_career_discord_config.sql b/packages/db/drizzle/0064_career_discord_config.sql new file mode 100644 index 000000000..76d698156 --- /dev/null +++ b/packages/db/drizzle/0064_career_discord_config.sql @@ -0,0 +1,21 @@ +ALTER TABLE "knight_hacks_discord_config" ALTER COLUMN "production_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "knight_hacks_discord_config" ADD CONSTRAINT "knight_hacks_discord_config_production_id_required_check" CHECK ("knight_hacks_discord_config"."key" = 'career_source_health_channel' OR "knight_hacks_discord_config"."production_id" IS NOT NULL); +--> statement-breakpoint +-- #648: production posting channels and opt-in reminder roles supplied by the +-- Career contributor. Digests share each track's posting channel. These keys +-- have no delivery consumers yet; this migration does not enable posting. +-- +-- No development IDs have been supplied. NULL retains the existing resolver's +-- production fallback, so configure development destinations before wiring or +-- exercising real Discord delivery outside production. +-- +-- An officer must configure the source-health channel in Blade before Discord +-- failure alerts can be enabled. Both IDs intentionally start NULL. +-- Replaying the seed must preserve officer edits, including an unset channel. +INSERT INTO "knight_hacks_discord_config" ("key", "kind", "label", "description", "production_id", "development_id") VALUES + ('career_internship_channel', 'channel', 'Career internship posting channel', 'Channel for internship posts and digests.', '1537949209867583499', NULL), + ('career_new_grad_channel', 'channel', 'Career new-grad posting channel', 'Channel for new-grad posts and digests.', '1537949230742900827', NULL), + ('career_source_health_channel', 'channel', 'Career source-health alert channel', 'Optional channel for job source failure alerts.', NULL, NULL), + ('career_internship_digest_role', 'role', 'Career internship digest role', 'Discord role pinged for internship digests.', '1537949002031562862', NULL), + ('career_new_grad_digest_role', 'role', 'Career new-grad digest role', 'Discord role pinged for new-grad digests.', '1537948941348503683', NULL) +ON CONFLICT ("key") DO NOTHING; diff --git a/packages/db/drizzle/meta/0064_snapshot.json b/packages/db/drizzle/meta/0064_snapshot.json new file mode 100644 index 000000000..7f97058a1 --- /dev/null +++ b/packages/db/drizzle/meta/0064_snapshot.json @@ -0,0 +1,15087 @@ +{ + "id": "f7fbdc9c-7ef5-472c-93a4-a2398e67f6ad", + "prevId": "1d33ad72-c70b-41cb-86c5-1bc48565f581", + "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": false + }, + "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_production_id_required_check": { + "name": "knight_hacks_discord_config_production_id_required_check", + "value": "\"knight_hacks_discord_config\".\"key\" = 'career_source_health_channel' OR \"knight_hacks_discord_config\".\"production_id\" IS NOT NULL" + }, + "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_id": { + "name": "tag_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "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_tag_id_knight_hacks_event_tag_id_fk": { + "name": "knight_hacks_event_tag_id_knight_hacks_event_tag_id_fk", + "tableFrom": "knight_hacks_event", + "tableTo": "knight_hacks_event_tag", + "columnsFrom": ["tag_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "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 + }, + "emoji": { + "name": "emoji", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "announcement_channel_id": { + "name": "announcement_channel_id", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "skip_next_week": { + "name": "skip_next_week", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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_announcement_channel_check": { + "name": "knight_hacks_event_tag_announcement_channel_check", + "value": "\"knight_hacks_event_tag\".\"announcement_channel_id\" IS NULL OR \"knight_hacks_event_tag\".\"announcement_channel_id\" ~ '^[0-9]{17,20}$'" + }, + "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 + }, + "store_catalog_visible": { + "name": "store_catalog_visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "store_open": { + "name": "store_open", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "store_location": { + "name": "store_location", + "type": "varchar(240)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "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 + }, + "issue_reports_channel_id": { + "name": "issue_reports_channel_id", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "issue_reports_role_id": { + "name": "issue_reports_role_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_issue_reports_channel_id_check": { + "name": "knight_hacks_hackathon_issue_reports_channel_id_check", + "value": "\"knight_hacks_hackathon\".\"issue_reports_channel_id\" IS NULL OR \"knight_hacks_hackathon\".\"issue_reports_channel_id\" ~ '^[0-9]{17,20}$'" + }, + "knight_hacks_hackathon_issue_reports_role_id_check": { + "name": "knight_hacks_hackathon_issue_reports_role_id_check", + "value": "\"knight_hacks_hackathon\".\"issue_reports_role_id\" IS NULL OR \"knight_hacks_hackathon\".\"issue_reports_role_id\" ~ '^[0-9]{17,20}$'" + }, + "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 + }, + "hacker_schedule_published": { + "name": "hacker_schedule_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hacker_schedule_emergency": { + "name": "hacker_schedule_emergency", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "project_claims_started_at": { + "name": "project_claims_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "project_claim_url": { + "name": "project_claim_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "challenge_groups_initialized_at": { + "name": "challenge_groups_initialized_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "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_map_configuration": { + "name": "knight_hacks_hackathon_map_configuration", + "schema": "", + "columns": { + "hackathon_id": { + "name": "hackathon_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "restrictions_enabled": { + "name": "restrictions_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "rooms": { + "name": "rooms", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "knight_hacks_hackathon_map_configuration_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_hackathon_map_configuration_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_hackathon_map_configuration", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "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_hacker_team": { + "name": "knight_hacks_hacker_team", + "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 + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "together": { + "name": "together", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "frozen_at": { + "name": "frozen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "class_id": { + "name": "class_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hacker_team_hackathon_idx": { + "name": "hacker_team_hackathon_idx", + "columns": [ + { + "expression": "hackathon_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hacker_team_class_idx": { + "name": "hacker_team_class_idx", + "columns": [ + { + "expression": "class_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knight_hacks_hacker_team_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_hacker_team_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_hacker_team", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "hacker_team_scoped_class_fk": { + "name": "hacker_team_scoped_class_fk", + "tableFrom": "knight_hacks_hacker_team", + "tableTo": "knight_hacks_hackathon_class", + "columnsFrom": ["class_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "hacker_team_id_hackathon_unique": { + "name": "hacker_team_id_hackathon_unique", + "nullsNotDistinct": false, + "columns": ["id", "hackathon_id"] + } + }, + "policies": {}, + "checkConstraints": { + "hacker_team_name_required": { + "name": "hacker_team_name_required", + "value": "length(trim(\"knight_hacks_hacker_team\".\"name\")) > 0" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_hacker_team_member": { + "name": "knight_hacks_hacker_team_member", + "schema": "", + "columns": { + "attendee_id": { + "name": "attendee_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "hackathon_id": { + "name": "hackathon_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "hacker_team_member_team_idx": { + "name": "hacker_team_member_team_idx", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hacker_team_member_hackathon_idx": { + "name": "hacker_team_member_hackathon_idx", + "columns": [ + { + "expression": "hackathon_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hacker_team_one_owner": { + "name": "hacker_team_one_owner", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knight_hacks_hacker_team_member\".\"role\" = 'owner'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hacker_team_member_scoped_team_fk": { + "name": "hacker_team_member_scoped_team_fk", + "tableFrom": "knight_hacks_hacker_team_member", + "tableTo": "knight_hacks_hacker_team", + "columnsFrom": ["team_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "hacker_team_member_scoped_attendee_fk": { + "name": "hacker_team_member_scoped_attendee_fk", + "tableFrom": "knight_hacks_hacker_team_member", + "tableTo": "knight_hacks_hacker_attendee", + "columnsFrom": ["attendee_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "hacker_team_member_role_valid": { + "name": "hacker_team_member_role_valid", + "value": "\"knight_hacks_hacker_team_member\".\"role\" in ('pending', 'member', 'owner')" + } + }, + "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_appointment": { + "name": "knight_hacks_judging_appointment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schedule_id": { + "name": "schedule_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "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 + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "deadline_at": { + "name": "deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp with time zone", + "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_judging_appointment_project_idx": { + "name": "knight_hacks_judging_appointment_project_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starts_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knight_hacks_judging_appointment_schedule_fk": { + "name": "knight_hacks_judging_appointment_schedule_fk", + "tableFrom": "knight_hacks_judging_appointment", + "tableTo": "knight_hacks_judging_schedule", + "columnsFrom": ["schedule_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_judging_appointment_room_fk": { + "name": "knight_hacks_judging_appointment_room_fk", + "tableFrom": "knight_hacks_judging_appointment", + "tableTo": "knight_hacks_judging_room", + "columnsFrom": ["room_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "knight_hacks_judging_appointment_project_challenge_fk": { + "name": "knight_hacks_judging_appointment_project_challenge_fk", + "tableFrom": "knight_hacks_judging_appointment", + "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" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "knight_hacks_judging_appointment_task_unique": { + "name": "knight_hacks_judging_appointment_task_unique", + "nullsNotDistinct": false, + "columns": ["schedule_id", "project_id", "challenge_id"] + }, + "knight_hacks_judging_appointment_room_slot_unique": { + "name": "knight_hacks_judging_appointment_room_slot_unique", + "nullsNotDistinct": false, + "columns": ["schedule_id", "room_id", "starts_at"] + }, + "knight_hacks_judging_appointment_scope_unique": { + "name": "knight_hacks_judging_appointment_scope_unique", + "nullsNotDistinct": false, + "columns": ["id", "hackathon_id"] + } + }, + "policies": {}, + "checkConstraints": { + "knight_hacks_judging_appointment_times_check": { + "name": "knight_hacks_judging_appointment_times_check", + "value": "\"knight_hacks_judging_appointment\".\"starts_at\" < \"knight_hacks_judging_appointment\".\"deadline_at\" AND \"knight_hacks_judging_appointment\".\"deadline_at\" <= \"knight_hacks_judging_appointment\".\"ends_at\"" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_judging_building": { + "name": "knight_hacks_judging_building", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "knight_hacks_judging_building_name_unique": { + "name": "knight_hacks_judging_building_name_unique", + "columns": [ + { + "expression": "lower(btrim(\"name\"))", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "knight_hacks_judging_building_name_check": { + "name": "knight_hacks_judging_building_name_check", + "value": "length(btrim(\"knight_hacks_judging_building\".\"name\")) > 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 + }, + "building_id": { + "name": "building_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "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 AND \"knight_hacks_judging_room\".\"building_id\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "knight_hacks_judging_room_active_location_unique": { + "name": "knight_hacks_judging_room_active_location_unique", + "columns": [ + { + "expression": "hackathon_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "building_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(btrim(\"name\"))", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knight_hacks_judging_room\".\"archived_at\" IS NULL AND \"knight_hacks_judging_room\".\"building_id\" IS NOT 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_building_id_knight_hacks_judging_building_id_fk": { + "name": "knight_hacks_judging_room_building_id_knight_hacks_judging_building_id_fk", + "tableFrom": "knight_hacks_judging_room", + "tableTo": "knight_hacks_judging_building", + "columnsFrom": ["building_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "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_judging_schedule": { + "name": "knight_hacks_judging_schedule", + "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 + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "setup_minutes": { + "name": "setup_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "judging_minutes": { + "name": "judging_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "teardown_minutes": { + "name": "teardown_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "same_building_break_minutes": { + "name": "same_building_break_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "different_building_break_minutes": { + "name": "different_building_break_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reduced_breaks_acknowledged_at": { + "name": "reduced_breaks_acknowledged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_result_at": { + "name": "first_result_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "saved_by_user_id": { + "name": "saved_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()" + } + }, + "indexes": {}, + "foreignKeys": { + "knight_hacks_judging_schedule_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_judging_schedule_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_judging_schedule", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_judging_schedule_saved_by_user_id_auth_user_id_fk": { + "name": "knight_hacks_judging_schedule_saved_by_user_id_auth_user_id_fk", + "tableFrom": "knight_hacks_judging_schedule", + "tableTo": "auth_user", + "columnsFrom": ["saved_by_user_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "knight_hacks_judging_schedule_hackathon_unique": { + "name": "knight_hacks_judging_schedule_hackathon_unique", + "nullsNotDistinct": false, + "columns": ["hackathon_id"] + }, + "knight_hacks_judging_schedule_scope_unique": { + "name": "knight_hacks_judging_schedule_scope_unique", + "nullsNotDistinct": false, + "columns": ["id", "hackathon_id"] + } + }, + "policies": {}, + "checkConstraints": { + "knight_hacks_judging_schedule_timing_check": { + "name": "knight_hacks_judging_schedule_timing_check", + "value": "\"knight_hacks_judging_schedule\".\"ends_at\" > \"knight_hacks_judging_schedule\".\"starts_at\" AND \"knight_hacks_judging_schedule\".\"setup_minutes\" >= 0 AND \"knight_hacks_judging_schedule\".\"judging_minutes\" > 0 AND \"knight_hacks_judging_schedule\".\"teardown_minutes\" >= 0 AND \"knight_hacks_judging_schedule\".\"same_building_break_minutes\" > 0 AND \"knight_hacks_judging_schedule\".\"different_building_break_minutes\" >= \"knight_hacks_judging_schedule\".\"same_building_break_minutes\"" + }, + "knight_hacks_judging_schedule_minute_check": { + "name": "knight_hacks_judging_schedule_minute_check", + "value": "extract(second from \"knight_hacks_judging_schedule\".\"starts_at\") = 0 AND extract(second from \"knight_hacks_judging_schedule\".\"ends_at\") = 0" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_judging_schedule_job": { + "name": "knight_hacks_judging_schedule_job", + "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 + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_fingerprint": { + "name": "source_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timing": { + "name": "timing", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "problem": { + "name": "problem", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "checkpoint": { + "name": "checkpoint", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true, + "default": "'searching'" + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "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()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "knight_hacks_judging_schedule_job_hackathon_idx": { + "name": "knight_hacks_judging_schedule_job_hackathon_idx", + "columns": [ + { + "expression": "hackathon_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_judging_schedule_job_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_judging_schedule_job_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_judging_schedule_job", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_judging_schedule_job_created_by_user_id_auth_user_id_fk": { + "name": "knight_hacks_judging_schedule_job_created_by_user_id_auth_user_id_fk", + "tableFrom": "knight_hacks_judging_schedule_job", + "tableTo": "auth_user", + "columnsFrom": ["created_by_user_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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_point_store_item": { + "name": "knight_hacks_point_store_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 + }, + "name": { + "name": "name", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "price": { + "name": "price", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "stock": { + "name": "stock", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sold_out": { + "name": "sold_out", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image_object_name": { + "name": "image_object_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "point_store_item_hackathon_idx": { + "name": "point_store_item_hackathon_idx", + "columns": [ + { + "expression": "hackathon_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knight_hacks_point_store_item_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_point_store_item_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_point_store_item", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "point_store_item_hackathon_unique": { + "name": "point_store_item_hackathon_unique", + "nullsNotDistinct": false, + "columns": ["id", "hackathon_id"] + } + }, + "policies": {}, + "checkConstraints": { + "point_store_item_price_check": { + "name": "point_store_item_price_check", + "value": "\"knight_hacks_point_store_item\".\"price\" BETWEEN 0 AND 1000000" + }, + "point_store_item_stock_check": { + "name": "point_store_item_stock_check", + "value": "\"knight_hacks_point_store_item\".\"stock\" IS NULL OR \"knight_hacks_point_store_item\".\"stock\" BETWEEN 0 AND 1000000" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_point_store_purchase": { + "name": "knight_hacks_point_store_purchase", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "hackathon_id": { + "name": "hackathon_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attendee_id": { + "name": "attendee_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "item_id": { + "name": "item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "hacker_name": { + "name": "hacker_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "item_name": { + "name": "item_name", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "unit_price": { + "name": "unit_price", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total": { + "name": "total", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "stock_tracked": { + "name": "stock_tracked", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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 + }, + "voided_by_name": { + "name": "voided_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "void_reason": { + "name": "void_reason", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "restocked": { + "name": "restocked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "point_store_purchase_history_idx": { + "name": "point_store_purchase_history_idx", + "columns": [ + { + "expression": "hackathon_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": {} + }, + "point_store_purchase_balance_idx": { + "name": "point_store_purchase_balance_idx", + "columns": [ + { + "expression": "attendee_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hackathon_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knight_hacks_point_store_purchase_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_point_store_purchase_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_point_store_purchase", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_point_store_purchase_attendee_id_knight_hacks_hacker_attendee_id_fk": { + "name": "knight_hacks_point_store_purchase_attendee_id_knight_hacks_hacker_attendee_id_fk", + "tableFrom": "knight_hacks_point_store_purchase", + "tableTo": "knight_hacks_hacker_attendee", + "columnsFrom": ["attendee_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "knight_hacks_point_store_purchase_actor_id_auth_user_id_fk": { + "name": "knight_hacks_point_store_purchase_actor_id_auth_user_id_fk", + "tableFrom": "knight_hacks_point_store_purchase", + "tableTo": "auth_user", + "columnsFrom": ["actor_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "knight_hacks_point_store_purchase_voided_by_auth_user_id_fk": { + "name": "knight_hacks_point_store_purchase_voided_by_auth_user_id_fk", + "tableFrom": "knight_hacks_point_store_purchase", + "tableTo": "auth_user", + "columnsFrom": ["voided_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "point_store_purchase_item_fk": { + "name": "point_store_purchase_item_fk", + "tableFrom": "knight_hacks_point_store_purchase", + "tableTo": "knight_hacks_point_store_item", + "columnsFrom": ["item_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "point_store_purchase_amount_check": { + "name": "point_store_purchase_amount_check", + "value": "\"knight_hacks_point_store_purchase\".\"unit_price\" BETWEEN 0 AND 1000000 AND \"knight_hacks_point_store_purchase\".\"quantity\" BETWEEN 1 AND 1000 AND \"knight_hacks_point_store_purchase\".\"total\" = \"knight_hacks_point_store_purchase\".\"unit_price\" * \"knight_hacks_point_store_purchase\".\"quantity\"" + }, + "point_store_purchase_void_check": { + "name": "point_store_purchase_void_check", + "value": "(\"knight_hacks_point_store_purchase\".\"voided_at\" IS NULL AND NOT \"knight_hacks_point_store_purchase\".\"restocked\") OR \"knight_hacks_point_store_purchase\".\"voided_at\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_print_job": { + "name": "knight_hacks_print_job", + "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 + }, + "hacker_attendee_id": { + "name": "hacker_attendee_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category_reminder_attempted_at": { + "name": "category_reminder_attempted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "category_reminder_sent_at": { + "name": "category_reminder_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "status_note": { + "name": "status_note", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status_changed_at": { + "name": "status_changed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status_changed_by_user_id": { + "name": "status_changed_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "estimated_ready_at": { + "name": "estimated_ready_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "knight_hacks_print_job_queue_idx": { + "name": "knight_hacks_print_job_queue_idx", + "columns": [ + { + "expression": "hackathon_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "knight_hacks_print_job_attendee_idx": { + "name": "knight_hacks_print_job_attendee_idx", + "columns": [ + { + "expression": "hacker_attendee_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "knight_hacks_print_job_status_changed_by_idx": { + "name": "knight_hacks_print_job_status_changed_by_idx", + "columns": [ + { + "expression": "status_changed_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knight_hacks_print_job_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_print_job_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_print_job", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_print_job_status_changed_by_user_id_auth_user_id_fk": { + "name": "knight_hacks_print_job_status_changed_by_user_id_auth_user_id_fk", + "tableFrom": "knight_hacks_print_job", + "tableTo": "auth_user", + "columnsFrom": ["status_changed_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "knight_hacks_print_job_attendee_scope_fk": { + "name": "knight_hacks_print_job_attendee_scope_fk", + "tableFrom": "knight_hacks_print_job", + "tableTo": "knight_hacks_hacker_attendee", + "columnsFrom": ["hacker_attendee_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "knight_hacks_print_job_scoped_identity_unique": { + "name": "knight_hacks_print_job_scoped_identity_unique", + "nullsNotDistinct": false, + "columns": ["id", "hackathon_id", "hacker_attendee_id"] + } + }, + "policies": {}, + "checkConstraints": { + "knight_hacks_print_job_category_check": { + "name": "knight_hacks_print_job_category_check", + "value": "\"knight_hacks_print_job\".\"category\" IS NULL OR \"knight_hacks_print_job\".\"category\" IN ('project', 'personal')" + }, + "knight_hacks_print_job_status_check": { + "name": "knight_hacks_print_job_status_check", + "value": "\"knight_hacks_print_job\".\"status\" IN ('received', 'printing', 'needs_clarification', 'ready_for_pickup', 'picked_up', 'cancelled')" + }, + "knight_hacks_print_job_description_not_blank_check": { + "name": "knight_hacks_print_job_description_not_blank_check", + "value": "\"knight_hacks_print_job\".\"description\" ~ '[^[:space:]]'" + }, + "knight_hacks_print_job_clarification_note_check": { + "name": "knight_hacks_print_job_clarification_note_check", + "value": "\"knight_hacks_print_job\".\"status\" <> 'needs_clarification' OR (\"knight_hacks_print_job\".\"status_note\" IS NOT NULL AND \"knight_hacks_print_job\".\"status_note\" ~ '[^[:space:]]')" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_print_job_file": { + "name": "knight_hacks_print_job_file", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "print_job_id": { + "name": "print_job_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "hackathon_id": { + "name": "hackathon_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "hacker_attendee_id": { + "name": "hacker_attendee_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(100)", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "knight_hacks_print_job_file_job_idx": { + "name": "knight_hacks_print_job_file_job_idx", + "columns": [ + { + "expression": "print_job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "knight_hacks_print_job_file_attendee_idx": { + "name": "knight_hacks_print_job_file_attendee_idx", + "columns": [ + { + "expression": "hacker_attendee_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "knight_hacks_print_job_file_staged_idx": { + "name": "knight_hacks_print_job_file_staged_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knight_hacks_print_job_file\".\"print_job_id\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knight_hacks_print_job_file_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_print_job_file_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_print_job_file", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_print_job_file_attendee_scope_fk": { + "name": "knight_hacks_print_job_file_attendee_scope_fk", + "tableFrom": "knight_hacks_print_job_file", + "tableTo": "knight_hacks_hacker_attendee", + "columnsFrom": ["hacker_attendee_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_print_job_file_job_scope_fk": { + "name": "knight_hacks_print_job_file_job_scope_fk", + "tableFrom": "knight_hacks_print_job_file", + "tableTo": "knight_hacks_print_job", + "columnsFrom": ["print_job_id", "hackathon_id", "hacker_attendee_id"], + "columnsTo": ["id", "hackathon_id", "hacker_attendee_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "knight_hacks_print_job_file_object_unique": { + "name": "knight_hacks_print_job_file_object_unique", + "nullsNotDistinct": false, + "columns": ["object_name"] + } + }, + "policies": {}, + "checkConstraints": { + "knight_hacks_print_job_file_size_check": { + "name": "knight_hacks_print_job_file_size_check", + "value": "\"knight_hacks_print_job_file\".\"size\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_printing_configuration": { + "name": "knight_hacks_printing_configuration", + "schema": "", + "columns": { + "hackathon_id": { + "name": "hackathon_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "discord_channel_id": { + "name": "discord_channel_id", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "is_open": { + "name": "is_open", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "print_minutes": { + "name": "print_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 60 + }, + "printer_count": { + "name": "printer_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "knight_hacks_printing_configuration_hackathon_id_knight_hacks_hackathon_id_fk": { + "name": "knight_hacks_printing_configuration_hackathon_id_knight_hacks_hackathon_id_fk", + "tableFrom": "knight_hacks_printing_configuration", + "tableTo": "knight_hacks_hackathon", + "columnsFrom": ["hackathon_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "knight_hacks_printing_configuration_print_minutes_check": { + "name": "knight_hacks_printing_configuration_print_minutes_check", + "value": "\"knight_hacks_printing_configuration\".\"print_minutes\" BETWEEN 5 AND 600" + }, + "knight_hacks_printing_configuration_printer_count_check": { + "name": "knight_hacks_printing_configuration_printer_count_check", + "value": "\"knight_hacks_printing_configuration\".\"printer_count\" BETWEEN 1 AND 20" + }, + "knight_hacks_printing_configuration_channel_id_check": { + "name": "knight_hacks_printing_configuration_channel_id_check", + "value": "\"knight_hacks_printing_configuration\".\"discord_channel_id\" IS NULL OR \"knight_hacks_printing_configuration\".\"discord_channel_id\" ~ '^[0-9]{17,20}$'" + } + }, + "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 + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_group": { + "name": "is_group", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "import_label_prefix": { + "name": "import_label_prefix", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "is_general": { + "name": "is_general", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_scheduled": { + "name": "is_scheduled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_remote": { + "name": "is_remote", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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" + }, + "project_challenge_parent_scope_fk": { + "name": "project_challenge_parent_scope_fk", + "tableFrom": "knight_hacks_project_challenge", + "tableTo": "knight_hacks_project_challenge", + "columnsFrom": ["parent_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "no action", + "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", "is_group"] + }, + "knight_hacks_project_challenge_id_hackathon_unique": { + "name": "knight_hacks_project_challenge_id_hackathon_unique", + "nullsNotDistinct": false, + "columns": ["id", "hackathon_id"] + } + }, + "policies": {}, + "checkConstraints": { + "project_challenge_root_general": { + "name": "project_challenge_root_general", + "value": "NOT \"knight_hacks_project_challenge\".\"is_general\" OR \"knight_hacks_project_challenge\".\"parent_id\" IS NULL" + }, + "project_challenge_not_self": { + "name": "project_challenge_not_self", + "value": "\"knight_hacks_project_challenge\".\"parent_id\" IS NULL OR \"knight_hacks_project_challenge\".\"parent_id\" <> \"knight_hacks_project_challenge\".\"id\"" + }, + "project_challenge_mode_check": { + "name": "project_challenge_mode_check", + "value": "NOT (\"knight_hacks_project_challenge\".\"is_scheduled\" AND \"knight_hacks_project_challenge\".\"is_remote\")" + } + }, + "isRLSEnabled": false + }, + "public.knight_hacks_project_claim": { + "name": "knight_hacks_project_claim", + "schema": "", + "columns": { + "member_id": { + "name": "member_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "project_id": { + "name": "project_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 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "knight_hacks_project_claim_user_id_auth_user_id_fk": { + "name": "knight_hacks_project_claim_user_id_auth_user_id_fk", + "tableFrom": "knight_hacks_project_claim", + "tableTo": "auth_user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "project_claim_member_scope_fk": { + "name": "project_claim_member_scope_fk", + "tableFrom": "knight_hacks_project_claim", + "tableTo": "knight_hacks_project_member", + "columnsFrom": ["member_id", "project_id"], + "columnsTo": ["id", "project_id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "project_claim_project_scope_fk": { + "name": "project_claim_project_scope_fk", + "tableFrom": "knight_hacks_project_claim", + "tableTo": "knight_hacks_project", + "columnsFrom": ["project_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "project_claim_user_event_unique": { + "name": "project_claim_user_event_unique", + "nullsNotDistinct": false, + "columns": ["user_id", "hackathon_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knight_hacks_project_claim_link": { + "name": "knight_hacks_project_claim_link", + "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 + }, + "token": { + "name": "token", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "consumed_by_user_id": { + "name": "consumed_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_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": {}, + "foreignKeys": { + "knight_hacks_project_claim_link_member_id_knight_hacks_project_member_id_fk": { + "name": "knight_hacks_project_claim_link_member_id_knight_hacks_project_member_id_fk", + "tableFrom": "knight_hacks_project_claim_link", + "tableTo": "knight_hacks_project_member", + "columnsFrom": ["member_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_project_claim_link_consumed_by_user_id_auth_user_id_fk": { + "name": "knight_hacks_project_claim_link_consumed_by_user_id_auth_user_id_fk", + "tableFrom": "knight_hacks_project_claim_link", + "tableTo": "auth_user", + "columnsFrom": ["consumed_by_user_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "project_claim_link_member_unique": { + "name": "project_claim_link_member_unique", + "nullsNotDistinct": false, + "columns": ["member_id"] + }, + "project_claim_link_token_unique": { + "name": "project_claim_link_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": { + "project_claim_link_consumed_check": { + "name": "project_claim_link_consumed_check", + "value": "(\"knight_hacks_project_claim_link\".\"consumed_at\" IS NULL AND \"knight_hacks_project_claim_link\".\"consumed_by_user_id\" IS NULL AND \"knight_hacks_project_claim_link\".\"token\" IS NOT NULL) OR (\"knight_hacks_project_claim_link\".\"consumed_at\" IS NOT NULL AND \"knight_hacks_project_claim_link\".\"consumed_by_user_id\" IS NOT NULL AND \"knight_hacks_project_claim_link\".\"token\" IS NULL)" + } + }, + "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 + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "auto_submitted_at": { + "name": "auto_submitted_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_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_appointment_fk": { + "name": "knight_hacks_project_evaluation_appointment_fk", + "tableFrom": "knight_hacks_project_evaluation", + "tableTo": "knight_hacks_judging_appointment", + "columnsFrom": ["appointment_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "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_draft": { + "name": "knight_hacks_project_evaluation_draft", + "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 + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "evaluation_id": { + "name": "evaluation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "base_evaluation_revision": { + "name": "base_evaluation_revision", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ratings": { + "name": "ratings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "responses": { + "name": "responses", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "deadline_at": { + "name": "deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reconciliation_failed_at": { + "name": "reconciliation_failed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reconciliation_error_code": { + "name": "reconciliation_error_code", + "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": { + "knight_hacks_evaluation_draft_due_idx": { + "name": "knight_hacks_evaluation_draft_due_idx", + "columns": [ + { + "expression": "hackathon_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deadline_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knight_hacks_project_evaluation_draft_evaluation_id_knight_hacks_project_evaluation_id_fk": { + "name": "knight_hacks_project_evaluation_draft_evaluation_id_knight_hacks_project_evaluation_id_fk", + "tableFrom": "knight_hacks_project_evaluation_draft", + "tableTo": "knight_hacks_project_evaluation", + "columnsFrom": ["evaluation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_evaluation_draft_appointment_fk": { + "name": "knight_hacks_evaluation_draft_appointment_fk", + "tableFrom": "knight_hacks_project_evaluation_draft", + "tableTo": "knight_hacks_judging_appointment", + "columnsFrom": ["appointment_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_evaluation_draft_project_fk": { + "name": "knight_hacks_evaluation_draft_project_fk", + "tableFrom": "knight_hacks_project_evaluation_draft", + "tableTo": "knight_hacks_project_to_challenge", + "columnsFrom": ["project_id", "challenge_id", "hackathon_id"], + "columnsTo": ["project_id", "challenge_id", "hackathon_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knight_hacks_evaluation_draft_judge_fk": { + "name": "knight_hacks_evaluation_draft_judge_fk", + "tableFrom": "knight_hacks_project_evaluation_draft", + "tableTo": "knight_hacks_judge", + "columnsFrom": ["judge_id", "hackathon_id"], + "columnsTo": ["id", "hackathon_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "knight_hacks_evaluation_draft_author_unique": { + "name": "knight_hacks_evaluation_draft_author_unique", + "nullsNotDistinct": false, + "columns": ["judge_id", "project_id", "challenge_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 + }, + "invited_user_id": { + "name": "invited_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "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" + }, + "knight_hacks_project_member_invited_user_id_auth_user_id_fk": { + "name": "knight_hacks_project_member_invited_user_id_auth_user_id_fk", + "tableFrom": "knight_hacks_project_member", + "tableTo": "auth_user", + "columnsFrom": ["invited_user_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "project_member_id_project_unique": { + "name": "project_member_id_project_unique", + "nullsNotDistinct": false, + "columns": ["id", "project_id"] + }, + "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 + }, + "is_opt_in": { + "name": "is_opt_in", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": 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 2775869b7..9d9277431 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -449,6 +449,13 @@ "when": 1791641041552, "tag": "0063_first_jean_grey", "breakpoints": true + }, + { + "idx": 64, + "version": "7", + "when": 1793667755542, + "tag": "0064_career_discord_config", + "breakpoints": true } ] } diff --git a/packages/db/scripts/seed_devdb.ts b/packages/db/scripts/seed_devdb.ts index 474c4da96..fec94c59f 100644 --- a/packages/db/scripts/seed_devdb.ts +++ b/packages/db/scripts/seed_devdb.ts @@ -110,9 +110,9 @@ async function guildIds() { const row = await backupDb.query.DiscordConfig.findFirst({ where: eq(discordConfigSchema.DiscordConfig.key, "guild"), }); - if (!row?.developmentId) { + if (!row?.developmentId || !row.productionId) { throw new Error( - 'The "guild" row in knight_hacks_discord_config is missing or has no development_id. Seeding cannot map production Discord objects onto the development server without both.', + 'The "guild" row in knight_hacks_discord_config is missing or has no production_id or development_id. Seeding cannot map production Discord objects onto the development server without both.', ); } return { development: row.developmentId, production: row.productionId }; diff --git a/packages/db/src/schemas/discord-config.ts b/packages/db/src/schemas/discord-config.ts index a42302305..fb9ad811a 100644 --- a/packages/db/src/schemas/discord-config.ts +++ b/packages/db/src/schemas/discord-config.ts @@ -17,6 +17,8 @@ const createTable = pgTableCreator((name) => `knight_hacks_${name}`); * `developmentId` is nullable and means "no separate development value" — the * resolver falls back to `productionId`, which is exactly what the old * `ALUMNI_ROLE = PROD_ALUMNI_ROLE` line did. + * Only `career_source_health_channel` may also omit `productionId`; null then + * means no Discord failure-alert destination has been configured. * * Read this through `@forge/utils/discord-config` rather than querying it * directly; that module caches the whole table and applies the fallback rule. @@ -40,8 +42,8 @@ export const DiscordConfig = createTable( label: t.varchar({ length: 128 }).notNull(), /** What this controls and what breaks when it is wrong. */ description: t.text().notNull(), - /** Snowflake used when `NODE_ENV === "production"`. */ - productionId: t.varchar({ length: 20 }).notNull(), + /** Production snowflake. Only the optional Career failure channel may omit it. */ + productionId: t.varchar({ length: 20 }), /** Snowflake used outside production. `NULL` reuses `productionId`. */ developmentId: t.varchar({ length: 20 }), updatedAt: t @@ -63,6 +65,10 @@ export const DiscordConfig = createTable( "knight_hacks_discord_config_production_id_check", sql`${table.productionId} ~ '^[0-9]{17,20}$'`, ), + requiredProductionId: check( + "knight_hacks_discord_config_production_id_required_check", + sql`${table.key} = 'career_source_health_channel' OR ${table.productionId} IS NOT NULL`, + ), validDevelopmentId: check( "knight_hacks_discord_config_development_id_check", sql`${table.developmentId} IS NULL OR ${table.developmentId} ~ '^[0-9]{17,20}$'`, @@ -80,7 +86,7 @@ export type SelectDiscordConfig = typeof DiscordConfig.$inferSelect; export function resolveDiscordConfigId( row: Pick, isProduction: boolean, -): string { +): string | null { return isProduction ? row.productionId : (row.developmentId ?? row.productionId); diff --git a/packages/db/src/tests/career-discord-config-migration.test.ts b/packages/db/src/tests/career-discord-config-migration.test.ts new file mode 100644 index 000000000..cca2fcf87 --- /dev/null +++ b/packages/db/src/tests/career-discord-config-migration.test.ts @@ -0,0 +1,235 @@ +import type { QueryResultRow } from "pg"; +import { getTableConfig } from "drizzle-orm/pg-core"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +import { DISCORD } from "@forge/consts"; + +import type { DisposableDatabase } from "../testing"; +import { + DiscordConfig, + resolveDiscordConfigId, +} from "../schemas/discord-config"; +import { + applyMigration, + canRunDatabaseTests, + provisionDisposableDatabase, + readMigrations, +} from "../testing"; +import { EXPECTED_CAREER_CONFIG } from "./support/career-discord-config"; + +interface ConfigRow extends QueryResultRow { + description: string; + development_id: string | null; + key: string; + kind: string; + label: string; + production_id: string | null; +} + +/** Loads the Career migration and its seed statement for replay tests. */ +async function careerMigration() { + const migration = (await readMigrations()).find(({ name }) => + name.startsWith("0064_career_discord_config"), + ); + if (!migration) throw new Error("Career config migration is missing."); + // Replays re-run only the seed; the schema statements are not repeatable. + const seed = migration.sql + .split("--> statement-breakpoint") + .find((statement) => statement.includes("INSERT INTO")); + if (!seed) throw new Error("Career config seed statement is missing."); + return { migration, seed: { name: migration.name, sql: seed } }; +} + +describe("Career Discord configuration storage contract", () => { + it("allows an absent production value with a key-specific presence constraint", () => { + expect(DiscordConfig.productionId.notNull).toBe(false); + expect( + getTableConfig(DiscordConfig).checks.map(({ name }) => name), + ).toContain("knight_hacks_discord_config_production_id_required_check"); + }); + + it("registers an additive schema migration and a non-destructive seed", async () => { + const { migration, seed } = await careerMigration(); + expect(migration.sql).toContain( + 'ALTER COLUMN "production_id" DROP NOT NULL', + ); + expect(migration.sql).toContain("career_source_health_channel"); + expect(migration.sql).not.toMatch(/DROP (TABLE|COLUMN)/); + expect(migration.sql).not.toMatch(/\b(UPDATE|DELETE|TRUNCATE)\b/i); + expect(seed.sql).toContain('ON CONFLICT ("key") DO NOTHING'); + for (const row of EXPECTED_CAREER_CONFIG) { + expect(seed.sql).toContain(`'${row.key}'`); + if (row.production !== null) + expect(seed.sql).toContain(`'${row.production}'`); + } + }); + + it("resolves an unconfigured destination to null, with an independent development override", () => { + const unset = { developmentId: null, productionId: null }; + expect(resolveDiscordConfigId(unset, true)).toBeNull(); + expect(resolveDiscordConfigId(unset, false)).toBeNull(); + const developmentOnly = { ...unset, developmentId: "222222222222222222" }; + expect(resolveDiscordConfigId(developmentOnly, true)).toBeNull(); + expect(resolveDiscordConfigId(developmentOnly, false)).toBe( + "222222222222222222", + ); + }); +}); + +describe.runIf(canRunDatabaseTests())( + "Career Discord config migrations on PostgreSQL", + () => { + let database: DisposableDatabase; + + beforeAll(async () => { + database = await provisionDisposableDatabase("forge_career_config"); + }, 120_000); + + afterAll(async () => { + await database.drop(); + }); + + /** Reads all Discord config rows, ordered by key. */ + async function rows() { + return ( + await database.client.query( + 'SELECT * FROM "knight_hacks_discord_config" ORDER BY "key"', + ) + ).rows; + } + + it("TC-648-001: seeds all five rows with the supplied IDs and only failure alerts unset", async () => { + const saved = await rows(); + expect(saved.map(({ key }) => key).sort()).toEqual( + [...DISCORD.CONFIG_KEYS].sort(), + ); + for (const expected of EXPECTED_CAREER_CONFIG) { + const row = saved.find(({ key }) => key === expected.key); + expect(row).toMatchObject({ + description: expected.description, + development_id: expected.development, + key: expected.key, + kind: expected.kind, + production_id: expected.production, + }); + expect(row?.label.trim().length).toBeGreaterThan(0); + expect(row?.description.trim().length).toBeGreaterThan(0); + } + expect( + saved + .filter(({ production_id }) => production_id === null) + .map(({ key }) => key), + ).toEqual(["career_source_health_channel"]); + }); + + it("TC-648-002: upgrades the previous schema without changing existing officer edits", async () => { + const { migration } = await careerMigration(); + await database.client.query("BEGIN"); + try { + await database.client.query( + "DELETE FROM knight_hacks_discord_config WHERE key LIKE 'career_%'", + ); + await database.client.query( + "ALTER TABLE knight_hacks_discord_config DROP CONSTRAINT knight_hacks_discord_config_production_id_required_check", + ); + await database.client.query( + "ALTER TABLE knight_hacks_discord_config ALTER COLUMN production_id SET NOT NULL", + ); + await database.client.query( + "UPDATE knight_hacks_discord_config SET production_id = '999999999999999999' WHERE key = 'log_channel'", + ); + const before = await rows(); + await applyMigration(database.client, migration); + const after = await rows(); + expect(after.filter(({ key }) => !key.startsWith("career_"))).toEqual( + before, + ); + expect(after).toHaveLength(DISCORD.CONFIG_KEYS.length); + } finally { + await database.client.query("ROLLBACK"); + } + }); + + it("TC-648-002: seed replay preserves edited and cleared values and inserts missing rows", async () => { + const { seed } = await careerMigration(); + await database.client.query("BEGIN"); + try { + await database.client.query( + "UPDATE knight_hacks_discord_config SET label = 'Officer choice', description = 'Officer custom description', production_id = '999999999999999999', development_id = '888888888888888888' WHERE key = 'career_internship_channel'", + ); + await database.client.query( + "DELETE FROM knight_hacks_discord_config WHERE key = 'career_new_grad_digest_role'", + ); + await applyMigration(database.client, seed); + const once = await rows(); + await applyMigration(database.client, seed); + expect(await rows()).toEqual(once); + expect(once).toHaveLength(DISCORD.CONFIG_KEYS.length); + expect( + once.find(({ key }) => key === "career_internship_channel"), + ).toMatchObject({ + label: "Officer choice", + description: "Officer custom description", + production_id: "999999999999999999", + development_id: "888888888888888888", + }); + expect( + once.find(({ key }) => key === "career_source_health_channel") + ?.production_id, + ).toBeNull(); + } finally { + await database.client.query("ROLLBACK"); + } + }); + + it("requires production IDs for every other key, including after renaming the optional row", async () => { + for (const key of DISCORD.CONFIG_KEYS.filter( + (key) => key !== "career_source_health_channel", + )) { + await expect( + database.client.query( + "UPDATE knight_hacks_discord_config SET production_id = NULL WHERE key = $1", + [key], + ), + ).rejects.toThrow(/production_id_required_check/); + } + await expect( + database.client.query( + "UPDATE knight_hacks_discord_config SET key = 'unexpected_optional' WHERE key = 'career_source_health_channel'", + ), + ).rejects.toThrow(/production_id_required_check/); + }); + + it("lets officers configure and clear failure alerts while retaining snowflake checks", async () => { + for (const column of ["production_id", "development_id"]) { + for (const value of [ + "", + "123", + "12345678901234567 ", + "<@&1537949002031562862>", + ]) { + await expect( + database.client.query( + `UPDATE knight_hacks_discord_config SET ${column} = $1 WHERE key = 'career_source_health_channel'`, + [value], + ), + ).rejects.toThrow(new RegExp(`${column}_check|value too long`)); + } + } + await database.client.query( + "UPDATE knight_hacks_discord_config SET production_id = '999999999999999999' WHERE key = 'career_source_health_channel'", + ); + expect( + (await rows()).find(({ key }) => key === "career_source_health_channel") + ?.production_id, + ).toBe("999999999999999999"); + await database.client.query( + "UPDATE knight_hacks_discord_config SET production_id = NULL WHERE key = 'career_source_health_channel'", + ); + expect( + (await rows()).find(({ key }) => key === "career_source_health_channel") + ?.production_id, + ).toBeNull(); + }); + }, +); diff --git a/packages/db/src/tests/discord-config-migration.test.ts b/packages/db/src/tests/discord-config-migration.test.ts index 690b21163..4af1ad0bd 100644 --- a/packages/db/src/tests/discord-config-migration.test.ts +++ b/packages/db/src/tests/discord-config-migration.test.ts @@ -7,6 +7,7 @@ import { DISCORD } from "@forge/consts"; import type { DisposableDatabase } from "../testing"; import { resolveDiscordConfigId } from "../schemas/discord-config"; import { canRunDatabaseTests, provisionDisposableDatabase } from "../testing"; +import { EXPECTED_CAREER_CONFIG } from "./support/career-discord-config"; const migrationsDirectory = new URL("../../drizzle/", import.meta.url); @@ -123,7 +124,7 @@ interface DiscordConfigRow extends QueryResultRow { key: string; kind: string; label: string; - production_id: string; + production_id: string | null; } async function readDiscordConfigMigration() { @@ -166,9 +167,11 @@ describe("Discord config migration contract", () => { }); it("covers exactly the keys the platform knows how to read", () => { - expect([...EXPECTED_BACKFILL.map(({ key }) => key)].sort()).toEqual( - [...DISCORD.CONFIG_KEYS].sort(), - ); + expect( + [...EXPECTED_BACKFILL, ...EXPECTED_CAREER_CONFIG] + .map(({ key }) => key) + .sort(), + ).toEqual([...DISCORD.CONFIG_KEYS].sort()); }); it("constrains kind to exactly the documented set", async () => { @@ -228,7 +231,7 @@ describe.runIf(canRunDatabaseTests())( FROM "knight_hacks_discord_config" ORDER BY "key"`, ); - expect(rows).toHaveLength(EXPECTED_BACKFILL.length); + expect(rows).toHaveLength(DISCORD.CONFIG_KEYS.length); for (const expected of EXPECTED_BACKFILL) { const row = rows.find(({ key }) => key === expected.key); expect( diff --git a/packages/db/src/tests/migration-lineage.test.ts b/packages/db/src/tests/migration-lineage.test.ts index 8c7286d8c..c52fbaf01 100644 --- a/packages/db/src/tests/migration-lineage.test.ts +++ b/packages/db/src/tests/migration-lineage.test.ts @@ -106,7 +106,7 @@ describe("canonical production migration lineage", () => { await readFile(new URL("_journal.json", metadataDirectory), "utf8"), ) as { entries: JournalEntry[] }; - expect(journal.entries).toHaveLength(64); + expect(journal.entries).toHaveLength(65); expect(journal.entries[10]).toMatchObject({ idx: 10, tag: "0010_wooden_supreme_intelligence", @@ -117,8 +117,8 @@ describe("canonical production migration lineage", () => { tag: "0059_needy_menace", }); expect(journal.entries.at(-1)).toMatchObject({ - idx: 63, - tag: "0063_first_jean_grey", + idx: 64, + tag: "0064_career_discord_config", }); for (const [position, entry] of journal.entries.entries()) { diff --git a/packages/db/src/tests/support/career-discord-config.ts b/packages/db/src/tests/support/career-discord-config.ts new file mode 100644 index 000000000..2b1203b8a --- /dev/null +++ b/packages/db/src/tests/support/career-discord-config.ts @@ -0,0 +1,38 @@ +/** Production IDs supplied by the Career contributor for #648. */ +export const EXPECTED_CAREER_CONFIG = [ + { + development: null, + key: "career_internship_channel", + description: "Channel for internship posts and digests.", + kind: "channel", + production: "1537949209867583499", + }, + { + development: null, + key: "career_new_grad_channel", + description: "Channel for new-grad posts and digests.", + kind: "channel", + production: "1537949230742900827", + }, + { + development: null, + key: "career_source_health_channel", + description: "Optional channel for job source failure alerts.", + kind: "channel", + production: null, + }, + { + development: null, + key: "career_internship_digest_role", + description: "Discord role pinged for internship digests.", + kind: "role", + production: "1537949002031562862", + }, + { + development: null, + key: "career_new_grad_digest_role", + description: "Discord role pinged for new-grad digests.", + kind: "role", + production: "1537948941348503683", + }, +] as const; diff --git a/packages/utils/src/discord-config.ts b/packages/utils/src/discord-config.ts index bed0ee0c8..0d5e51896 100644 --- a/packages/utils/src/discord-config.ts +++ b/packages/utils/src/discord-config.ts @@ -16,18 +16,15 @@ import { } from "@forge/db/schemas/discord-config"; /** - * The whole table is fourteen narrow rows, so it is cached as a unit rather than + * The whole table is a small set of narrow rows, cached as a unit rather than * per key: `role-sync` touches the guild ID once per user and the Discord bot * reads it on every interaction, and neither should pay for a round trip. * * Invalidation is time-based, matching `liveRoleDiscordGateway`'s role-count - * cache. There is no admin UI yet — an officer changes these rows with SQL — so - * there is no in-process mutation to hook, and a sixty-second staleness window - * on a value that changes once a year is the right trade. When the Blade admin - * UI lands, its mutation must call {@link invalidateDiscordConfigCache}, and - * because `apps/cron`, `apps/tk`, and each Blade server instance hold separate - * caches, that call only clears the process that served the request; the others - * still converge within the TTL. + * cache. Blade's officer update calls {@link invalidateDiscordConfigCache} + * after committing. Because `apps/cron`, `apps/tk`, and each Blade server + * instance hold separate caches, that call only clears the process that served + * the request; the others still converge within the TTL. */ const CACHE_TTL_MS = 60_000; @@ -93,14 +90,13 @@ export function invalidateDiscordConfigCache(): void { /** * Resolves one setting to the Discord snowflake for the current environment. * - * Throws when the row is missing. That is deliberate: these IDs used to be - * compile-time constants, so the failure mode they never had was "silently - * undefined". A missing row means the backfill did not run, and a Discord call - * built from `undefined` would 404 somewhere far away from the cause. + * A missing row means the migration did not run and always throws. An explicit + * null value is an intentionally unconfigured destination; the public reader + * chooses whether to allow it or throw. */ -export async function getDiscordConfigId( +async function readDiscordConfigId( key: DISCORD.ConfigKey, -): Promise { +): Promise { const { rows } = await readSnapshot(); const row = rows.get(key); if (!row) { @@ -111,6 +107,29 @@ export async function getDiscordConfigId( return resolveDiscordConfigId(row, isProduction()); } +/** Required reads always yield a snowflake or throw, including for optional keys. */ +export async function getDiscordConfigId( + key: DISCORD.ConfigKey, +): Promise { + const id = await readDiscordConfigId(key); + if (id === null) { + throw new Error(`Discord config "${key}" is not configured.`); + } + return id; +} + +/** + * Explicitly permits an unconfigured destination. A missing row still throws: + * migration/setup errors must not silently disable alerts. Consumers should + * skip only the Discord send when this returns null, preserving ordinary work + * and error logging. + */ +export function getOptionalDiscordConfigId( + key: DISCORD.OptionalConfigKey, +): Promise { + return readDiscordConfigId(key); +} + /** Convenience for the hottest lookup in the codebase. */ export function getKnightHacksGuildId(): Promise { return getDiscordConfigId("guild"); diff --git a/packages/utils/src/tests/discord-config-consumers.test.ts b/packages/utils/src/tests/discord-config-consumers.test.ts index 4c9970712..2d56ff909 100644 --- a/packages/utils/src/tests/discord-config-consumers.test.ts +++ b/packages/utils/src/tests/discord-config-consumers.test.ts @@ -4,8 +4,8 @@ import { describe, expect, it } from "vitest"; import { DISCORD } from "@forge/consts"; /** - * `DISCORD.CONFIG_KEY_CONSUMERS` is hand-declared, and the console marks ten of - * the fourteen rows as read by nothing on the strength of it. That claim decays: + * `DISCORD.CONFIG_KEY_CONSUMERS` is hand-declared, and the console uses it to + * label settings as in use or unused. That claim decays: * the first commit that resolves `vip_role`, or deletes the last * `recruiting_channel` call, turns an officer-facing badge into a lie with no * other signal. So the classification is re-derived from the source tree here @@ -43,7 +43,8 @@ const excludedFile = new URL( * skipped. A scan that quietly ignored one would under-report the live set, * which is the direction that matters. */ -const CONFIG_ID_CALL = /getDiscordConfigId\(\s*(?:"([^"]*)"|'([^']*)')?/g; +const CONFIG_ID_CALL = + /get(?:Optional)?DiscordConfigId\(\s*(?:"([^"]*)"|'([^']*)')?/g; const GUILD_HELPER_CALL = "getKnightHacksGuildId("; interface WorkspaceScan { @@ -134,6 +135,12 @@ async function scanWorkspace(): Promise { const scan = await scanWorkspace(); describe("Discord config consumer catalog", () => { + it("includes optional-reader calls and flags computed optional keys", () => { + const source = `getOptionalDiscordConfigId("career_source_health_channel"); getOptionalDiscordConfigId(key);`; + expect( + [...source.matchAll(CONFIG_ID_CALL)].map((match) => match[1] ?? match[2]), + ).toEqual(["career_source_health_channel", undefined]); + }); it("finds the call sites it is supposed to compare against", () => { // Without this, a broken regex or a mis-resolved root would compare two // empty sets and pass while enforcing nothing. diff --git a/packages/utils/src/tests/discord-config.test.ts b/packages/utils/src/tests/discord-config.test.ts index 05cdf0341..8a96b7090 100644 --- a/packages/utils/src/tests/discord-config.test.ts +++ b/packages/utils/src/tests/discord-config.test.ts @@ -10,6 +10,7 @@ vi.mock("@forge/db/client", () => ({ const { getDiscordConfigId, + getOptionalDiscordConfigId, getKnightHacksGuildId, invalidateDiscordConfigCache, } = await import("../discord-config"); @@ -51,6 +52,75 @@ afterEach(() => { }); describe("Discord config read path", () => { + it("returns null only for an unconfigured optional destination, not a missing row", async () => { + selectSpy.mockResolvedValue([ + { + developmentId: null, + key: "career_source_health_channel", + productionId: null, + }, + ]); + await expect( + getOptionalDiscordConfigId("career_source_health_channel"), + ).resolves.toBeNull(); + await expect( + getDiscordConfigId("career_source_health_channel"), + ).rejects.toThrow(/not configured/); + expect(selectSpy).toHaveBeenCalledTimes(1); + invalidateDiscordConfigCache(); + selectSpy.mockResolvedValue([]); + await expect( + getOptionalDiscordConfigId("career_source_health_channel"), + ).rejects.toThrow(/has no row/); + }); + + it("applies environment selection and production fallback to optional reads", async () => { + selectSpy.mockResolvedValue([ + { + developmentId: "222222222222222222", + key: "career_source_health_channel", + productionId: "111111111111111111", + }, + ]); + setNodeEnv("production"); + await expect( + getOptionalDiscordConfigId("career_source_health_channel"), + ).resolves.toBe("111111111111111111"); + setNodeEnv("development"); + await expect( + getOptionalDiscordConfigId("career_source_health_channel"), + ).resolves.toBe("222222222222222222"); + selectSpy.mockResolvedValue([ + { + developmentId: null, + key: "career_source_health_channel", + productionId: "333333333333333333", + }, + ]); + invalidateDiscordConfigCache(); + await expect( + getOptionalDiscordConfigId("career_source_health_channel"), + ).resolves.toBe("333333333333333333"); + }); + + it("keeps a development-only optional destination out of production", async () => { + selectSpy.mockResolvedValue([ + { + developmentId: "222222222222222222", + key: "career_source_health_channel", + productionId: null, + }, + ]); + setNodeEnv("production"); + await expect( + getOptionalDiscordConfigId("career_source_health_channel"), + ).resolves.toBeNull(); + setNodeEnv("development"); + await expect( + getOptionalDiscordConfigId("career_source_health_channel"), + ).resolves.toBe("222222222222222222"); + }); + it("serves the development ID outside production and the production ID inside it", async () => { setNodeEnv("development"); await expect(getKnightHacksGuildId()).resolves.toBe("1151877367434850364"); @@ -148,4 +218,20 @@ describe("Discord config read path", () => { await expect(getKnightHacksGuildId()).resolves.toBe("1151877367434850364"); expect(selectSpy).toHaveBeenCalledTimes(2); }); + + it("keeps the strict reader from returning null for an unconfigured setting", async () => { + selectSpy.mockResolvedValue([ + { + developmentId: null, + key: "career_source_health_channel", + productionId: null, + }, + ]); + + await expect( + getDiscordConfigId("career_source_health_channel"), + ).rejects.toThrow( + /Discord config "career_source_health_channel" is not configured/, + ); + }); }); diff --git a/packages/validators/src/platform-config.ts b/packages/validators/src/platform-config.ts index 93371e5e3..3f17ae5aa 100644 --- a/packages/validators/src/platform-config.ts +++ b/packages/validators/src/platform-config.ts @@ -10,7 +10,7 @@ import { roleUuidSchema } from "./role-management"; // `knight_hacks_discord_config` rows and a Blade role's `knight_hacks_club_team_role` // classification. // -// Three of the columns these write are nullable, and `NULL` means something +// Some columns these write are nullable, and `NULL` means something // specific in each of them. A cleared `` yields `""`, so the coercion has // to happen here — the client binds `value={draft.field ?? ""}` and sends it raw. // @@ -76,8 +76,8 @@ export const discordConfigUpdateSchema = z .max(1_000) // The column is `text`; 1000 is an abuse bound rather than a schema mirror. .describe("What this setting controls and what breaks when it is wrong."), - productionId: configSnowflakeSchema.describe( - "Discord snowflake used when NODE_ENV is production.", + productionId: optionalConfigSnowflakeSchema.describe( + "Production Discord snowflake. Only the optional Career failure channel may be empty.", ), developmentId: optionalConfigSnowflakeSchema.describe( "Discord snowflake used outside production. Empty means reuse the production one.", @@ -91,7 +91,19 @@ export const discordConfigUpdateSchema = z "Confirms the officer accepts repointing every Discord consumer at another server.", ), }) - .strict(); + .strict() + .superRefine((input, ctx) => { + if ( + input.productionId === null && + !DISCORD.OPTIONAL_CONFIG_KEYS.some((key) => key === input.key) + ) { + ctx.addIssue({ + code: "custom", + message: "A production Discord ID is required for this setting.", + path: ["productionId"], + }); + } + }); export const clubClassificationUpdateSchema = z .object({