diff --git a/CLAUDE.md b/CLAUDE.md index 671113d..cf16eb1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,8 @@ npm run db:generate --prefix server # generate a migration into server/drizzle/ npm run db:studio --prefix server # Drizzle Studio npm run db:migrate --prefix server # apply pending migrations to the dev DB (local .env) npm run db:migrate:prod --prefix server # apply pending migrations to prod (server/.env.production, gitignored) +npm run db:backfill-users --prefix server # mirror Clerk's user list into the `users` table (idempotent) +npm run db:backfill-users:prod --prefix server # same, against prod ``` There is no test runner configured (`client/src/setupTests.ts` is a leftover). Verify changes by running the app. @@ -55,7 +57,9 @@ Algorithms in `server/src/algorithms/` self-register via `registerAlgorithm()` ( ### Auth -Clerk everywhere. Server: `clerkMiddleware()` globally + `requireAuth` per route (`getAuth(req).userId`). Client: `AuthGuard` gates the app shell and hydrates Clerk `unsafeMetadata` into the Redux `auth` slice. After mutating metadata server-side, call `await user.reload()` client-side or Redux stays stale. +Clerk owns **sessions only**. Server: `clerkMiddleware()` globally + `requireAuth` per route (`getAuth(req).userId`). Client: `AuthGuard` gates the app shell and hydrates the Redux `auth` slice from `GET /api/user/me`. + +User profile data (email/username cache, Sleeper link, synced leagues) lives in our own `users` table, keyed by the Clerk user id — see PLAYBOOK's "Users and auth ownership" for the three rules that keep a future auth migration cheap. Two things that used to be true and no longer are: nothing reads Clerk `unsafeMetadata`, and `await user.reload()` is not how you refresh a profile (use `useInvalidateCurrentUser()`). ### State split diff --git a/PLAYBOOK.md b/PLAYBOOK.md index 9f0d06a..c1cda8b 100644 --- a/PLAYBOOK.md +++ b/PLAYBOOK.md @@ -460,13 +460,31 @@ The provider pattern lives in `server/src/providers/`. To add a new platform: ## Auth & user state -User metadata flows: **Clerk `unsafeMetadata` → `AuthGuard` → Redux `auth` slice** +User profile flows: **`users` table → `GET /api/user/me` → `AuthGuard` → Redux `auth` slice** -- `AuthGuard` calls `setUser()` once on load and again whenever Clerk's `user` object updates (e.g. after `user.reload()`) -- After mutating metadata on the server, always call `await user.reload()` on the client to keep Clerk's cache fresh -- The `auth` slice holds: `user` (id, sleeperUsername, sleeperUserId, syncedLeagueIds), `selectedLeagueId`, `selectedYear` +- `AuthGuard` blocks render on `useCurrentUser()` (`client/src/hooks/useCurrentUser.ts`), then calls `setUser()`. It re-runs whenever that query is invalidated. +- After mutating the profile server-side, call `useInvalidateCurrentUser()` on the client. **Do not** use `await user.reload()` — that was the old Clerk-metadata cache-bust and no longer affects anything we read. +- The `auth` slice holds: `user` (id, username, email, sleeperUsername, sleeperUserId, syncedLeagueIds), `selectedLeagueId`, `selectedYear` - `selectedLeagueId` and `selectedYear` are persisted to `localStorage` (`huddle:selection`) by the store subscriber in `client/src/store/index.ts`, so the user's chosen league/season survives a refresh. +### Users and auth ownership + +Clerk owns **sessions only**. Everything else about a user is ours, in the `users` table. + +| Data | Owner | Notes | +|---|---|---| +| Session / JWT / sign-in UI | Clerk | `clerkMiddleware()` + `requireAuth`, `getToken()` client-side | +| `email`, `username` | Clerk, **cached** by us | Refreshed by `usersService.ensureUser` on a 24h TTL | +| `sleeperUsername`, `sleeperUserId`, `syncedLeagueIds` | **Us** | Formerly Clerk `unsafeMetadata`; migrated in `0012` | + +Three rules keep a future auth migration cheap — they exist because the alternative is a rewrite: + +1. **`users.id` is the Clerk user id, and stays that way.** All 18 user-referencing columns across 11 tables store that same string. Swapping providers later means issuing our own JWTs with the same `sub`, not renumbering the database. Never migrate these to fresh uuids. +2. **Never call Clerk for display names.** Use `usersService.getUserSummaries(ids)` — one SQL round-trip. `huddleRoutes` used to make a `clerkClient.users.getUserList()` HTTP call inside five request handlers; that's what this replaced. +3. **Never `DELETE` a `users` row.** Every FK is `ON DELETE RESTRICT` on purpose — forum posts, votes and survey answers are league history that must outlive an account. To delete a user, blank the identity fields and set `is_placeholder`. + +`scripts/backfill-users.mjs` (`npm run db:backfill-users --prefix server`) mirrors Clerk's user list into `users`. It's idempotent and safe to re-run on a schedule — identity fields are always refreshed, but Sleeper fields are only filled when ours are empty, so it can never roll back a user's later edits. Run it after any migration that adds a new user-referencing column, and add that column to the `USER_ID_COLUMNS` list at the top of the script. + --- ## Database changes @@ -502,9 +520,26 @@ This replaces the old `psql -f ...` workflow — no `psql` install required, and | Renaming a column | Drizzle sees drop + add and generates `DROP`/`ADD`, silently discarding data. Resolve as a rename, or hand-edit to `ALTER ... RENAME`. | | Adding `NOT NULL` | Fails on a populated table unless you give it a default. | | Removing an enum value | Postgres can add values but not drop them; requires recreating the type. | +| Changing a column's type | Drizzle emits a bare `SET DATA TYPE`, which Postgres rejects whenever no implicit cast exists (e.g. `integer` → `boolean` in `0013`). Hand-edit to `DROP DEFAULT` → `SET DATA TYPE ... USING (...)` → `SET DEFAULT`. | +| Renaming a table | Needs a TTY — `drizzle-kit generate` prompts "created or renamed?" per table, and answering "create" produces a `DROP`/`CREATE` that discards the data. For a bulk rename it's safer to answer "create" deliberately (the *snapshot* is correct either way, since it records end state) and then replace the generated SQL with `ALTER TABLE ... RENAME`. Constraint and index names don't follow the rename; see "Table naming". | | Deploys | Never auto-migrate on boot — Vercel serverless would race on every cold start. Migrations are always run deliberately. | -Legacy artifact: the primary key on `huddles` is still named `groups_pkey`, because `0001` renamed the table but Postgres keeps constraint names. Harmless; rename if you're ever touching that table. +### Table naming + +> **Tables are named for what they hold. No product-name prefix.** + +`commissioners`, `polls`, `survey_responses` — not `huddle_commissioners`, `huddle_polls`. The `huddle_` prefix that 19 tables used to carry said nothing: every table in this database belongs to Huddle. It cost real ergonomics — `\dt` and editor autocomplete on `hud` returned almost the whole schema — and it implied a scoping rule it didn't keep (`team_claims` and `side_bets` carried `huddle_id` without the prefix; the poll/survey child tables carried the prefix without a `huddle_id`). `0015` removed it from all 21 remaining tables. + +The one structural distinction worth knowing isn't in the names: **`users` is global; everything else is reachable from a huddle**, directly via `huddle_id` or transitively through a parent (`poll_options` → `polls` → huddle). If you add auth tables later (`sessions`, `accounts`), they join `users` on the global side. + +The same rule applies to TypeScript types (`Award`, `Poll`, `SurveyResponse`, not `HuddleAward`), on both sides of the wire — the client had already settled on the unprefixed names, so `0015`'s follow-up brought the server in line rather than the other way round. Keep the `Huddle` prefix only where it's doing real work: `Huddle` itself, `HuddleClaim`, `HuddleDetailResponse`, `HuddleMemberStatus`. Watch for collisions with lucide-react icons when you strip a prefix — `CommissionerPage` imports `Award as LucideAward` for exactly this reason. + +**Renaming a table is a three-part job, and Postgres only does the first part.** `ALTER TABLE ... RENAME TO` leaves every constraint and index still carrying the old name, and Drizzle doesn't track those names, so they will never show up in a generated diff — they just rot until something collides. `0015` renamed 115 objects for 21 tables. Generate the statements from the live schema (`pg_constraint` + `pg_index`) rather than by hand. + +Two traps that cost a follow-up migration (`0016`) the first time: + +- **The 63-byte identifier limit.** Postgres silently truncates longer constraint names, so `huddle_survey_answer_options_option_id_huddle_survey_options_id_fk` lost its `_fk` suffix on creation. Renaming carried the truncation forward, leaving a name Drizzle's snapshot didn't expect. +- **Verify against the snapshot afterwards.** `db:generate` reporting "no schema changes" only proves `schema.ts` matches the snapshot — not that the *database* does. Diff the snapshot's `foreignKeys`/`indexes` names against `pg_constraint`/`pg_indexes` directly; that's what caught the truncation. --- diff --git a/client/src/components/AccountModal.tsx b/client/src/components/AccountModal.tsx index 8c08427..d5f4121 100644 --- a/client/src/components/AccountModal.tsx +++ b/client/src/components/AccountModal.tsx @@ -26,7 +26,7 @@ import { type ReactNode, } from "react"; import { Link } from "react-router-dom"; -import { UserProfile, useAuth, useUser } from "@clerk/clerk-react"; +import { UserProfile, useAuth } from "@clerk/clerk-react"; import { ChevronRight } from "lucide-react"; import { Plug, X } from "lucide-react"; import axios from "axios"; @@ -36,6 +36,7 @@ import { setSyncedLeagueIds, setSelectedLeague, } from "../store/slices/authSlice"; +import { useInvalidateCurrentUser } from "../hooks/useCurrentUser"; import { Button } from "./ui/button"; interface AccountModalContextValue { @@ -148,9 +149,9 @@ function AccountModal({ onClose, initialTab }: { onClose: () => void; initialTab function IntegrationsPage() { const { getToken } = useAuth(); - const { user } = useUser(); const dispatch = useAppDispatch(); const queryClient = useQueryClient(); + const invalidateCurrentUser = useInvalidateCurrentUser(); const sleeperUsername = useAppSelector( (state) => state.auth.user?.sleeperUsername, ); @@ -179,8 +180,8 @@ function IntegrationsPage() { setErrorMsg(""); try { await patchSleeperUsername(input.trim()); - // Reload Clerk user — AuthGuard's useEffect will re-hydrate Redux with fresh metadata - await user?.reload(); + // Refetch /api/user/me — AuthGuard's useEffect re-hydrates Redux from it. + await invalidateCurrentUser(); setInput(""); setStatus("success"); } catch (err: unknown) { @@ -202,8 +203,8 @@ function IntegrationsPage() { dispatch(setSyncedLeagueIds([])); dispatch(setSelectedLeague(null)); queryClient.removeQueries({ queryKey: ["sleeper-leagues-all"] }); - // Sync Clerk cache so AuthGuard re-hydrates with cleared metadata - await user?.reload(); + // Refetch /api/user/me so AuthGuard re-hydrates with the cleared link. + await invalidateCurrentUser(); setStatus("idle"); } catch (err: unknown) { setStatus("error"); diff --git a/client/src/components/auth/AuthGuard.tsx b/client/src/components/auth/AuthGuard.tsx index 2b19f29..515bb49 100644 --- a/client/src/components/auth/AuthGuard.tsx +++ b/client/src/components/auth/AuthGuard.tsx @@ -4,61 +4,61 @@ * Responsibilities: * 1. Wait for Clerk to finish loading the session (spinner while loading). * 2. Redirect to /sign-in if the user isn't signed in. - * 3. Mirror Clerk's user (including the Sleeper metadata we tuck into - * `unsafeMetadata`) into the Redux `auth` slice so the rest of the - * app can read it synchronously via `useAppSelector`. + * 3. Fetch the user's Huddle profile from `GET /api/user/me` and mirror it + * into the Redux `auth` slice so the rest of the app can read it + * synchronously via `useAppSelector`. * 4. Hold render until Redux is hydrated — otherwise the first paint * would fire queries with `sleeperUserId = null` and waste a * no-op TanStack Query round-trip. * - * Why `unsafeMetadata` and not a separate DB? - * Clerk lets us stash arbitrary JSON on the user record and read it back - * without an extra fetch. We use it for the Sleeper handle and the - * user's list of synced leagueIds — the data is small, user-owned, and - * non-sensitive. Anything bigger lives in our Postgres tables. + * Why our API and not Clerk's `unsafeMetadata`? + * It used to be Clerk metadata — small, user-owned JSON readable without an + * extra fetch. The cost was that the Sleeper link and synced-league list + * were the only product state we couldn't recover without a third-party + * export. They now live in our `users` table. Clerk still owns the session; + * it no longer owns any product data. * - * IMPORTANT: After mutating `unsafeMetadata` on the server, always call - * `await user.reload()` on the client to invalidate Clerk's cache — - * otherwise the next `useUser()` read returns stale data and this guard - * dispatches the old values into Redux. + * This is also the request that *creates* the user's row (see the route's + * comment), so it deliberately blocks render: everything downstream assumes a + * `users` row exists, and the foreign keys enforce it. */ import { type ReactNode, useEffect } from "react"; import { useUser, useAuth, RedirectToSignIn } from "@clerk/clerk-react"; import { useAppDispatch, useAppSelector } from "../../store/hooks"; import { setUser, clearUser } from "../../store/slices/authSlice"; +import { useCurrentUser } from "../../hooks/useCurrentUser"; interface AuthGuardProps { children: ReactNode; } export function AuthGuard({ children }: AuthGuardProps) { - const { isLoaded, isSignedIn, user } = useUser(); + const { isLoaded, isSignedIn } = useUser(); const { isLoaded: authLoaded } = useAuth(); const dispatch = useAppDispatch(); const reduxUser = useAppSelector((state) => state.auth.user); + const { data: profile, isError, error } = useCurrentUser(); - // Keep Redux in lockstep with Clerk's user object. Fires on initial load - // and again any time Clerk publishes an updated `user` (e.g. after the - // app calls `user.reload()` post-metadata write). + // Keep Redux in lockstep with our profile endpoint. Fires on initial load + // and again whenever the query is invalidated (e.g. after linking Sleeper). useEffect(() => { if (!isLoaded || !authLoaded) return; - if (isSignedIn && user) { - const meta = user.unsafeMetadata ?? {}; + if (isSignedIn && profile) { dispatch( setUser({ - id: user.id, - username: user.username, - email: user.primaryEmailAddress?.emailAddress ?? "", - sleeperUsername: (meta.sleeperUsername as string) ?? null, - sleeperUserId: (meta.sleeperUserId as string) ?? null, - syncedLeagueIds: (meta.syncedLeagueIds as string[]) ?? [], + id: profile.id, + username: profile.username, + email: profile.email ?? "", + sleeperUsername: profile.sleeperUsername, + sleeperUserId: profile.sleeperUserId, + syncedLeagueIds: profile.syncedLeagueIds, }), ); - } else { + } else if (!isSignedIn) { dispatch(clearUser()); } - }, [isLoaded, authLoaded, isSignedIn, user, dispatch]); + }, [isLoaded, authLoaded, isSignedIn, profile, dispatch]); if (!isLoaded || !authLoaded) { return ; @@ -66,10 +66,31 @@ export function AuthGuard({ children }: AuthGuardProps) { if (!isSignedIn) return ; - // Hold render until Redux is hydrated — prevents queries firing with null sleeperUserId. - // Without this we'd briefly mount children with `useAppSelector(...)` returning the - // initial state, which would cause every Sleeper hook to fire its `enabled: !!id` guard - // twice (once with null, once with the real id once Redux catches up). + // The profile request failing means we have no user row and no Sleeper link, + // so rendering the app would just produce a wall of broken widgets. Surface + // it instead of spinning forever. + if (isError) { + return ( +
+

Couldn't load your account

+

+ {error instanceof Error ? error.message : "Please try again."} +

+ +
+ ); + } + + // Hold render until Redux is hydrated — prevents queries firing with null + // sleeperUserId. Without this we'd briefly mount children with + // `useAppSelector(...)` returning the initial state, which would cause every + // Sleeper hook to fire its `enabled: !!id` guard twice (once with null, once + // with the real id once Redux catches up). if (!reduxUser) return ; return <>{children}; diff --git a/client/src/hooks/useCurrentUser.ts b/client/src/hooks/useCurrentUser.ts new file mode 100644 index 0000000..3218fa7 --- /dev/null +++ b/client/src/hooks/useCurrentUser.ts @@ -0,0 +1,59 @@ +/** + * The signed-in user's Huddle profile, from our own API. + * + * This replaces reading Clerk's `unsafeMetadata` on the client. Clerk still + * owns the *session* (`useAuth`/`useUser` for tokens and sign-in state), but + * the Sleeper link and synced-league list now come from our `users` table via + * `GET /api/user/me`. + * + * That endpoint is also what creates the user's row, so it must succeed before + * anything else calls the API — AuthGuard blocks render on it for exactly that + * reason. Don't add a competing fetch of this data elsewhere; read the Redux + * `auth.user` mirror instead, or reuse this hook's cache. + * + * Stale time is Infinity: this only changes when *we* change it, so it's + * refreshed by explicit invalidation (see `useInvalidateCurrentUser`) rather + * than by polling. This is the same reasoning as the 24h player-dictionary + * tier in `useSleeper.ts`. + */ +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useAuth } from "@clerk/clerk-react"; +import axios from "axios"; + +export interface CurrentUser { + id: string; + email: string | null; + username: string | null; + sleeperUsername: string | null; + sleeperUserId: string | null; + syncedLeagueIds: string[]; +} + +export const currentUserKey = ["current-user"] as const; + +export function useCurrentUser() { + const { getToken, isSignedIn } = useAuth(); + + return useQuery({ + queryKey: currentUserKey, + enabled: !!isSignedIn, + staleTime: Infinity, + queryFn: async (): Promise => { + const token = await getToken(); + const res = await axios.get<{ user: CurrentUser }>("/api/user/me", { + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }); + return res.data.user; + }, + }); +} + +/** + * Call after any mutation that changes the user's profile. Replaces the old + * `await user.reload()` dance, which existed only to bust Clerk's metadata + * cache. + */ +export function useInvalidateCurrentUser() { + const queryClient = useQueryClient(); + return () => queryClient.invalidateQueries({ queryKey: currentUserKey }); +} diff --git a/client/src/hooks/useHuddles.ts b/client/src/hooks/useHuddles.ts index 96d4178..384df37 100644 --- a/client/src/hooks/useHuddles.ts +++ b/client/src/hooks/useHuddles.ts @@ -546,7 +546,7 @@ export function useRemoveClaim() { // ── Announcements ───────────────────────────────────────────────────────────── -import type { HuddleAnnouncement } from "../types/huddle"; +import type { Announcement } from "../types/huddle"; /** Fetches announcements for a huddle, newest first. */ export function useAnnouncements(huddleId: string | null) { @@ -555,7 +555,7 @@ export function useAnnouncements(huddleId: string | null) { queryKey: ["huddle-announcements", huddleId], queryFn: async () => { const token = await getToken(); - const res = await axios.get<{ announcements: HuddleAnnouncement[] }>( + const res = await axios.get<{ announcements: Announcement[] }>( `/api/huddles/${huddleId}/announcements`, { headers: authHeader(token) }, ); @@ -572,7 +572,7 @@ export function useCreateAnnouncement() { return useMutation({ mutationFn: async (input: { huddleId: string; title: string; body: string }) => { const token = await getToken(); - const res = await axios.post<{ announcement: HuddleAnnouncement }>( + const res = await axios.post<{ announcement: Announcement }>( `/api/huddles/${input.huddleId}/announcements`, { title: input.title, body: input.body }, { headers: authHeader(token) }, @@ -603,7 +603,7 @@ export function useDeleteAnnouncement() { // ── Awards ──────────────────────────────────────────────────────────────────── -import type { HuddleAward } from "../types/huddle"; +import type { Award } from "../types/huddle"; export function useAwards(huddleId: string | null, rosterId?: number) { const { getToken } = useAuth(); @@ -612,7 +612,7 @@ export function useAwards(huddleId: string | null, rosterId?: number) { queryFn: async () => { const token = await getToken(); const params = rosterId !== undefined ? { rosterId } : {}; - const res = await axios.get<{ awards: HuddleAward[] }>( + const res = await axios.get<{ awards: Award[] }>( `/api/huddles/${huddleId}/awards`, { params, headers: authHeader(token) }, ); @@ -637,7 +637,7 @@ export function useCreateAward() { season?: string; }) => { const token = await getToken(); - const res = await axios.post<{ award: HuddleAward }>( + const res = await axios.post<{ award: Award }>( `/api/huddles/${input.huddleId}/awards`, { rosterId: input.rosterId, glyph: input.glyph, color: input.color, title: input.title, description: input.description, season: input.season }, @@ -729,7 +729,7 @@ export function useUpdateAward() { season?: string; }) => { const token = await getToken(); - const res = await axios.patch<{ award: HuddleAward }>( + const res = await axios.patch<{ award: Award }>( `/api/huddles/${input.huddleId}/awards/${input.awardId}`, { rosterId: input.rosterId, glyph: input.glyph, color: input.color, title: input.title, description: input.description, season: input.season }, diff --git a/client/src/pages/CommissionerPage.tsx b/client/src/pages/CommissionerPage.tsx index 846ef43..1b884e3 100644 --- a/client/src/pages/CommissionerPage.tsx +++ b/client/src/pages/CommissionerPage.tsx @@ -19,7 +19,9 @@ */ import { useState, useMemo } from "react"; import { Navigate, useNavigate } from "react-router-dom"; -import { Megaphone, DollarSign, Trophy, Award, Plus, Trash2, Timer, BarChart3 } from "lucide-react"; +// lucide's `Award` is aliased: `Award` is the domain type imported below, and +// `AwardIcon` is already taken by the award-glyph asset type from useHuddles. +import { Megaphone, DollarSign, Trophy, Award as LucideAward, Plus, Trash2, Timer, BarChart3 } from "lucide-react"; import { useAppSelector } from "../store/hooks"; import { useLeagueUsers, useLeagueRosters } from "../hooks/useSleeper"; import { @@ -63,7 +65,7 @@ import type { Roster, TeamUser } from "../types/fantasy"; import type { CommissionerSummary, HuddleClaimSummary, - HuddleAward, + Award, UserSummary, ActiveTrophies, } from "../types/huddle"; @@ -1295,7 +1297,7 @@ function AwardBadge({ deleting, iconSvg, }: { - award: HuddleAward; + award: Award; teamName: string; onEdit: () => void; onDelete: () => void; @@ -1493,7 +1495,7 @@ function TrophyRoomPanel({ const [rosterId, setRosterId] = useState(""); const [season, setSeason] = useState(""); - function startEdit(a: HuddleAward) { + function startEdit(a: Award) { setEditingId(a.id); setGlyph(a.glyph); setColor(a.color); @@ -1995,7 +1997,7 @@ export function CommissionerPage() { /> ) : (
- {awards.map((a: HuddleAward) => ( + {awards.map((a: Award) => (
}) { +function HuddleAwardsStrip({ awards, iconMap }: { awards: Award[]; iconMap: Map }) { if (awards.length === 0) return null; return (
diff --git a/client/src/types/huddle.ts b/client/src/types/huddle.ts index 6fe139f..4dc09a1 100644 --- a/client/src/types/huddle.ts +++ b/client/src/types/huddle.ts @@ -67,7 +67,7 @@ export interface HuddleDetailResponse { myClaim: { id: string; rosterId: number; status: ClaimStatus } | null; } -export interface HuddleAnnouncement { +export interface Announcement { id: string; huddleId: string; authorId: string; @@ -289,7 +289,7 @@ export interface SurveyAnswerInput { optionIds?: string[]; } -export interface HuddleAward { +export interface Award { id: string; huddleId: string; rosterId: number; diff --git a/client/src/widgets/dashboard/Announcements.tsx b/client/src/widgets/dashboard/Announcements.tsx index 05b540d..ecafd2b 100644 --- a/client/src/widgets/dashboard/Announcements.tsx +++ b/client/src/widgets/dashboard/Announcements.tsx @@ -8,11 +8,11 @@ */ import { useSelectedLeagueHuddle, useAnnouncements } from "../../hooks/useHuddles"; import { SectionHead } from "./_shared"; -import type { HuddleAnnouncement } from "../../types/huddle"; +import type { Announcement } from "../../types/huddle"; // ---- Single announcement card ---- -function AnnouncementCard({ item }: { item: HuddleAnnouncement }) { +function AnnouncementCard({ item }: { item: Announcement }) { const date = new Date(item.createdAt).toLocaleDateString("en-US", { month: "short", day: "numeric", diff --git a/server/drizzle/0011_sticky_shockwave.sql b/server/drizzle/0011_sticky_shockwave.sql new file mode 100644 index 0000000..496d33e --- /dev/null +++ b/server/drizzle/0011_sticky_shockwave.sql @@ -0,0 +1,15 @@ +CREATE TABLE "users" ( + "id" text PRIMARY KEY NOT NULL, + "email" text, + "username" text, + "sleeper_username" text, + "sleeper_user_id" text, + "synced_league_ids" text[] DEFAULT '{}'::text[] NOT NULL, + "is_placeholder" boolean DEFAULT false NOT NULL, + "identity_synced_at" timestamp with time zone DEFAULT now() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE INDEX "users_email_idx" ON "users" USING btree ("email");--> statement-breakpoint +CREATE INDEX "users_username_idx" ON "users" USING btree ("username"); \ No newline at end of file diff --git a/server/drizzle/0012_green_valkyrie.sql b/server/drizzle/0012_green_valkyrie.sql new file mode 100644 index 0000000..f050589 --- /dev/null +++ b/server/drizzle/0012_green_valkyrie.sql @@ -0,0 +1,53 @@ +-- Hand-added seed step (the ALTER TABLEs below are Drizzle-generated). +-- +-- Every user id already referenced by our data needs a `users` row before the +-- foreign keys can be added. `db:migrate` applies 0011 and 0012 in the same +-- run, so this cannot rely on scripts/backfill-users.mjs having been run in +-- between — it seeds minimal placeholder rows itself, from the data alone, and +-- is therefore guaranteed to satisfy every constraint that follows. +-- +-- Run `npm run db:backfill-users --prefix server` afterwards to fill these in +-- with real emails/usernames and the Sleeper links from Clerk's metadata; that +-- script clears is_placeholder for every id Clerk still knows about. Whatever +-- stays flagged is a deleted account we're keeping for attribution. +INSERT INTO "users" ("id", "is_placeholder") +SELECT DISTINCT "id", true FROM ( + SELECT "user_id" AS id FROM "huddle_commissioners" + UNION SELECT "added_by" FROM "huddle_commissioners" + UNION SELECT "user_id" FROM "team_claims" + UNION SELECT "decided_by" FROM "team_claims" + UNION SELECT "author_id" FROM "huddle_announcements" + UNION SELECT "marked_by" FROM "huddle_dues_payments" + UNION SELECT "granted_by" FROM "huddle_awards" + UNION SELECT "proposer_id" FROM "side_bets" + UNION SELECT "opponent_id" FROM "side_bets" + UNION SELECT "winner_id" FROM "side_bets" + UNION SELECT "author_id" FROM "huddle_forum_topics" + UNION SELECT "deleted_by" FROM "huddle_forum_topics" + UNION SELECT "author_id" FROM "huddle_forum_replies" + UNION SELECT "deleted_by" FROM "huddle_forum_replies" + UNION SELECT "author_id" FROM "huddle_polls" + UNION SELECT "user_id" FROM "huddle_poll_votes" + UNION SELECT "author_id" FROM "huddle_surveys" + UNION SELECT "user_id" FROM "huddle_survey_responses" +) refs +WHERE "id" IS NOT NULL +ON CONFLICT ("id") DO NOTHING;--> statement-breakpoint +ALTER TABLE "huddle_announcements" ADD CONSTRAINT "huddle_announcements_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_awards" ADD CONSTRAINT "huddle_awards_granted_by_users_id_fk" FOREIGN KEY ("granted_by") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_commissioners" ADD CONSTRAINT "huddle_commissioners_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_commissioners" ADD CONSTRAINT "huddle_commissioners_added_by_users_id_fk" FOREIGN KEY ("added_by") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_dues_payments" ADD CONSTRAINT "huddle_dues_payments_marked_by_users_id_fk" FOREIGN KEY ("marked_by") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_forum_replies" ADD CONSTRAINT "huddle_forum_replies_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_forum_replies" ADD CONSTRAINT "huddle_forum_replies_deleted_by_users_id_fk" FOREIGN KEY ("deleted_by") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_forum_topics" ADD CONSTRAINT "huddle_forum_topics_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_forum_topics" ADD CONSTRAINT "huddle_forum_topics_deleted_by_users_id_fk" FOREIGN KEY ("deleted_by") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_poll_votes" ADD CONSTRAINT "huddle_poll_votes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_polls" ADD CONSTRAINT "huddle_polls_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_survey_responses" ADD CONSTRAINT "huddle_survey_responses_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_surveys" ADD CONSTRAINT "huddle_surveys_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "side_bets" ADD CONSTRAINT "side_bets_proposer_id_users_id_fk" FOREIGN KEY ("proposer_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "side_bets" ADD CONSTRAINT "side_bets_opponent_id_users_id_fk" FOREIGN KEY ("opponent_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "side_bets" ADD CONSTRAINT "side_bets_winner_id_users_id_fk" FOREIGN KEY ("winner_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "team_claims" ADD CONSTRAINT "team_claims_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "team_claims" ADD CONSTRAINT "team_claims_decided_by_users_id_fk" FOREIGN KEY ("decided_by") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action; \ No newline at end of file diff --git a/server/drizzle/0013_hot_cannonball.sql b/server/drizzle/0013_hot_cannonball.sql new file mode 100644 index 0000000..d359bcc --- /dev/null +++ b/server/drizzle/0013_hot_cannonball.sql @@ -0,0 +1,48 @@ +ALTER TABLE "side_bets" RENAME TO "huddle_side_bets";--> statement-breakpoint +ALTER TABLE "team_claims" RENAME TO "huddle_team_claims";--> statement-breakpoint +ALTER TABLE "huddle_side_bets" DROP CONSTRAINT "side_bets_huddle_id_huddles_id_fk"; +--> statement-breakpoint +ALTER TABLE "huddle_side_bets" DROP CONSTRAINT "side_bets_proposer_id_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "huddle_side_bets" DROP CONSTRAINT "side_bets_opponent_id_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "huddle_side_bets" DROP CONSTRAINT "side_bets_winner_id_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "huddle_team_claims" DROP CONSTRAINT "team_claims_huddle_id_huddles_id_fk"; +--> statement-breakpoint +ALTER TABLE "huddle_team_claims" DROP CONSTRAINT "team_claims_user_id_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "huddle_team_claims" DROP CONSTRAINT "team_claims_decided_by_users_id_fk"; +--> statement-breakpoint +DROP INDEX "side_bets_huddle_idx";--> statement-breakpoint +DROP INDEX "side_bets_proposer_idx";--> statement-breakpoint +DROP INDEX "side_bets_opponent_idx";--> statement-breakpoint +DROP INDEX "team_claims_huddle_roster_approved_uniq";--> statement-breakpoint +DROP INDEX "team_claims_huddle_user_approved_uniq";--> statement-breakpoint +DROP INDEX "team_claims_huddle_idx";--> statement-breakpoint +DROP INDEX "team_claims_user_idx";--> statement-breakpoint +-- Hand-edited: Drizzle emitted a bare `SET DATA TYPE boolean`, which Postgres +-- rejects — integer has no implicit cast to boolean, and the existing +-- `DEFAULT 1` can't be cast either. Drop the default, convert with an explicit +-- USING, then restore it. (PLAYBOOK, "Sharp edges".) +ALTER TABLE "huddle_active_trophies" ALTER COLUMN "enabled" DROP DEFAULT;--> statement-breakpoint +ALTER TABLE "huddle_active_trophies" ALTER COLUMN "enabled" SET DATA TYPE boolean USING ("enabled" <> 0);--> statement-breakpoint +ALTER TABLE "huddle_active_trophies" ALTER COLUMN "enabled" SET DEFAULT true;--> statement-breakpoint +-- Hand-added: legacy constraint name left behind when 0001 renamed `groups` +-- to `huddles`. Postgres keeps constraint names across a table rename, and +-- Drizzle doesn't track them, so this never appears in a generated diff. +ALTER INDEX IF EXISTS "groups_pkey" RENAME TO "huddles_pkey";--> statement-breakpoint +ALTER TABLE "huddle_side_bets" ADD CONSTRAINT "huddle_side_bets_huddle_id_huddles_id_fk" FOREIGN KEY ("huddle_id") REFERENCES "public"."huddles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_side_bets" ADD CONSTRAINT "huddle_side_bets_proposer_id_users_id_fk" FOREIGN KEY ("proposer_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_side_bets" ADD CONSTRAINT "huddle_side_bets_opponent_id_users_id_fk" FOREIGN KEY ("opponent_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_side_bets" ADD CONSTRAINT "huddle_side_bets_winner_id_users_id_fk" FOREIGN KEY ("winner_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_team_claims" ADD CONSTRAINT "huddle_team_claims_huddle_id_huddles_id_fk" FOREIGN KEY ("huddle_id") REFERENCES "public"."huddles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_team_claims" ADD CONSTRAINT "huddle_team_claims_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "huddle_team_claims" ADD CONSTRAINT "huddle_team_claims_decided_by_users_id_fk" FOREIGN KEY ("decided_by") REFERENCES "public"."users"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "huddle_side_bets_huddle_idx" ON "huddle_side_bets" USING btree ("huddle_id");--> statement-breakpoint +CREATE INDEX "huddle_side_bets_proposer_idx" ON "huddle_side_bets" USING btree ("huddle_id","proposer_id");--> statement-breakpoint +CREATE INDEX "huddle_side_bets_opponent_idx" ON "huddle_side_bets" USING btree ("huddle_id","opponent_id");--> statement-breakpoint +CREATE UNIQUE INDEX "huddle_team_claims_huddle_roster_approved_uniq" ON "huddle_team_claims" USING btree ("huddle_id","roster_id") WHERE "huddle_team_claims"."status" = 'approved';--> statement-breakpoint +CREATE UNIQUE INDEX "huddle_team_claims_huddle_user_approved_uniq" ON "huddle_team_claims" USING btree ("huddle_id","user_id") WHERE "huddle_team_claims"."status" = 'approved';--> statement-breakpoint +CREATE INDEX "huddle_team_claims_huddle_idx" ON "huddle_team_claims" USING btree ("huddle_id");--> statement-breakpoint +CREATE INDEX "huddle_team_claims_user_idx" ON "huddle_team_claims" USING btree ("user_id"); \ No newline at end of file diff --git a/server/drizzle/0014_rename_legacy_pkeys.sql b/server/drizzle/0014_rename_legacy_pkeys.sql new file mode 100644 index 0000000..10eae54 --- /dev/null +++ b/server/drizzle/0014_rename_legacy_pkeys.sql @@ -0,0 +1,10 @@ +-- Custom migration (`drizzle-kit generate --custom`), so the snapshot is +-- written correctly even though Drizzle sees no schema change here. +-- +-- Postgres keeps constraint names across `ALTER TABLE ... RENAME TO`, so 0013's +-- table renames left the old names behind on the primary keys — the same wart +-- `groups_pkey` was on `huddles`. Drizzle doesn't track PK constraint names, so +-- these will never show up in a generated diff; they have to be renamed by hand +-- or not at all. +ALTER INDEX IF EXISTS "team_claims_pkey" RENAME TO "huddle_team_claims_pkey";--> statement-breakpoint +ALTER INDEX IF EXISTS "side_bets_pkey" RENAME TO "huddle_side_bets_pkey"; diff --git a/server/drizzle/0015_drop_huddle_prefix.sql b/server/drizzle/0015_drop_huddle_prefix.sql new file mode 100644 index 0000000..727a9a8 --- /dev/null +++ b/server/drizzle/0015_drop_huddle_prefix.sql @@ -0,0 +1,133 @@ +-- Hand-written, replacing Drizzle's generated SQL for this step. +-- +-- Drizzle prompts "created or renamed?" once per table and cannot be answered +-- reliably from a script for 21 tables, so it was answered "create" — which +-- produces a correct *snapshot* (the snapshot records the end state, not the +-- operations) alongside a DROP/CREATE migration that would discard every row. +-- That SQL was replaced with the renames below, generated from the live schema +-- so no index or constraint is missed. +-- +-- Naming rule this establishes: tables are named for what they hold. The +-- `huddle_` prefix said nothing — every table in the database belongs to +-- Huddle — while making `\dt` and autocomplete useless. See PLAYBOOK, +-- "Table naming". +-- +-- Postgres does not rename constraints or indexes when a table is renamed, so +-- each one is renamed explicitly; otherwise the next `db:generate` diffs +-- against names that no longer match the snapshot. + +ALTER TABLE "huddle_active_trophies" RENAME TO "active_trophies";--> statement-breakpoint +ALTER TABLE "active_trophies" RENAME CONSTRAINT "huddle_active_trophies_huddle_id_huddles_id_fk" TO "active_trophies_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "active_trophies" RENAME CONSTRAINT "huddle_active_trophies_huddle_id_trophy_type_pk" TO "active_trophies_huddle_id_trophy_type_pk";--> statement-breakpoint +ALTER TABLE "huddle_announcements" RENAME TO "announcements";--> statement-breakpoint +ALTER TABLE "announcements" RENAME CONSTRAINT "huddle_announcements_author_id_users_id_fk" TO "announcements_author_id_users_id_fk";--> statement-breakpoint +ALTER TABLE "announcements" RENAME CONSTRAINT "huddle_announcements_huddle_id_huddles_id_fk" TO "announcements_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "announcements" RENAME CONSTRAINT "huddle_announcements_pkey" TO "announcements_pkey";--> statement-breakpoint +ALTER INDEX "huddle_announcements_huddle_idx" RENAME TO "announcements_huddle_idx";--> statement-breakpoint +ALTER TABLE "huddle_awards" RENAME TO "awards";--> statement-breakpoint +ALTER TABLE "awards" RENAME CONSTRAINT "huddle_awards_granted_by_users_id_fk" TO "awards_granted_by_users_id_fk";--> statement-breakpoint +ALTER TABLE "awards" RENAME CONSTRAINT "huddle_awards_huddle_id_huddles_id_fk" TO "awards_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "awards" RENAME CONSTRAINT "huddle_awards_pkey" TO "awards_pkey";--> statement-breakpoint +ALTER INDEX "huddle_awards_huddle_idx" RENAME TO "awards_huddle_idx";--> statement-breakpoint +ALTER INDEX "huddle_awards_huddle_roster_idx" RENAME TO "awards_huddle_roster_idx";--> statement-breakpoint +ALTER TABLE "huddle_commissioners" RENAME TO "commissioners";--> statement-breakpoint +ALTER TABLE "commissioners" RENAME CONSTRAINT "huddle_commissioners_added_by_users_id_fk" TO "commissioners_added_by_users_id_fk";--> statement-breakpoint +ALTER TABLE "commissioners" RENAME CONSTRAINT "huddle_commissioners_huddle_id_huddles_id_fk" TO "commissioners_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "commissioners" RENAME CONSTRAINT "huddle_commissioners_huddle_id_user_id_pk" TO "commissioners_huddle_id_user_id_pk";--> statement-breakpoint +ALTER TABLE "commissioners" RENAME CONSTRAINT "huddle_commissioners_user_id_users_id_fk" TO "commissioners_user_id_users_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_commissioners_user_idx" RENAME TO "commissioners_user_idx";--> statement-breakpoint +ALTER TABLE "huddle_countdown_config" RENAME TO "countdown_config";--> statement-breakpoint +ALTER TABLE "countdown_config" RENAME CONSTRAINT "huddle_countdown_config_huddle_id_huddles_id_fk" TO "countdown_config_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "countdown_config" RENAME CONSTRAINT "huddle_countdown_config_pkey" TO "countdown_config_pkey";--> statement-breakpoint +ALTER TABLE "huddle_dues_config" RENAME TO "dues_config";--> statement-breakpoint +ALTER TABLE "dues_config" RENAME CONSTRAINT "huddle_dues_config_huddle_id_huddles_id_fk" TO "dues_config_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "dues_config" RENAME CONSTRAINT "huddle_dues_config_pkey" TO "dues_config_pkey";--> statement-breakpoint +ALTER TABLE "huddle_dues_payments" RENAME TO "dues_payments";--> statement-breakpoint +ALTER TABLE "dues_payments" RENAME CONSTRAINT "huddle_dues_payments_huddle_id_huddles_id_fk" TO "dues_payments_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "dues_payments" RENAME CONSTRAINT "huddle_dues_payments_marked_by_users_id_fk" TO "dues_payments_marked_by_users_id_fk";--> statement-breakpoint +ALTER TABLE "dues_payments" RENAME CONSTRAINT "huddle_dues_payments_pkey" TO "dues_payments_pkey";--> statement-breakpoint +ALTER INDEX "huddle_dues_payments_huddle_roster_uniq" RENAME TO "dues_payments_huddle_roster_uniq";--> statement-breakpoint +ALTER TABLE "huddle_forum_replies" RENAME TO "forum_replies";--> statement-breakpoint +ALTER TABLE "forum_replies" RENAME CONSTRAINT "huddle_forum_replies_author_id_users_id_fk" TO "forum_replies_author_id_users_id_fk";--> statement-breakpoint +ALTER TABLE "forum_replies" RENAME CONSTRAINT "huddle_forum_replies_deleted_by_users_id_fk" TO "forum_replies_deleted_by_users_id_fk";--> statement-breakpoint +ALTER TABLE "forum_replies" RENAME CONSTRAINT "huddle_forum_replies_huddle_id_huddles_id_fk" TO "forum_replies_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "forum_replies" RENAME CONSTRAINT "huddle_forum_replies_pkey" TO "forum_replies_pkey";--> statement-breakpoint +ALTER TABLE "forum_replies" RENAME CONSTRAINT "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk" TO "forum_replies_topic_id_forum_topics_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_forum_replies_topic_idx" RENAME TO "forum_replies_topic_idx";--> statement-breakpoint +ALTER TABLE "huddle_forum_topics" RENAME TO "forum_topics";--> statement-breakpoint +ALTER TABLE "forum_topics" RENAME CONSTRAINT "huddle_forum_topics_author_id_users_id_fk" TO "forum_topics_author_id_users_id_fk";--> statement-breakpoint +ALTER TABLE "forum_topics" RENAME CONSTRAINT "huddle_forum_topics_deleted_by_users_id_fk" TO "forum_topics_deleted_by_users_id_fk";--> statement-breakpoint +ALTER TABLE "forum_topics" RENAME CONSTRAINT "huddle_forum_topics_huddle_id_huddles_id_fk" TO "forum_topics_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "forum_topics" RENAME CONSTRAINT "huddle_forum_topics_pkey" TO "forum_topics_pkey";--> statement-breakpoint +ALTER INDEX "huddle_forum_topics_huddle_updated_idx" RENAME TO "forum_topics_huddle_updated_idx";--> statement-breakpoint +ALTER TABLE "huddle_payout_entries" RENAME TO "payout_entries";--> statement-breakpoint +ALTER TABLE "payout_entries" RENAME CONSTRAINT "huddle_payout_entries_huddle_id_huddles_id_fk" TO "payout_entries_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "payout_entries" RENAME CONSTRAINT "huddle_payout_entries_pkey" TO "payout_entries_pkey";--> statement-breakpoint +ALTER INDEX "huddle_payout_entries_huddle_idx" RENAME TO "payout_entries_huddle_idx";--> statement-breakpoint +ALTER TABLE "huddle_poll_options" RENAME TO "poll_options";--> statement-breakpoint +ALTER TABLE "poll_options" RENAME CONSTRAINT "huddle_poll_options_pkey" TO "poll_options_pkey";--> statement-breakpoint +ALTER TABLE "poll_options" RENAME CONSTRAINT "huddle_poll_options_poll_id_huddle_polls_id_fk" TO "poll_options_poll_id_polls_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_poll_options_poll_idx" RENAME TO "poll_options_poll_idx";--> statement-breakpoint +ALTER TABLE "huddle_poll_votes" RENAME TO "poll_votes";--> statement-breakpoint +ALTER TABLE "poll_votes" RENAME CONSTRAINT "huddle_poll_votes_option_id_huddle_poll_options_id_fk" TO "poll_votes_option_id_poll_options_id_fk";--> statement-breakpoint +ALTER TABLE "poll_votes" RENAME CONSTRAINT "huddle_poll_votes_pkey" TO "poll_votes_pkey";--> statement-breakpoint +ALTER TABLE "poll_votes" RENAME CONSTRAINT "huddle_poll_votes_poll_id_huddle_polls_id_fk" TO "poll_votes_poll_id_polls_id_fk";--> statement-breakpoint +ALTER TABLE "poll_votes" RENAME CONSTRAINT "huddle_poll_votes_user_id_users_id_fk" TO "poll_votes_user_id_users_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_poll_votes_option_user_uniq" RENAME TO "poll_votes_option_user_uniq";--> statement-breakpoint +ALTER INDEX "huddle_poll_votes_poll_user_idx" RENAME TO "poll_votes_poll_user_idx";--> statement-breakpoint +ALTER TABLE "huddle_polls" RENAME TO "polls";--> statement-breakpoint +ALTER TABLE "polls" RENAME CONSTRAINT "huddle_polls_author_id_users_id_fk" TO "polls_author_id_users_id_fk";--> statement-breakpoint +ALTER TABLE "polls" RENAME CONSTRAINT "huddle_polls_huddle_id_huddles_id_fk" TO "polls_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "polls" RENAME CONSTRAINT "huddle_polls_pkey" TO "polls_pkey";--> statement-breakpoint +ALTER TABLE "polls" RENAME CONSTRAINT "huddle_polls_topic_id_huddle_forum_topics_id_fk" TO "polls_topic_id_forum_topics_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_polls_dashboard_active_uniq" RENAME TO "polls_dashboard_active_uniq";--> statement-breakpoint +ALTER INDEX "huddle_polls_topic_idx" RENAME TO "polls_topic_idx";--> statement-breakpoint +ALTER TABLE "huddle_side_bets" RENAME TO "side_bets";--> statement-breakpoint +ALTER TABLE "side_bets" RENAME CONSTRAINT "huddle_side_bets_huddle_id_huddles_id_fk" TO "side_bets_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "side_bets" RENAME CONSTRAINT "huddle_side_bets_opponent_id_users_id_fk" TO "side_bets_opponent_id_users_id_fk";--> statement-breakpoint +ALTER TABLE "side_bets" RENAME CONSTRAINT "huddle_side_bets_pkey" TO "side_bets_pkey";--> statement-breakpoint +ALTER TABLE "side_bets" RENAME CONSTRAINT "huddle_side_bets_proposer_id_users_id_fk" TO "side_bets_proposer_id_users_id_fk";--> statement-breakpoint +ALTER TABLE "side_bets" RENAME CONSTRAINT "huddle_side_bets_winner_id_users_id_fk" TO "side_bets_winner_id_users_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_side_bets_huddle_idx" RENAME TO "side_bets_huddle_idx";--> statement-breakpoint +ALTER INDEX "huddle_side_bets_opponent_idx" RENAME TO "side_bets_opponent_idx";--> statement-breakpoint +ALTER INDEX "huddle_side_bets_proposer_idx" RENAME TO "side_bets_proposer_idx";--> statement-breakpoint +ALTER TABLE "huddle_survey_answer_options" RENAME TO "survey_answer_options";--> statement-breakpoint +ALTER TABLE "survey_answer_options" RENAME CONSTRAINT "huddle_survey_answer_options_option_id_huddle_survey_options_id" TO "survey_answer_options_option_id_survey_options_id";--> statement-breakpoint +ALTER TABLE "survey_answer_options" RENAME CONSTRAINT "huddle_survey_answer_options_pkey" TO "survey_answer_options_pkey";--> statement-breakpoint +ALTER TABLE "survey_answer_options" RENAME CONSTRAINT "huddle_survey_answer_options_question_id_huddle_survey_question" TO "survey_answer_options_question_id_survey_questions_id_fk";--> statement-breakpoint +ALTER TABLE "survey_answer_options" RENAME CONSTRAINT "huddle_survey_answer_options_response_id_huddle_survey_response" TO "survey_answer_options_response_id_survey_responses_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_survey_answer_options_question_idx" RENAME TO "survey_answer_options_question_idx";--> statement-breakpoint +ALTER INDEX "huddle_survey_answer_options_response_option_uniq" RENAME TO "survey_answer_options_response_option_uniq";--> statement-breakpoint +ALTER TABLE "huddle_survey_answers" RENAME TO "survey_answers";--> statement-breakpoint +ALTER TABLE "survey_answers" RENAME CONSTRAINT "huddle_survey_answers_pkey" TO "survey_answers_pkey";--> statement-breakpoint +ALTER TABLE "survey_answers" RENAME CONSTRAINT "huddle_survey_answers_question_id_huddle_survey_questions_id_fk" TO "survey_answers_question_id_survey_questions_id_fk";--> statement-breakpoint +ALTER TABLE "survey_answers" RENAME CONSTRAINT "huddle_survey_answers_response_id_huddle_survey_responses_id_fk" TO "survey_answers_response_id_survey_responses_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_survey_answers_question_idx" RENAME TO "survey_answers_question_idx";--> statement-breakpoint +ALTER INDEX "huddle_survey_answers_response_idx" RENAME TO "survey_answers_response_idx";--> statement-breakpoint +ALTER TABLE "huddle_survey_options" RENAME TO "survey_options";--> statement-breakpoint +ALTER TABLE "survey_options" RENAME CONSTRAINT "huddle_survey_options_pkey" TO "survey_options_pkey";--> statement-breakpoint +ALTER TABLE "survey_options" RENAME CONSTRAINT "huddle_survey_options_question_id_huddle_survey_questions_id_fk" TO "survey_options_question_id_survey_questions_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_survey_options_question_idx" RENAME TO "survey_options_question_idx";--> statement-breakpoint +ALTER TABLE "huddle_survey_questions" RENAME TO "survey_questions";--> statement-breakpoint +ALTER TABLE "survey_questions" RENAME CONSTRAINT "huddle_survey_questions_pkey" TO "survey_questions_pkey";--> statement-breakpoint +ALTER TABLE "survey_questions" RENAME CONSTRAINT "huddle_survey_questions_survey_id_huddle_surveys_id_fk" TO "survey_questions_survey_id_surveys_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_survey_questions_survey_idx" RENAME TO "survey_questions_survey_idx";--> statement-breakpoint +ALTER TABLE "huddle_survey_responses" RENAME TO "survey_responses";--> statement-breakpoint +ALTER TABLE "survey_responses" RENAME CONSTRAINT "huddle_survey_responses_pkey" TO "survey_responses_pkey";--> statement-breakpoint +ALTER TABLE "survey_responses" RENAME CONSTRAINT "huddle_survey_responses_survey_id_huddle_surveys_id_fk" TO "survey_responses_survey_id_surveys_id_fk";--> statement-breakpoint +ALTER TABLE "survey_responses" RENAME CONSTRAINT "huddle_survey_responses_user_id_users_id_fk" TO "survey_responses_user_id_users_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_survey_responses_survey_user_uniq" RENAME TO "survey_responses_survey_user_uniq";--> statement-breakpoint +ALTER TABLE "huddle_surveys" RENAME TO "surveys";--> statement-breakpoint +ALTER TABLE "surveys" RENAME CONSTRAINT "huddle_surveys_author_id_users_id_fk" TO "surveys_author_id_users_id_fk";--> statement-breakpoint +ALTER TABLE "surveys" RENAME CONSTRAINT "huddle_surveys_huddle_id_huddles_id_fk" TO "surveys_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "surveys" RENAME CONSTRAINT "huddle_surveys_pkey" TO "surveys_pkey";--> statement-breakpoint +ALTER INDEX "huddle_surveys_huddle_created_idx" RENAME TO "surveys_huddle_created_idx";--> statement-breakpoint +ALTER TABLE "huddle_team_claims" RENAME TO "team_claims";--> statement-breakpoint +ALTER TABLE "team_claims" RENAME CONSTRAINT "huddle_team_claims_decided_by_users_id_fk" TO "team_claims_decided_by_users_id_fk";--> statement-breakpoint +ALTER TABLE "team_claims" RENAME CONSTRAINT "huddle_team_claims_huddle_id_huddles_id_fk" TO "team_claims_huddle_id_huddles_id_fk";--> statement-breakpoint +ALTER TABLE "team_claims" RENAME CONSTRAINT "huddle_team_claims_pkey" TO "team_claims_pkey";--> statement-breakpoint +ALTER TABLE "team_claims" RENAME CONSTRAINT "huddle_team_claims_user_id_users_id_fk" TO "team_claims_user_id_users_id_fk";--> statement-breakpoint +ALTER INDEX "huddle_team_claims_huddle_idx" RENAME TO "team_claims_huddle_idx";--> statement-breakpoint +ALTER INDEX "huddle_team_claims_huddle_roster_approved_uniq" RENAME TO "team_claims_huddle_roster_approved_uniq";--> statement-breakpoint +ALTER INDEX "huddle_team_claims_huddle_user_approved_uniq" RENAME TO "team_claims_huddle_user_approved_uniq";--> statement-breakpoint +ALTER INDEX "huddle_team_claims_user_idx" RENAME TO "team_claims_user_idx"; diff --git a/server/drizzle/0016_fix_truncated_fk_name.sql b/server/drizzle/0016_fix_truncated_fk_name.sql new file mode 100644 index 0000000..e209cf0 --- /dev/null +++ b/server/drizzle/0016_fix_truncated_fk_name.sql @@ -0,0 +1,12 @@ +-- Custom migration, so the snapshot is written even though schema.ts is unchanged. +-- +-- This constraint was created before 0015 as +-- `huddle_survey_answer_options_option_id_huddle_survey_options_id_fk`, which is +-- 66 bytes — over Postgres's 63-byte identifier limit, so it was silently +-- truncated, losing its `_fk` suffix. 0015 renamed the truncated name and so +-- carried the truncation forward. Now that the `huddle_` prefixes are gone the +-- full name fits (52 bytes), and it has to match what Drizzle's snapshot +-- expects or every future `db:generate` will diff against it. +ALTER TABLE "survey_answer_options" + RENAME CONSTRAINT "survey_answer_options_option_id_survey_options_id" + TO "survey_answer_options_option_id_survey_options_id_fk"; diff --git a/server/drizzle/meta/0011_snapshot.json b/server/drizzle/meta/0011_snapshot.json new file mode 100644 index 0000000..7e8692b --- /dev/null +++ b/server/drizzle/meta/0011_snapshot.json @@ -0,0 +1,2410 @@ +{ + "id": "4fb314ac-fb58-467a-a0a7-7e4725a7c292", + "prevId": "6344b4e3-ad1b-40bc-9209-1c8c135255fd", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.huddle_active_trophies": { + "name": "huddle_active_trophies", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trophy_type": { + "name": "trophy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "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": { + "huddle_active_trophies_huddle_id_huddles_id_fk": { + "name": "huddle_active_trophies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_active_trophies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "huddle_active_trophies_huddle_id_trophy_type_pk": { + "name": "huddle_active_trophies_huddle_id_trophy_type_pk", + "columns": [ + "huddle_id", + "trophy_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_announcements": { + "name": "huddle_announcements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "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": { + "huddle_announcements_huddle_idx": { + "name": "huddle_announcements_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_announcements_huddle_id_huddles_id_fk": { + "name": "huddle_announcements_huddle_id_huddles_id_fk", + "tableFrom": "huddle_announcements", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_awards": { + "name": "huddle_awards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "glyph": { + "name": "glyph", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "granted_by": { + "name": "granted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "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": { + "huddle_awards_huddle_idx": { + "name": "huddle_awards_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_awards_huddle_roster_idx": { + "name": "huddle_awards_huddle_roster_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_awards_huddle_id_huddles_id_fk": { + "name": "huddle_awards_huddle_id_huddles_id_fk", + "tableFrom": "huddle_awards", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_commissioners": { + "name": "huddle_commissioners", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_commissioners_user_idx": { + "name": "huddle_commissioners_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_commissioners_huddle_id_huddles_id_fk": { + "name": "huddle_commissioners_huddle_id_huddles_id_fk", + "tableFrom": "huddle_commissioners", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "huddle_commissioners_huddle_id_user_id_pk": { + "name": "huddle_commissioners_huddle_id_user_id_pk", + "columns": [ + "huddle_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_countdown_config": { + "name": "huddle_countdown_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_at": { + "name": "target_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_countdown_config_huddle_id_huddles_id_fk": { + "name": "huddle_countdown_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_countdown_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_config": { + "name": "huddle_dues_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_dues_config_huddle_id_huddles_id_fk": { + "name": "huddle_dues_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_payments": { + "name": "huddle_dues_payments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "marked_by": { + "name": "marked_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_dues_payments_huddle_roster_uniq": { + "name": "huddle_dues_payments_huddle_roster_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_dues_payments_huddle_id_huddles_id_fk": { + "name": "huddle_dues_payments_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_payments", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_replies": { + "name": "huddle_forum_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_replies_topic_idx": { + "name": "huddle_forum_replies_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "huddle_forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_forum_replies_huddle_id_huddles_id_fk": { + "name": "huddle_forum_replies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_topics": { + "name": "huddle_forum_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_topics_huddle_updated_idx": { + "name": "huddle_forum_topics_huddle_updated_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_forum_topics_huddle_id_huddles_id_fk": { + "name": "huddle_forum_topics_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_topics", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_payout_entries": { + "name": "huddle_payout_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "huddle_payout_entries_huddle_idx": { + "name": "huddle_payout_entries_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_payout_entries_huddle_id_huddles_id_fk": { + "name": "huddle_payout_entries_huddle_id_huddles_id_fk", + "tableFrom": "huddle_payout_entries", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_options": { + "name": "huddle_poll_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_poll_options_poll_idx": { + "name": "huddle_poll_options_poll_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_poll_options_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_options_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_options", + "tableTo": "huddle_polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_votes": { + "name": "huddle_poll_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_poll_votes_poll_user_idx": { + "name": "huddle_poll_votes_poll_user_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_poll_votes_option_user_uniq": { + "name": "huddle_poll_votes_option_user_uniq", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_poll_votes_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_votes_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "huddle_polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_poll_votes_option_id_huddle_poll_options_id_fk": { + "name": "huddle_poll_votes_option_id_huddle_poll_options_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "huddle_poll_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_polls": { + "name": "huddle_polls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_dashboard_poll": { + "name": "is_dashboard_poll", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allow_multiple": { + "name": "allow_multiple", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_vote_changes": { + "name": "allow_vote_changes", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "results_visibility": { + "name": "results_visibility", + "type": "poll_results_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'always'" + }, + "closes_at": { + "name": "closes_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": { + "huddle_polls_topic_idx": { + "name": "huddle_polls_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_polls_dashboard_active_uniq": { + "name": "huddle_polls_dashboard_active_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"huddle_polls\".\"is_dashboard_poll\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_polls_huddle_id_huddles_id_fk": { + "name": "huddle_polls_huddle_id_huddles_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_polls_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_polls_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "huddle_forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answer_options": { + "name": "huddle_survey_answer_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answer_options_question_idx": { + "name": "huddle_survey_answer_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_survey_answer_options_response_option_uniq": { + "name": "huddle_survey_answer_options_response_option_uniq", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk": { + "name": "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answers": { + "name": "huddle_survey_answers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "text_value": { + "name": "text_value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answers_response_idx": { + "name": "huddle_survey_answers_response_idx", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_survey_answers_question_idx": { + "name": "huddle_survey_answers_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_answers_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answers_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answers", + "tableTo": "huddle_survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answers_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answers_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answers", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_options": { + "name": "huddle_survey_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_options_question_idx": { + "name": "huddle_survey_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_options", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_questions": { + "name": "huddle_survey_questions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "survey_question_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_questions_survey_idx": { + "name": "huddle_survey_questions_survey_idx", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_questions_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_questions_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_questions", + "tableTo": "huddle_surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_responses": { + "name": "huddle_survey_responses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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": { + "huddle_survey_responses_survey_user_uniq": { + "name": "huddle_survey_responses_survey_user_uniq", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_responses_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_responses_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_responses", + "tableTo": "huddle_surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_surveys": { + "name": "huddle_surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "closes_at": { + "name": "closes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "results_published": { + "name": "results_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_publish_on_close": { + "name": "auto_publish_on_close", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "anonymity": { + "name": "anonymity", + "type": "survey_anonymity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_surveys_huddle_created_idx": { + "name": "huddle_surveys_huddle_created_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_surveys_huddle_id_huddles_id_fk": { + "name": "huddle_surveys_huddle_id_huddles_id_fk", + "tableFrom": "huddle_surveys", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddles": { + "name": "huddles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_provider": { + "name": "league_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "league_id": { + "name": "league_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code_updated_at": { + "name": "invite_code_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invite_link_token": { + "name": "invite_link_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invite_link_expires_at": { + "name": "invite_link_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": { + "huddles_invite_code_uniq": { + "name": "huddles_invite_code_uniq", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddles_invite_link_token_uniq": { + "name": "huddles_invite_link_token_uniq", + "columns": [ + { + "expression": "invite_link_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.side_bets": { + "name": "side_bets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "proposer_id": { + "name": "proposer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opponent_id": { + "name": "opponent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposer_roster_id": { + "name": "proposer_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "opponent_roster_id": { + "name": "opponent_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "week": { + "name": "week", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "prize_description": { + "name": "prize_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "side_bet_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "winner_id": { + "name": "winner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "settlement_note": { + "name": "settlement_note", + "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": { + "side_bets_huddle_idx": { + "name": "side_bets_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "side_bets_proposer_idx": { + "name": "side_bets_proposer_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "proposer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "side_bets_opponent_idx": { + "name": "side_bets_opponent_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "opponent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "side_bets_huddle_id_huddles_id_fk": { + "name": "side_bets_huddle_id_huddles_id_fk", + "tableFrom": "side_bets", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_claims": { + "name": "team_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "claim_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "team_claims_huddle_roster_approved_uniq": { + "name": "team_claims_huddle_roster_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_huddle_user_approved_uniq": { + "name": "team_claims_huddle_user_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_huddle_idx": { + "name": "team_claims_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_user_idx": { + "name": "team_claims_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_claims_huddle_id_huddles_id_fk": { + "name": "team_claims_huddle_id_huddles_id_fk", + "tableFrom": "team_claims", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_username": { + "name": "sleeper_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_user_id": { + "name": "sleeper_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_league_ids": { + "name": "synced_league_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_placeholder": { + "name": "is_placeholder", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "identity_synced_at": { + "name": "identity_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_username_idx": { + "name": "users_username_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.claim_status": { + "name": "claim_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected" + ] + }, + "public.poll_results_visibility": { + "name": "poll_results_visibility", + "schema": "public", + "values": [ + "always", + "after_vote", + "after_close" + ] + }, + "public.side_bet_status": { + "name": "side_bet_status", + "schema": "public", + "values": [ + "pending", + "accepted", + "rejected", + "cancelled", + "settled" + ] + }, + "public.survey_anonymity": { + "name": "survey_anonymity", + "schema": "public", + "values": [ + "none", + "anonymous_to_league", + "anonymous_to_all" + ] + }, + "public.survey_question_type": { + "name": "survey_question_type", + "schema": "public", + "values": [ + "short_text", + "paragraph", + "multiple_choice", + "checkboxes" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/drizzle/meta/0012_snapshot.json b/server/drizzle/meta/0012_snapshot.json new file mode 100644 index 0000000..695452b --- /dev/null +++ b/server/drizzle/meta/0012_snapshot.json @@ -0,0 +1,2644 @@ +{ + "id": "48c4474c-9264-4e99-922a-8f1e3ed418a2", + "prevId": "4fb314ac-fb58-467a-a0a7-7e4725a7c292", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.huddle_active_trophies": { + "name": "huddle_active_trophies", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trophy_type": { + "name": "trophy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "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": { + "huddle_active_trophies_huddle_id_huddles_id_fk": { + "name": "huddle_active_trophies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_active_trophies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "huddle_active_trophies_huddle_id_trophy_type_pk": { + "name": "huddle_active_trophies_huddle_id_trophy_type_pk", + "columns": [ + "huddle_id", + "trophy_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_announcements": { + "name": "huddle_announcements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "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": { + "huddle_announcements_huddle_idx": { + "name": "huddle_announcements_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_announcements_huddle_id_huddles_id_fk": { + "name": "huddle_announcements_huddle_id_huddles_id_fk", + "tableFrom": "huddle_announcements", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_announcements_author_id_users_id_fk": { + "name": "huddle_announcements_author_id_users_id_fk", + "tableFrom": "huddle_announcements", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_awards": { + "name": "huddle_awards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "glyph": { + "name": "glyph", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "granted_by": { + "name": "granted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "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": { + "huddle_awards_huddle_idx": { + "name": "huddle_awards_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_awards_huddle_roster_idx": { + "name": "huddle_awards_huddle_roster_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_awards_huddle_id_huddles_id_fk": { + "name": "huddle_awards_huddle_id_huddles_id_fk", + "tableFrom": "huddle_awards", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_awards_granted_by_users_id_fk": { + "name": "huddle_awards_granted_by_users_id_fk", + "tableFrom": "huddle_awards", + "tableTo": "users", + "columnsFrom": [ + "granted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_commissioners": { + "name": "huddle_commissioners", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_commissioners_user_idx": { + "name": "huddle_commissioners_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_commissioners_huddle_id_huddles_id_fk": { + "name": "huddle_commissioners_huddle_id_huddles_id_fk", + "tableFrom": "huddle_commissioners", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_commissioners_user_id_users_id_fk": { + "name": "huddle_commissioners_user_id_users_id_fk", + "tableFrom": "huddle_commissioners", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_commissioners_added_by_users_id_fk": { + "name": "huddle_commissioners_added_by_users_id_fk", + "tableFrom": "huddle_commissioners", + "tableTo": "users", + "columnsFrom": [ + "added_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "huddle_commissioners_huddle_id_user_id_pk": { + "name": "huddle_commissioners_huddle_id_user_id_pk", + "columns": [ + "huddle_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_countdown_config": { + "name": "huddle_countdown_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_at": { + "name": "target_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_countdown_config_huddle_id_huddles_id_fk": { + "name": "huddle_countdown_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_countdown_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_config": { + "name": "huddle_dues_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_dues_config_huddle_id_huddles_id_fk": { + "name": "huddle_dues_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_payments": { + "name": "huddle_dues_payments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "marked_by": { + "name": "marked_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_dues_payments_huddle_roster_uniq": { + "name": "huddle_dues_payments_huddle_roster_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_dues_payments_huddle_id_huddles_id_fk": { + "name": "huddle_dues_payments_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_payments", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_dues_payments_marked_by_users_id_fk": { + "name": "huddle_dues_payments_marked_by_users_id_fk", + "tableFrom": "huddle_dues_payments", + "tableTo": "users", + "columnsFrom": [ + "marked_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_replies": { + "name": "huddle_forum_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_replies_topic_idx": { + "name": "huddle_forum_replies_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "huddle_forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_forum_replies_huddle_id_huddles_id_fk": { + "name": "huddle_forum_replies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_forum_replies_author_id_users_id_fk": { + "name": "huddle_forum_replies_author_id_users_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_forum_replies_deleted_by_users_id_fk": { + "name": "huddle_forum_replies_deleted_by_users_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "users", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_topics": { + "name": "huddle_forum_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_topics_huddle_updated_idx": { + "name": "huddle_forum_topics_huddle_updated_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_forum_topics_huddle_id_huddles_id_fk": { + "name": "huddle_forum_topics_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_topics", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_forum_topics_author_id_users_id_fk": { + "name": "huddle_forum_topics_author_id_users_id_fk", + "tableFrom": "huddle_forum_topics", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_forum_topics_deleted_by_users_id_fk": { + "name": "huddle_forum_topics_deleted_by_users_id_fk", + "tableFrom": "huddle_forum_topics", + "tableTo": "users", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_payout_entries": { + "name": "huddle_payout_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "huddle_payout_entries_huddle_idx": { + "name": "huddle_payout_entries_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_payout_entries_huddle_id_huddles_id_fk": { + "name": "huddle_payout_entries_huddle_id_huddles_id_fk", + "tableFrom": "huddle_payout_entries", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_options": { + "name": "huddle_poll_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_poll_options_poll_idx": { + "name": "huddle_poll_options_poll_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_poll_options_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_options_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_options", + "tableTo": "huddle_polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_votes": { + "name": "huddle_poll_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_poll_votes_poll_user_idx": { + "name": "huddle_poll_votes_poll_user_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_poll_votes_option_user_uniq": { + "name": "huddle_poll_votes_option_user_uniq", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_poll_votes_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_votes_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "huddle_polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_poll_votes_option_id_huddle_poll_options_id_fk": { + "name": "huddle_poll_votes_option_id_huddle_poll_options_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "huddle_poll_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_poll_votes_user_id_users_id_fk": { + "name": "huddle_poll_votes_user_id_users_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_polls": { + "name": "huddle_polls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_dashboard_poll": { + "name": "is_dashboard_poll", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allow_multiple": { + "name": "allow_multiple", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_vote_changes": { + "name": "allow_vote_changes", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "results_visibility": { + "name": "results_visibility", + "type": "poll_results_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'always'" + }, + "closes_at": { + "name": "closes_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": { + "huddle_polls_topic_idx": { + "name": "huddle_polls_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_polls_dashboard_active_uniq": { + "name": "huddle_polls_dashboard_active_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"huddle_polls\".\"is_dashboard_poll\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_polls_huddle_id_huddles_id_fk": { + "name": "huddle_polls_huddle_id_huddles_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_polls_author_id_users_id_fk": { + "name": "huddle_polls_author_id_users_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_polls_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_polls_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "huddle_forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answer_options": { + "name": "huddle_survey_answer_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answer_options_question_idx": { + "name": "huddle_survey_answer_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_survey_answer_options_response_option_uniq": { + "name": "huddle_survey_answer_options_response_option_uniq", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk": { + "name": "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answers": { + "name": "huddle_survey_answers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "text_value": { + "name": "text_value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answers_response_idx": { + "name": "huddle_survey_answers_response_idx", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_survey_answers_question_idx": { + "name": "huddle_survey_answers_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_answers_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answers_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answers", + "tableTo": "huddle_survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answers_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answers_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answers", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_options": { + "name": "huddle_survey_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_options_question_idx": { + "name": "huddle_survey_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_options", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_questions": { + "name": "huddle_survey_questions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "survey_question_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_questions_survey_idx": { + "name": "huddle_survey_questions_survey_idx", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_questions_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_questions_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_questions", + "tableTo": "huddle_surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_responses": { + "name": "huddle_survey_responses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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": { + "huddle_survey_responses_survey_user_uniq": { + "name": "huddle_survey_responses_survey_user_uniq", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_responses_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_responses_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_responses", + "tableTo": "huddle_surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_responses_user_id_users_id_fk": { + "name": "huddle_survey_responses_user_id_users_id_fk", + "tableFrom": "huddle_survey_responses", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_surveys": { + "name": "huddle_surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "closes_at": { + "name": "closes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "results_published": { + "name": "results_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_publish_on_close": { + "name": "auto_publish_on_close", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "anonymity": { + "name": "anonymity", + "type": "survey_anonymity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_surveys_huddle_created_idx": { + "name": "huddle_surveys_huddle_created_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_surveys_huddle_id_huddles_id_fk": { + "name": "huddle_surveys_huddle_id_huddles_id_fk", + "tableFrom": "huddle_surveys", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_surveys_author_id_users_id_fk": { + "name": "huddle_surveys_author_id_users_id_fk", + "tableFrom": "huddle_surveys", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddles": { + "name": "huddles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_provider": { + "name": "league_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "league_id": { + "name": "league_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code_updated_at": { + "name": "invite_code_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invite_link_token": { + "name": "invite_link_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invite_link_expires_at": { + "name": "invite_link_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": { + "huddles_invite_code_uniq": { + "name": "huddles_invite_code_uniq", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddles_invite_link_token_uniq": { + "name": "huddles_invite_link_token_uniq", + "columns": [ + { + "expression": "invite_link_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.side_bets": { + "name": "side_bets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "proposer_id": { + "name": "proposer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opponent_id": { + "name": "opponent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposer_roster_id": { + "name": "proposer_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "opponent_roster_id": { + "name": "opponent_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "week": { + "name": "week", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "prize_description": { + "name": "prize_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "side_bet_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "winner_id": { + "name": "winner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "settlement_note": { + "name": "settlement_note", + "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": { + "side_bets_huddle_idx": { + "name": "side_bets_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "side_bets_proposer_idx": { + "name": "side_bets_proposer_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "proposer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "side_bets_opponent_idx": { + "name": "side_bets_opponent_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "opponent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "side_bets_huddle_id_huddles_id_fk": { + "name": "side_bets_huddle_id_huddles_id_fk", + "tableFrom": "side_bets", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "side_bets_proposer_id_users_id_fk": { + "name": "side_bets_proposer_id_users_id_fk", + "tableFrom": "side_bets", + "tableTo": "users", + "columnsFrom": [ + "proposer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "side_bets_opponent_id_users_id_fk": { + "name": "side_bets_opponent_id_users_id_fk", + "tableFrom": "side_bets", + "tableTo": "users", + "columnsFrom": [ + "opponent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "side_bets_winner_id_users_id_fk": { + "name": "side_bets_winner_id_users_id_fk", + "tableFrom": "side_bets", + "tableTo": "users", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_claims": { + "name": "team_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "claim_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "team_claims_huddle_roster_approved_uniq": { + "name": "team_claims_huddle_roster_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_huddle_user_approved_uniq": { + "name": "team_claims_huddle_user_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_huddle_idx": { + "name": "team_claims_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_user_idx": { + "name": "team_claims_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_claims_huddle_id_huddles_id_fk": { + "name": "team_claims_huddle_id_huddles_id_fk", + "tableFrom": "team_claims", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_claims_user_id_users_id_fk": { + "name": "team_claims_user_id_users_id_fk", + "tableFrom": "team_claims", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "team_claims_decided_by_users_id_fk": { + "name": "team_claims_decided_by_users_id_fk", + "tableFrom": "team_claims", + "tableTo": "users", + "columnsFrom": [ + "decided_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_username": { + "name": "sleeper_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_user_id": { + "name": "sleeper_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_league_ids": { + "name": "synced_league_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_placeholder": { + "name": "is_placeholder", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "identity_synced_at": { + "name": "identity_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_username_idx": { + "name": "users_username_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.claim_status": { + "name": "claim_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected" + ] + }, + "public.poll_results_visibility": { + "name": "poll_results_visibility", + "schema": "public", + "values": [ + "always", + "after_vote", + "after_close" + ] + }, + "public.side_bet_status": { + "name": "side_bet_status", + "schema": "public", + "values": [ + "pending", + "accepted", + "rejected", + "cancelled", + "settled" + ] + }, + "public.survey_anonymity": { + "name": "survey_anonymity", + "schema": "public", + "values": [ + "none", + "anonymous_to_league", + "anonymous_to_all" + ] + }, + "public.survey_question_type": { + "name": "survey_question_type", + "schema": "public", + "values": [ + "short_text", + "paragraph", + "multiple_choice", + "checkboxes" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/drizzle/meta/0013_snapshot.json b/server/drizzle/meta/0013_snapshot.json new file mode 100644 index 0000000..95be831 --- /dev/null +++ b/server/drizzle/meta/0013_snapshot.json @@ -0,0 +1,2644 @@ +{ + "id": "63c4d38b-4b6d-49ad-a885-3cc750ee2e2a", + "prevId": "48c4474c-9264-4e99-922a-8f1e3ed418a2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.huddle_active_trophies": { + "name": "huddle_active_trophies", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trophy_type": { + "name": "trophy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_active_trophies_huddle_id_huddles_id_fk": { + "name": "huddle_active_trophies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_active_trophies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "huddle_active_trophies_huddle_id_trophy_type_pk": { + "name": "huddle_active_trophies_huddle_id_trophy_type_pk", + "columns": [ + "huddle_id", + "trophy_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_announcements": { + "name": "huddle_announcements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "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": { + "huddle_announcements_huddle_idx": { + "name": "huddle_announcements_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_announcements_huddle_id_huddles_id_fk": { + "name": "huddle_announcements_huddle_id_huddles_id_fk", + "tableFrom": "huddle_announcements", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_announcements_author_id_users_id_fk": { + "name": "huddle_announcements_author_id_users_id_fk", + "tableFrom": "huddle_announcements", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_awards": { + "name": "huddle_awards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "glyph": { + "name": "glyph", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "granted_by": { + "name": "granted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "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": { + "huddle_awards_huddle_idx": { + "name": "huddle_awards_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_awards_huddle_roster_idx": { + "name": "huddle_awards_huddle_roster_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_awards_huddle_id_huddles_id_fk": { + "name": "huddle_awards_huddle_id_huddles_id_fk", + "tableFrom": "huddle_awards", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_awards_granted_by_users_id_fk": { + "name": "huddle_awards_granted_by_users_id_fk", + "tableFrom": "huddle_awards", + "tableTo": "users", + "columnsFrom": [ + "granted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_commissioners": { + "name": "huddle_commissioners", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_commissioners_user_idx": { + "name": "huddle_commissioners_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_commissioners_huddle_id_huddles_id_fk": { + "name": "huddle_commissioners_huddle_id_huddles_id_fk", + "tableFrom": "huddle_commissioners", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_commissioners_user_id_users_id_fk": { + "name": "huddle_commissioners_user_id_users_id_fk", + "tableFrom": "huddle_commissioners", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_commissioners_added_by_users_id_fk": { + "name": "huddle_commissioners_added_by_users_id_fk", + "tableFrom": "huddle_commissioners", + "tableTo": "users", + "columnsFrom": [ + "added_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "huddle_commissioners_huddle_id_user_id_pk": { + "name": "huddle_commissioners_huddle_id_user_id_pk", + "columns": [ + "huddle_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_countdown_config": { + "name": "huddle_countdown_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_at": { + "name": "target_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_countdown_config_huddle_id_huddles_id_fk": { + "name": "huddle_countdown_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_countdown_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_config": { + "name": "huddle_dues_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_dues_config_huddle_id_huddles_id_fk": { + "name": "huddle_dues_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_payments": { + "name": "huddle_dues_payments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "marked_by": { + "name": "marked_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_dues_payments_huddle_roster_uniq": { + "name": "huddle_dues_payments_huddle_roster_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_dues_payments_huddle_id_huddles_id_fk": { + "name": "huddle_dues_payments_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_payments", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_dues_payments_marked_by_users_id_fk": { + "name": "huddle_dues_payments_marked_by_users_id_fk", + "tableFrom": "huddle_dues_payments", + "tableTo": "users", + "columnsFrom": [ + "marked_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_replies": { + "name": "huddle_forum_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_replies_topic_idx": { + "name": "huddle_forum_replies_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "huddle_forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_forum_replies_huddle_id_huddles_id_fk": { + "name": "huddle_forum_replies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_forum_replies_author_id_users_id_fk": { + "name": "huddle_forum_replies_author_id_users_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_forum_replies_deleted_by_users_id_fk": { + "name": "huddle_forum_replies_deleted_by_users_id_fk", + "tableFrom": "huddle_forum_replies", + "tableTo": "users", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_topics": { + "name": "huddle_forum_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_topics_huddle_updated_idx": { + "name": "huddle_forum_topics_huddle_updated_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_forum_topics_huddle_id_huddles_id_fk": { + "name": "huddle_forum_topics_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_topics", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_forum_topics_author_id_users_id_fk": { + "name": "huddle_forum_topics_author_id_users_id_fk", + "tableFrom": "huddle_forum_topics", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_forum_topics_deleted_by_users_id_fk": { + "name": "huddle_forum_topics_deleted_by_users_id_fk", + "tableFrom": "huddle_forum_topics", + "tableTo": "users", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_payout_entries": { + "name": "huddle_payout_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "huddle_payout_entries_huddle_idx": { + "name": "huddle_payout_entries_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_payout_entries_huddle_id_huddles_id_fk": { + "name": "huddle_payout_entries_huddle_id_huddles_id_fk", + "tableFrom": "huddle_payout_entries", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_options": { + "name": "huddle_poll_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_poll_options_poll_idx": { + "name": "huddle_poll_options_poll_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_poll_options_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_options_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_options", + "tableTo": "huddle_polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_votes": { + "name": "huddle_poll_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_poll_votes_poll_user_idx": { + "name": "huddle_poll_votes_poll_user_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_poll_votes_option_user_uniq": { + "name": "huddle_poll_votes_option_user_uniq", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_poll_votes_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_votes_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "huddle_polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_poll_votes_option_id_huddle_poll_options_id_fk": { + "name": "huddle_poll_votes_option_id_huddle_poll_options_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "huddle_poll_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_poll_votes_user_id_users_id_fk": { + "name": "huddle_poll_votes_user_id_users_id_fk", + "tableFrom": "huddle_poll_votes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_polls": { + "name": "huddle_polls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_dashboard_poll": { + "name": "is_dashboard_poll", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allow_multiple": { + "name": "allow_multiple", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_vote_changes": { + "name": "allow_vote_changes", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "results_visibility": { + "name": "results_visibility", + "type": "poll_results_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'always'" + }, + "closes_at": { + "name": "closes_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": { + "huddle_polls_topic_idx": { + "name": "huddle_polls_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_polls_dashboard_active_uniq": { + "name": "huddle_polls_dashboard_active_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"huddle_polls\".\"is_dashboard_poll\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_polls_huddle_id_huddles_id_fk": { + "name": "huddle_polls_huddle_id_huddles_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_polls_author_id_users_id_fk": { + "name": "huddle_polls_author_id_users_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_polls_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_polls_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_polls", + "tableTo": "huddle_forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_side_bets": { + "name": "huddle_side_bets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "proposer_id": { + "name": "proposer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opponent_id": { + "name": "opponent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposer_roster_id": { + "name": "proposer_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "opponent_roster_id": { + "name": "opponent_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "week": { + "name": "week", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "prize_description": { + "name": "prize_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "side_bet_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "winner_id": { + "name": "winner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "settlement_note": { + "name": "settlement_note", + "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": { + "huddle_side_bets_huddle_idx": { + "name": "huddle_side_bets_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_side_bets_proposer_idx": { + "name": "huddle_side_bets_proposer_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "proposer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_side_bets_opponent_idx": { + "name": "huddle_side_bets_opponent_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "opponent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_side_bets_huddle_id_huddles_id_fk": { + "name": "huddle_side_bets_huddle_id_huddles_id_fk", + "tableFrom": "huddle_side_bets", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_side_bets_proposer_id_users_id_fk": { + "name": "huddle_side_bets_proposer_id_users_id_fk", + "tableFrom": "huddle_side_bets", + "tableTo": "users", + "columnsFrom": [ + "proposer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_side_bets_opponent_id_users_id_fk": { + "name": "huddle_side_bets_opponent_id_users_id_fk", + "tableFrom": "huddle_side_bets", + "tableTo": "users", + "columnsFrom": [ + "opponent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_side_bets_winner_id_users_id_fk": { + "name": "huddle_side_bets_winner_id_users_id_fk", + "tableFrom": "huddle_side_bets", + "tableTo": "users", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answer_options": { + "name": "huddle_survey_answer_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answer_options_question_idx": { + "name": "huddle_survey_answer_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_survey_answer_options_response_option_uniq": { + "name": "huddle_survey_answer_options_response_option_uniq", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk": { + "name": "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk", + "tableFrom": "huddle_survey_answer_options", + "tableTo": "huddle_survey_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answers": { + "name": "huddle_survey_answers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "text_value": { + "name": "text_value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answers_response_idx": { + "name": "huddle_survey_answers_response_idx", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_survey_answers_question_idx": { + "name": "huddle_survey_answers_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_answers_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answers_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answers", + "tableTo": "huddle_survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_answers_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answers_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answers", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_options": { + "name": "huddle_survey_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_options_question_idx": { + "name": "huddle_survey_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_options", + "tableTo": "huddle_survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_questions": { + "name": "huddle_survey_questions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "survey_question_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_questions_survey_idx": { + "name": "huddle_survey_questions_survey_idx", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_questions_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_questions_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_questions", + "tableTo": "huddle_surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_responses": { + "name": "huddle_survey_responses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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": { + "huddle_survey_responses_survey_user_uniq": { + "name": "huddle_survey_responses_survey_user_uniq", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_survey_responses_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_responses_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_responses", + "tableTo": "huddle_surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_survey_responses_user_id_users_id_fk": { + "name": "huddle_survey_responses_user_id_users_id_fk", + "tableFrom": "huddle_survey_responses", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_surveys": { + "name": "huddle_surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "closes_at": { + "name": "closes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "results_published": { + "name": "results_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_publish_on_close": { + "name": "auto_publish_on_close", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "anonymity": { + "name": "anonymity", + "type": "survey_anonymity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_surveys_huddle_created_idx": { + "name": "huddle_surveys_huddle_created_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_surveys_huddle_id_huddles_id_fk": { + "name": "huddle_surveys_huddle_id_huddles_id_fk", + "tableFrom": "huddle_surveys", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_surveys_author_id_users_id_fk": { + "name": "huddle_surveys_author_id_users_id_fk", + "tableFrom": "huddle_surveys", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_team_claims": { + "name": "huddle_team_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "claim_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_team_claims_huddle_roster_approved_uniq": { + "name": "huddle_team_claims_huddle_roster_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"huddle_team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_team_claims_huddle_user_approved_uniq": { + "name": "huddle_team_claims_huddle_user_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"huddle_team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_team_claims_huddle_idx": { + "name": "huddle_team_claims_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddle_team_claims_user_idx": { + "name": "huddle_team_claims_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "huddle_team_claims_huddle_id_huddles_id_fk": { + "name": "huddle_team_claims_huddle_id_huddles_id_fk", + "tableFrom": "huddle_team_claims", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "huddle_team_claims_user_id_users_id_fk": { + "name": "huddle_team_claims_user_id_users_id_fk", + "tableFrom": "huddle_team_claims", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "huddle_team_claims_decided_by_users_id_fk": { + "name": "huddle_team_claims_decided_by_users_id_fk", + "tableFrom": "huddle_team_claims", + "tableTo": "users", + "columnsFrom": [ + "decided_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddles": { + "name": "huddles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_provider": { + "name": "league_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "league_id": { + "name": "league_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code_updated_at": { + "name": "invite_code_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invite_link_token": { + "name": "invite_link_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invite_link_expires_at": { + "name": "invite_link_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": { + "huddles_invite_code_uniq": { + "name": "huddles_invite_code_uniq", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddles_invite_link_token_uniq": { + "name": "huddles_invite_link_token_uniq", + "columns": [ + { + "expression": "invite_link_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_username": { + "name": "sleeper_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_user_id": { + "name": "sleeper_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_league_ids": { + "name": "synced_league_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_placeholder": { + "name": "is_placeholder", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "identity_synced_at": { + "name": "identity_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_username_idx": { + "name": "users_username_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.claim_status": { + "name": "claim_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected" + ] + }, + "public.poll_results_visibility": { + "name": "poll_results_visibility", + "schema": "public", + "values": [ + "always", + "after_vote", + "after_close" + ] + }, + "public.side_bet_status": { + "name": "side_bet_status", + "schema": "public", + "values": [ + "pending", + "accepted", + "rejected", + "cancelled", + "settled" + ] + }, + "public.survey_anonymity": { + "name": "survey_anonymity", + "schema": "public", + "values": [ + "none", + "anonymous_to_league", + "anonymous_to_all" + ] + }, + "public.survey_question_type": { + "name": "survey_question_type", + "schema": "public", + "values": [ + "short_text", + "paragraph", + "multiple_choice", + "checkboxes" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/drizzle/meta/0014_snapshot.json b/server/drizzle/meta/0014_snapshot.json new file mode 100644 index 0000000..b62dbe0 --- /dev/null +++ b/server/drizzle/meta/0014_snapshot.json @@ -0,0 +1,2644 @@ +{ + "id": "f9994cad-9a04-4600-976e-240953d6631c", + "prevId": "63c4d38b-4b6d-49ad-a885-3cc750ee2e2a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.huddle_active_trophies": { + "name": "huddle_active_trophies", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trophy_type": { + "name": "trophy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_active_trophies_huddle_id_huddles_id_fk": { + "name": "huddle_active_trophies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_active_trophies", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "huddle_active_trophies_huddle_id_trophy_type_pk": { + "name": "huddle_active_trophies_huddle_id_trophy_type_pk", + "columns": [ + "huddle_id", + "trophy_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_announcements": { + "name": "huddle_announcements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "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": { + "huddle_announcements_huddle_idx": { + "name": "huddle_announcements_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_announcements_huddle_id_huddles_id_fk": { + "name": "huddle_announcements_huddle_id_huddles_id_fk", + "tableFrom": "huddle_announcements", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_announcements_author_id_users_id_fk": { + "name": "huddle_announcements_author_id_users_id_fk", + "tableFrom": "huddle_announcements", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_awards": { + "name": "huddle_awards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "glyph": { + "name": "glyph", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "granted_by": { + "name": "granted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "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": { + "huddle_awards_huddle_idx": { + "name": "huddle_awards_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_awards_huddle_roster_idx": { + "name": "huddle_awards_huddle_roster_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_awards_huddle_id_huddles_id_fk": { + "name": "huddle_awards_huddle_id_huddles_id_fk", + "tableFrom": "huddle_awards", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_awards_granted_by_users_id_fk": { + "name": "huddle_awards_granted_by_users_id_fk", + "tableFrom": "huddle_awards", + "columnsFrom": [ + "granted_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_commissioners": { + "name": "huddle_commissioners", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_commissioners_user_idx": { + "name": "huddle_commissioners_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_commissioners_huddle_id_huddles_id_fk": { + "name": "huddle_commissioners_huddle_id_huddles_id_fk", + "tableFrom": "huddle_commissioners", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_commissioners_user_id_users_id_fk": { + "name": "huddle_commissioners_user_id_users_id_fk", + "tableFrom": "huddle_commissioners", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "huddle_commissioners_added_by_users_id_fk": { + "name": "huddle_commissioners_added_by_users_id_fk", + "tableFrom": "huddle_commissioners", + "columnsFrom": [ + "added_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": { + "huddle_commissioners_huddle_id_user_id_pk": { + "name": "huddle_commissioners_huddle_id_user_id_pk", + "columns": [ + "huddle_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_countdown_config": { + "name": "huddle_countdown_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_at": { + "name": "target_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_countdown_config_huddle_id_huddles_id_fk": { + "name": "huddle_countdown_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_countdown_config", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_config": { + "name": "huddle_dues_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "huddle_dues_config_huddle_id_huddles_id_fk": { + "name": "huddle_dues_config_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_config", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_dues_payments": { + "name": "huddle_dues_payments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "marked_by": { + "name": "marked_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_dues_payments_huddle_roster_uniq": { + "name": "huddle_dues_payments_huddle_roster_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_dues_payments_huddle_id_huddles_id_fk": { + "name": "huddle_dues_payments_huddle_id_huddles_id_fk", + "tableFrom": "huddle_dues_payments", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_dues_payments_marked_by_users_id_fk": { + "name": "huddle_dues_payments_marked_by_users_id_fk", + "tableFrom": "huddle_dues_payments", + "columnsFrom": [ + "marked_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_replies": { + "name": "huddle_forum_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_replies_topic_idx": { + "name": "huddle_forum_replies_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_forum_replies_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_forum_replies", + "columnsFrom": [ + "topic_id" + ], + "tableTo": "huddle_forum_topics", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_forum_replies_huddle_id_huddles_id_fk": { + "name": "huddle_forum_replies_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_replies", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_forum_replies_author_id_users_id_fk": { + "name": "huddle_forum_replies_author_id_users_id_fk", + "tableFrom": "huddle_forum_replies", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "huddle_forum_replies_deleted_by_users_id_fk": { + "name": "huddle_forum_replies_deleted_by_users_id_fk", + "tableFrom": "huddle_forum_replies", + "columnsFrom": [ + "deleted_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_forum_topics": { + "name": "huddle_forum_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_forum_topics_huddle_updated_idx": { + "name": "huddle_forum_topics_huddle_updated_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_forum_topics_huddle_id_huddles_id_fk": { + "name": "huddle_forum_topics_huddle_id_huddles_id_fk", + "tableFrom": "huddle_forum_topics", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_forum_topics_author_id_users_id_fk": { + "name": "huddle_forum_topics_author_id_users_id_fk", + "tableFrom": "huddle_forum_topics", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "huddle_forum_topics_deleted_by_users_id_fk": { + "name": "huddle_forum_topics_deleted_by_users_id_fk", + "tableFrom": "huddle_forum_topics", + "columnsFrom": [ + "deleted_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_payout_entries": { + "name": "huddle_payout_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "huddle_payout_entries_huddle_idx": { + "name": "huddle_payout_entries_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_payout_entries_huddle_id_huddles_id_fk": { + "name": "huddle_payout_entries_huddle_id_huddles_id_fk", + "tableFrom": "huddle_payout_entries", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_options": { + "name": "huddle_poll_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_poll_options_poll_idx": { + "name": "huddle_poll_options_poll_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_poll_options_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_options_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_options", + "columnsFrom": [ + "poll_id" + ], + "tableTo": "huddle_polls", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_poll_votes": { + "name": "huddle_poll_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_poll_votes_poll_user_idx": { + "name": "huddle_poll_votes_poll_user_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_poll_votes_option_user_uniq": { + "name": "huddle_poll_votes_option_user_uniq", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_poll_votes_poll_id_huddle_polls_id_fk": { + "name": "huddle_poll_votes_poll_id_huddle_polls_id_fk", + "tableFrom": "huddle_poll_votes", + "columnsFrom": [ + "poll_id" + ], + "tableTo": "huddle_polls", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_poll_votes_option_id_huddle_poll_options_id_fk": { + "name": "huddle_poll_votes_option_id_huddle_poll_options_id_fk", + "tableFrom": "huddle_poll_votes", + "columnsFrom": [ + "option_id" + ], + "tableTo": "huddle_poll_options", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_poll_votes_user_id_users_id_fk": { + "name": "huddle_poll_votes_user_id_users_id_fk", + "tableFrom": "huddle_poll_votes", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_polls": { + "name": "huddle_polls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_dashboard_poll": { + "name": "is_dashboard_poll", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allow_multiple": { + "name": "allow_multiple", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_vote_changes": { + "name": "allow_vote_changes", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "results_visibility": { + "name": "results_visibility", + "type": "poll_results_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'always'" + }, + "closes_at": { + "name": "closes_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": { + "huddle_polls_topic_idx": { + "name": "huddle_polls_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_polls_dashboard_active_uniq": { + "name": "huddle_polls_dashboard_active_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"huddle_polls\".\"is_dashboard_poll\" = true", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_polls_huddle_id_huddles_id_fk": { + "name": "huddle_polls_huddle_id_huddles_id_fk", + "tableFrom": "huddle_polls", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_polls_author_id_users_id_fk": { + "name": "huddle_polls_author_id_users_id_fk", + "tableFrom": "huddle_polls", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "huddle_polls_topic_id_huddle_forum_topics_id_fk": { + "name": "huddle_polls_topic_id_huddle_forum_topics_id_fk", + "tableFrom": "huddle_polls", + "columnsFrom": [ + "topic_id" + ], + "tableTo": "huddle_forum_topics", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_side_bets": { + "name": "huddle_side_bets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "proposer_id": { + "name": "proposer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opponent_id": { + "name": "opponent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposer_roster_id": { + "name": "proposer_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "opponent_roster_id": { + "name": "opponent_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "week": { + "name": "week", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "prize_description": { + "name": "prize_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "side_bet_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "winner_id": { + "name": "winner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "settlement_note": { + "name": "settlement_note", + "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": { + "huddle_side_bets_huddle_idx": { + "name": "huddle_side_bets_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_side_bets_proposer_idx": { + "name": "huddle_side_bets_proposer_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "proposer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_side_bets_opponent_idx": { + "name": "huddle_side_bets_opponent_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "opponent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_side_bets_huddle_id_huddles_id_fk": { + "name": "huddle_side_bets_huddle_id_huddles_id_fk", + "tableFrom": "huddle_side_bets", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_side_bets_proposer_id_users_id_fk": { + "name": "huddle_side_bets_proposer_id_users_id_fk", + "tableFrom": "huddle_side_bets", + "columnsFrom": [ + "proposer_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "huddle_side_bets_opponent_id_users_id_fk": { + "name": "huddle_side_bets_opponent_id_users_id_fk", + "tableFrom": "huddle_side_bets", + "columnsFrom": [ + "opponent_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "huddle_side_bets_winner_id_users_id_fk": { + "name": "huddle_side_bets_winner_id_users_id_fk", + "tableFrom": "huddle_side_bets", + "columnsFrom": [ + "winner_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answer_options": { + "name": "huddle_survey_answer_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answer_options_question_idx": { + "name": "huddle_survey_answer_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_survey_answer_options_response_option_uniq": { + "name": "huddle_survey_answer_options_response_option_uniq", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answer_options_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answer_options", + "columnsFrom": [ + "response_id" + ], + "tableTo": "huddle_survey_responses", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answer_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answer_options", + "columnsFrom": [ + "question_id" + ], + "tableTo": "huddle_survey_questions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk": { + "name": "huddle_survey_answer_options_option_id_huddle_survey_options_id_fk", + "tableFrom": "huddle_survey_answer_options", + "columnsFrom": [ + "option_id" + ], + "tableTo": "huddle_survey_options", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_answers": { + "name": "huddle_survey_answers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "text_value": { + "name": "text_value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "huddle_survey_answers_response_idx": { + "name": "huddle_survey_answers_response_idx", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_survey_answers_question_idx": { + "name": "huddle_survey_answers_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_survey_answers_response_id_huddle_survey_responses_id_fk": { + "name": "huddle_survey_answers_response_id_huddle_survey_responses_id_fk", + "tableFrom": "huddle_survey_answers", + "columnsFrom": [ + "response_id" + ], + "tableTo": "huddle_survey_responses", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_survey_answers_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_answers_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_answers", + "columnsFrom": [ + "question_id" + ], + "tableTo": "huddle_survey_questions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_options": { + "name": "huddle_survey_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_options_question_idx": { + "name": "huddle_survey_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_survey_options_question_id_huddle_survey_questions_id_fk": { + "name": "huddle_survey_options_question_id_huddle_survey_questions_id_fk", + "tableFrom": "huddle_survey_options", + "columnsFrom": [ + "question_id" + ], + "tableTo": "huddle_survey_questions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_questions": { + "name": "huddle_survey_questions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "survey_question_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "huddle_survey_questions_survey_idx": { + "name": "huddle_survey_questions_survey_idx", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_survey_questions_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_questions_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_questions", + "columnsFrom": [ + "survey_id" + ], + "tableTo": "huddle_surveys", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_survey_responses": { + "name": "huddle_survey_responses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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": { + "huddle_survey_responses_survey_user_uniq": { + "name": "huddle_survey_responses_survey_user_uniq", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_survey_responses_survey_id_huddle_surveys_id_fk": { + "name": "huddle_survey_responses_survey_id_huddle_surveys_id_fk", + "tableFrom": "huddle_survey_responses", + "columnsFrom": [ + "survey_id" + ], + "tableTo": "huddle_surveys", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_survey_responses_user_id_users_id_fk": { + "name": "huddle_survey_responses_user_id_users_id_fk", + "tableFrom": "huddle_survey_responses", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_surveys": { + "name": "huddle_surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "closes_at": { + "name": "closes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "results_published": { + "name": "results_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_publish_on_close": { + "name": "auto_publish_on_close", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "anonymity": { + "name": "anonymity", + "type": "survey_anonymity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "huddle_surveys_huddle_created_idx": { + "name": "huddle_surveys_huddle_created_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_surveys_huddle_id_huddles_id_fk": { + "name": "huddle_surveys_huddle_id_huddles_id_fk", + "tableFrom": "huddle_surveys", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_surveys_author_id_users_id_fk": { + "name": "huddle_surveys_author_id_users_id_fk", + "tableFrom": "huddle_surveys", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddle_team_claims": { + "name": "huddle_team_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "claim_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "huddle_team_claims_huddle_roster_approved_uniq": { + "name": "huddle_team_claims_huddle_roster_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"huddle_team_claims\".\"status\" = 'approved'", + "concurrently": false + }, + "huddle_team_claims_huddle_user_approved_uniq": { + "name": "huddle_team_claims_huddle_user_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"huddle_team_claims\".\"status\" = 'approved'", + "concurrently": false + }, + "huddle_team_claims_huddle_idx": { + "name": "huddle_team_claims_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddle_team_claims_user_idx": { + "name": "huddle_team_claims_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "huddle_team_claims_huddle_id_huddles_id_fk": { + "name": "huddle_team_claims_huddle_id_huddles_id_fk", + "tableFrom": "huddle_team_claims", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "huddle_team_claims_user_id_users_id_fk": { + "name": "huddle_team_claims_user_id_users_id_fk", + "tableFrom": "huddle_team_claims", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "huddle_team_claims_decided_by_users_id_fk": { + "name": "huddle_team_claims_decided_by_users_id_fk", + "tableFrom": "huddle_team_claims", + "columnsFrom": [ + "decided_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddles": { + "name": "huddles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_provider": { + "name": "league_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "league_id": { + "name": "league_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code_updated_at": { + "name": "invite_code_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invite_link_token": { + "name": "invite_link_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invite_link_expires_at": { + "name": "invite_link_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": { + "huddles_invite_code_uniq": { + "name": "huddles_invite_code_uniq", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddles_invite_link_token_uniq": { + "name": "huddles_invite_link_token_uniq", + "columns": [ + { + "expression": "invite_link_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_username": { + "name": "sleeper_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_user_id": { + "name": "sleeper_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_league_ids": { + "name": "synced_league_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_placeholder": { + "name": "is_placeholder", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "identity_synced_at": { + "name": "identity_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "users_username_idx": { + "name": "users_username_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.claim_status": { + "name": "claim_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected" + ] + }, + "public.poll_results_visibility": { + "name": "poll_results_visibility", + "schema": "public", + "values": [ + "always", + "after_vote", + "after_close" + ] + }, + "public.side_bet_status": { + "name": "side_bet_status", + "schema": "public", + "values": [ + "pending", + "accepted", + "rejected", + "cancelled", + "settled" + ] + }, + "public.survey_anonymity": { + "name": "survey_anonymity", + "schema": "public", + "values": [ + "none", + "anonymous_to_league", + "anonymous_to_all" + ] + }, + "public.survey_question_type": { + "name": "survey_question_type", + "schema": "public", + "values": [ + "short_text", + "paragraph", + "multiple_choice", + "checkboxes" + ] + } + }, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/drizzle/meta/0015_snapshot.json b/server/drizzle/meta/0015_snapshot.json new file mode 100644 index 0000000..9a8e0bf --- /dev/null +++ b/server/drizzle/meta/0015_snapshot.json @@ -0,0 +1,2644 @@ +{ + "id": "a0ea729f-abdb-42b6-8bfd-4ed4c0d8b1a9", + "prevId": "f9994cad-9a04-4600-976e-240953d6631c", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.active_trophies": { + "name": "active_trophies", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trophy_type": { + "name": "trophy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "active_trophies_huddle_id_huddles_id_fk": { + "name": "active_trophies_huddle_id_huddles_id_fk", + "tableFrom": "active_trophies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "active_trophies_huddle_id_trophy_type_pk": { + "name": "active_trophies_huddle_id_trophy_type_pk", + "columns": [ + "huddle_id", + "trophy_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.announcements": { + "name": "announcements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "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": { + "announcements_huddle_idx": { + "name": "announcements_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "announcements_huddle_id_huddles_id_fk": { + "name": "announcements_huddle_id_huddles_id_fk", + "tableFrom": "announcements", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "announcements_author_id_users_id_fk": { + "name": "announcements_author_id_users_id_fk", + "tableFrom": "announcements", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.awards": { + "name": "awards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "glyph": { + "name": "glyph", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "granted_by": { + "name": "granted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "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": { + "awards_huddle_idx": { + "name": "awards_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "awards_huddle_roster_idx": { + "name": "awards_huddle_roster_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "awards_huddle_id_huddles_id_fk": { + "name": "awards_huddle_id_huddles_id_fk", + "tableFrom": "awards", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "awards_granted_by_users_id_fk": { + "name": "awards_granted_by_users_id_fk", + "tableFrom": "awards", + "tableTo": "users", + "columnsFrom": [ + "granted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "commissioners_user_idx": { + "name": "commissioners_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "commissioners_huddle_id_huddles_id_fk": { + "name": "commissioners_huddle_id_huddles_id_fk", + "tableFrom": "commissioners", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "commissioners_user_id_users_id_fk": { + "name": "commissioners_user_id_users_id_fk", + "tableFrom": "commissioners", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "commissioners_added_by_users_id_fk": { + "name": "commissioners_added_by_users_id_fk", + "tableFrom": "commissioners", + "tableTo": "users", + "columnsFrom": [ + "added_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "commissioners_huddle_id_user_id_pk": { + "name": "commissioners_huddle_id_user_id_pk", + "columns": [ + "huddle_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.countdown_config": { + "name": "countdown_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_at": { + "name": "target_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "countdown_config_huddle_id_huddles_id_fk": { + "name": "countdown_config_huddle_id_huddles_id_fk", + "tableFrom": "countdown_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dues_config": { + "name": "dues_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "dues_config_huddle_id_huddles_id_fk": { + "name": "dues_config_huddle_id_huddles_id_fk", + "tableFrom": "dues_config", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dues_payments": { + "name": "dues_payments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "marked_by": { + "name": "marked_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dues_payments_huddle_roster_uniq": { + "name": "dues_payments_huddle_roster_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dues_payments_huddle_id_huddles_id_fk": { + "name": "dues_payments_huddle_id_huddles_id_fk", + "tableFrom": "dues_payments", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "dues_payments_marked_by_users_id_fk": { + "name": "dues_payments_marked_by_users_id_fk", + "tableFrom": "dues_payments", + "tableTo": "users", + "columnsFrom": [ + "marked_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forum_replies": { + "name": "forum_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forum_replies_topic_idx": { + "name": "forum_replies_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forum_replies_topic_id_forum_topics_id_fk": { + "name": "forum_replies_topic_id_forum_topics_id_fk", + "tableFrom": "forum_replies", + "tableTo": "forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "forum_replies_huddle_id_huddles_id_fk": { + "name": "forum_replies_huddle_id_huddles_id_fk", + "tableFrom": "forum_replies", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "forum_replies_author_id_users_id_fk": { + "name": "forum_replies_author_id_users_id_fk", + "tableFrom": "forum_replies", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "forum_replies_deleted_by_users_id_fk": { + "name": "forum_replies_deleted_by_users_id_fk", + "tableFrom": "forum_replies", + "tableTo": "users", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forum_topics": { + "name": "forum_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forum_topics_huddle_updated_idx": { + "name": "forum_topics_huddle_updated_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forum_topics_huddle_id_huddles_id_fk": { + "name": "forum_topics_huddle_id_huddles_id_fk", + "tableFrom": "forum_topics", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "forum_topics_author_id_users_id_fk": { + "name": "forum_topics_author_id_users_id_fk", + "tableFrom": "forum_topics", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "forum_topics_deleted_by_users_id_fk": { + "name": "forum_topics_deleted_by_users_id_fk", + "tableFrom": "forum_topics", + "tableTo": "users", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddles": { + "name": "huddles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_provider": { + "name": "league_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "league_id": { + "name": "league_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code_updated_at": { + "name": "invite_code_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invite_link_token": { + "name": "invite_link_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invite_link_expires_at": { + "name": "invite_link_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": { + "huddles_invite_code_uniq": { + "name": "huddles_invite_code_uniq", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "huddles_invite_link_token_uniq": { + "name": "huddles_invite_link_token_uniq", + "columns": [ + { + "expression": "invite_link_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payout_entries": { + "name": "payout_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "payout_entries_huddle_idx": { + "name": "payout_entries_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payout_entries_huddle_id_huddles_id_fk": { + "name": "payout_entries_huddle_id_huddles_id_fk", + "tableFrom": "payout_entries", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.poll_options": { + "name": "poll_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "poll_options_poll_idx": { + "name": "poll_options_poll_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "poll_options_poll_id_polls_id_fk": { + "name": "poll_options_poll_id_polls_id_fk", + "tableFrom": "poll_options", + "tableTo": "polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.poll_votes": { + "name": "poll_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "poll_votes_poll_user_idx": { + "name": "poll_votes_poll_user_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "poll_votes_option_user_uniq": { + "name": "poll_votes_option_user_uniq", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "poll_votes_poll_id_polls_id_fk": { + "name": "poll_votes_poll_id_polls_id_fk", + "tableFrom": "poll_votes", + "tableTo": "polls", + "columnsFrom": [ + "poll_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "poll_votes_option_id_poll_options_id_fk": { + "name": "poll_votes_option_id_poll_options_id_fk", + "tableFrom": "poll_votes", + "tableTo": "poll_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "poll_votes_user_id_users_id_fk": { + "name": "poll_votes_user_id_users_id_fk", + "tableFrom": "poll_votes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.polls": { + "name": "polls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_dashboard_poll": { + "name": "is_dashboard_poll", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allow_multiple": { + "name": "allow_multiple", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_vote_changes": { + "name": "allow_vote_changes", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "results_visibility": { + "name": "results_visibility", + "type": "poll_results_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'always'" + }, + "closes_at": { + "name": "closes_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": { + "polls_topic_idx": { + "name": "polls_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "polls_dashboard_active_uniq": { + "name": "polls_dashboard_active_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"polls\".\"is_dashboard_poll\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "polls_huddle_id_huddles_id_fk": { + "name": "polls_huddle_id_huddles_id_fk", + "tableFrom": "polls", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "polls_author_id_users_id_fk": { + "name": "polls_author_id_users_id_fk", + "tableFrom": "polls", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "polls_topic_id_forum_topics_id_fk": { + "name": "polls_topic_id_forum_topics_id_fk", + "tableFrom": "polls", + "tableTo": "forum_topics", + "columnsFrom": [ + "topic_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.side_bets": { + "name": "side_bets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "proposer_id": { + "name": "proposer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opponent_id": { + "name": "opponent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposer_roster_id": { + "name": "proposer_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "opponent_roster_id": { + "name": "opponent_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "week": { + "name": "week", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "prize_description": { + "name": "prize_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "side_bet_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "winner_id": { + "name": "winner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "settlement_note": { + "name": "settlement_note", + "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": { + "side_bets_huddle_idx": { + "name": "side_bets_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "side_bets_proposer_idx": { + "name": "side_bets_proposer_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "proposer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "side_bets_opponent_idx": { + "name": "side_bets_opponent_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "opponent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "side_bets_huddle_id_huddles_id_fk": { + "name": "side_bets_huddle_id_huddles_id_fk", + "tableFrom": "side_bets", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "side_bets_proposer_id_users_id_fk": { + "name": "side_bets_proposer_id_users_id_fk", + "tableFrom": "side_bets", + "tableTo": "users", + "columnsFrom": [ + "proposer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "side_bets_opponent_id_users_id_fk": { + "name": "side_bets_opponent_id_users_id_fk", + "tableFrom": "side_bets", + "tableTo": "users", + "columnsFrom": [ + "opponent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "side_bets_winner_id_users_id_fk": { + "name": "side_bets_winner_id_users_id_fk", + "tableFrom": "side_bets", + "tableTo": "users", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_answer_options": { + "name": "survey_answer_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "survey_answer_options_question_idx": { + "name": "survey_answer_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "survey_answer_options_response_option_uniq": { + "name": "survey_answer_options_response_option_uniq", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "survey_answer_options_response_id_survey_responses_id_fk": { + "name": "survey_answer_options_response_id_survey_responses_id_fk", + "tableFrom": "survey_answer_options", + "tableTo": "survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "survey_answer_options_question_id_survey_questions_id_fk": { + "name": "survey_answer_options_question_id_survey_questions_id_fk", + "tableFrom": "survey_answer_options", + "tableTo": "survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "survey_answer_options_option_id_survey_options_id_fk": { + "name": "survey_answer_options_option_id_survey_options_id_fk", + "tableFrom": "survey_answer_options", + "tableTo": "survey_options", + "columnsFrom": [ + "option_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_answers": { + "name": "survey_answers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "text_value": { + "name": "text_value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "survey_answers_response_idx": { + "name": "survey_answers_response_idx", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "survey_answers_question_idx": { + "name": "survey_answers_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "survey_answers_response_id_survey_responses_id_fk": { + "name": "survey_answers_response_id_survey_responses_id_fk", + "tableFrom": "survey_answers", + "tableTo": "survey_responses", + "columnsFrom": [ + "response_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "survey_answers_question_id_survey_questions_id_fk": { + "name": "survey_answers_question_id_survey_questions_id_fk", + "tableFrom": "survey_answers", + "tableTo": "survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_options": { + "name": "survey_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "survey_options_question_idx": { + "name": "survey_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "survey_options_question_id_survey_questions_id_fk": { + "name": "survey_options_question_id_survey_questions_id_fk", + "tableFrom": "survey_options", + "tableTo": "survey_questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_questions": { + "name": "survey_questions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "survey_question_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "survey_questions_survey_idx": { + "name": "survey_questions_survey_idx", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "survey_questions_survey_id_surveys_id_fk": { + "name": "survey_questions_survey_id_surveys_id_fk", + "tableFrom": "survey_questions", + "tableTo": "surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_responses": { + "name": "survey_responses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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": { + "survey_responses_survey_user_uniq": { + "name": "survey_responses_survey_user_uniq", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "survey_responses_survey_id_surveys_id_fk": { + "name": "survey_responses_survey_id_surveys_id_fk", + "tableFrom": "survey_responses", + "tableTo": "surveys", + "columnsFrom": [ + "survey_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "survey_responses_user_id_users_id_fk": { + "name": "survey_responses_user_id_users_id_fk", + "tableFrom": "survey_responses", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.surveys": { + "name": "surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "closes_at": { + "name": "closes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "results_published": { + "name": "results_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_publish_on_close": { + "name": "auto_publish_on_close", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "anonymity": { + "name": "anonymity", + "type": "survey_anonymity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "surveys_huddle_created_idx": { + "name": "surveys_huddle_created_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "surveys_huddle_id_huddles_id_fk": { + "name": "surveys_huddle_id_huddles_id_fk", + "tableFrom": "surveys", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "surveys_author_id_users_id_fk": { + "name": "surveys_author_id_users_id_fk", + "tableFrom": "surveys", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_claims": { + "name": "team_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "claim_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "team_claims_huddle_roster_approved_uniq": { + "name": "team_claims_huddle_roster_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_huddle_user_approved_uniq": { + "name": "team_claims_huddle_user_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_huddle_idx": { + "name": "team_claims_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_claims_user_idx": { + "name": "team_claims_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_claims_huddle_id_huddles_id_fk": { + "name": "team_claims_huddle_id_huddles_id_fk", + "tableFrom": "team_claims", + "tableTo": "huddles", + "columnsFrom": [ + "huddle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_claims_user_id_users_id_fk": { + "name": "team_claims_user_id_users_id_fk", + "tableFrom": "team_claims", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "team_claims_decided_by_users_id_fk": { + "name": "team_claims_decided_by_users_id_fk", + "tableFrom": "team_claims", + "tableTo": "users", + "columnsFrom": [ + "decided_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_username": { + "name": "sleeper_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_user_id": { + "name": "sleeper_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_league_ids": { + "name": "synced_league_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_placeholder": { + "name": "is_placeholder", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "identity_synced_at": { + "name": "identity_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_username_idx": { + "name": "users_username_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.claim_status": { + "name": "claim_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected" + ] + }, + "public.poll_results_visibility": { + "name": "poll_results_visibility", + "schema": "public", + "values": [ + "always", + "after_vote", + "after_close" + ] + }, + "public.side_bet_status": { + "name": "side_bet_status", + "schema": "public", + "values": [ + "pending", + "accepted", + "rejected", + "cancelled", + "settled" + ] + }, + "public.survey_anonymity": { + "name": "survey_anonymity", + "schema": "public", + "values": [ + "none", + "anonymous_to_league", + "anonymous_to_all" + ] + }, + "public.survey_question_type": { + "name": "survey_question_type", + "schema": "public", + "values": [ + "short_text", + "paragraph", + "multiple_choice", + "checkboxes" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/drizzle/meta/0016_snapshot.json b/server/drizzle/meta/0016_snapshot.json new file mode 100644 index 0000000..2ffc141 --- /dev/null +++ b/server/drizzle/meta/0016_snapshot.json @@ -0,0 +1,2644 @@ +{ + "id": "3c16b521-2e1c-4bbc-ad6a-c63e04b2e2b5", + "prevId": "a0ea729f-abdb-42b6-8bfd-4ed4c0d8b1a9", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.active_trophies": { + "name": "active_trophies", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trophy_type": { + "name": "trophy_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "active_trophies_huddle_id_huddles_id_fk": { + "name": "active_trophies_huddle_id_huddles_id_fk", + "tableFrom": "active_trophies", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "active_trophies_huddle_id_trophy_type_pk": { + "name": "active_trophies_huddle_id_trophy_type_pk", + "columns": [ + "huddle_id", + "trophy_type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.announcements": { + "name": "announcements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "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": { + "announcements_huddle_idx": { + "name": "announcements_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "announcements_huddle_id_huddles_id_fk": { + "name": "announcements_huddle_id_huddles_id_fk", + "tableFrom": "announcements", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "announcements_author_id_users_id_fk": { + "name": "announcements_author_id_users_id_fk", + "tableFrom": "announcements", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.awards": { + "name": "awards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "glyph": { + "name": "glyph", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "granted_by": { + "name": "granted_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "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": { + "awards_huddle_idx": { + "name": "awards_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "awards_huddle_roster_idx": { + "name": "awards_huddle_roster_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "awards_huddle_id_huddles_id_fk": { + "name": "awards_huddle_id_huddles_id_fk", + "tableFrom": "awards", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "awards_granted_by_users_id_fk": { + "name": "awards_granted_by_users_id_fk", + "tableFrom": "awards", + "columnsFrom": [ + "granted_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_at": { + "name": "added_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "commissioners_user_idx": { + "name": "commissioners_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "commissioners_huddle_id_huddles_id_fk": { + "name": "commissioners_huddle_id_huddles_id_fk", + "tableFrom": "commissioners", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "commissioners_user_id_users_id_fk": { + "name": "commissioners_user_id_users_id_fk", + "tableFrom": "commissioners", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "commissioners_added_by_users_id_fk": { + "name": "commissioners_added_by_users_id_fk", + "tableFrom": "commissioners", + "columnsFrom": [ + "added_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": { + "commissioners_huddle_id_user_id_pk": { + "name": "commissioners_huddle_id_user_id_pk", + "columns": [ + "huddle_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.countdown_config": { + "name": "countdown_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_at": { + "name": "target_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "countdown_config_huddle_id_huddles_id_fk": { + "name": "countdown_config_huddle_id_huddles_id_fk", + "tableFrom": "countdown_config", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dues_config": { + "name": "dues_config", + "schema": "", + "columns": { + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "dues_config_huddle_id_huddles_id_fk": { + "name": "dues_config_huddle_id_huddles_id_fk", + "tableFrom": "dues_config", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dues_payments": { + "name": "dues_payments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "marked_by": { + "name": "marked_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dues_payments_huddle_roster_uniq": { + "name": "dues_payments_huddle_roster_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "dues_payments_huddle_id_huddles_id_fk": { + "name": "dues_payments_huddle_id_huddles_id_fk", + "tableFrom": "dues_payments", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "dues_payments_marked_by_users_id_fk": { + "name": "dues_payments_marked_by_users_id_fk", + "tableFrom": "dues_payments", + "columnsFrom": [ + "marked_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forum_replies": { + "name": "forum_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forum_replies_topic_idx": { + "name": "forum_replies_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "forum_replies_topic_id_forum_topics_id_fk": { + "name": "forum_replies_topic_id_forum_topics_id_fk", + "tableFrom": "forum_replies", + "columnsFrom": [ + "topic_id" + ], + "tableTo": "forum_topics", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forum_replies_huddle_id_huddles_id_fk": { + "name": "forum_replies_huddle_id_huddles_id_fk", + "tableFrom": "forum_replies", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forum_replies_author_id_users_id_fk": { + "name": "forum_replies_author_id_users_id_fk", + "tableFrom": "forum_replies", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "forum_replies_deleted_by_users_id_fk": { + "name": "forum_replies_deleted_by_users_id_fk", + "tableFrom": "forum_replies", + "columnsFrom": [ + "deleted_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forum_topics": { + "name": "forum_topics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forum_topics_huddle_updated_idx": { + "name": "forum_topics_huddle_updated_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "forum_topics_huddle_id_huddles_id_fk": { + "name": "forum_topics_huddle_id_huddles_id_fk", + "tableFrom": "forum_topics", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "forum_topics_author_id_users_id_fk": { + "name": "forum_topics_author_id_users_id_fk", + "tableFrom": "forum_topics", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "forum_topics_deleted_by_users_id_fk": { + "name": "forum_topics_deleted_by_users_id_fk", + "tableFrom": "forum_topics", + "columnsFrom": [ + "deleted_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.huddles": { + "name": "huddles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_provider": { + "name": "league_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "league_id": { + "name": "league_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code_updated_at": { + "name": "invite_code_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invite_link_token": { + "name": "invite_link_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invite_link_expires_at": { + "name": "invite_link_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": { + "huddles_invite_code_uniq": { + "name": "huddles_invite_code_uniq", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "huddles_invite_link_token_uniq": { + "name": "huddles_invite_link_token_uniq", + "columns": [ + { + "expression": "invite_link_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payout_entries": { + "name": "payout_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "payout_entries_huddle_idx": { + "name": "payout_entries_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "payout_entries_huddle_id_huddles_id_fk": { + "name": "payout_entries_huddle_id_huddles_id_fk", + "tableFrom": "payout_entries", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.poll_options": { + "name": "poll_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "poll_options_poll_idx": { + "name": "poll_options_poll_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "poll_options_poll_id_polls_id_fk": { + "name": "poll_options_poll_id_polls_id_fk", + "tableFrom": "poll_options", + "columnsFrom": [ + "poll_id" + ], + "tableTo": "polls", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.poll_votes": { + "name": "poll_votes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "poll_id": { + "name": "poll_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "poll_votes_poll_user_idx": { + "name": "poll_votes_poll_user_idx", + "columns": [ + { + "expression": "poll_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "poll_votes_option_user_uniq": { + "name": "poll_votes_option_user_uniq", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "poll_votes_poll_id_polls_id_fk": { + "name": "poll_votes_poll_id_polls_id_fk", + "tableFrom": "poll_votes", + "columnsFrom": [ + "poll_id" + ], + "tableTo": "polls", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "poll_votes_option_id_poll_options_id_fk": { + "name": "poll_votes_option_id_poll_options_id_fk", + "tableFrom": "poll_votes", + "columnsFrom": [ + "option_id" + ], + "tableTo": "poll_options", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "poll_votes_user_id_users_id_fk": { + "name": "poll_votes_user_id_users_id_fk", + "tableFrom": "poll_votes", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.polls": { + "name": "polls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "topic_id": { + "name": "topic_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_dashboard_poll": { + "name": "is_dashboard_poll", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allow_multiple": { + "name": "allow_multiple", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_vote_changes": { + "name": "allow_vote_changes", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "results_visibility": { + "name": "results_visibility", + "type": "poll_results_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'always'" + }, + "closes_at": { + "name": "closes_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": { + "polls_topic_idx": { + "name": "polls_topic_idx", + "columns": [ + { + "expression": "topic_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "polls_dashboard_active_uniq": { + "name": "polls_dashboard_active_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"polls\".\"is_dashboard_poll\" = true", + "concurrently": false + } + }, + "foreignKeys": { + "polls_huddle_id_huddles_id_fk": { + "name": "polls_huddle_id_huddles_id_fk", + "tableFrom": "polls", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "polls_author_id_users_id_fk": { + "name": "polls_author_id_users_id_fk", + "tableFrom": "polls", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "polls_topic_id_forum_topics_id_fk": { + "name": "polls_topic_id_forum_topics_id_fk", + "tableFrom": "polls", + "columnsFrom": [ + "topic_id" + ], + "tableTo": "forum_topics", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.side_bets": { + "name": "side_bets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "proposer_id": { + "name": "proposer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opponent_id": { + "name": "opponent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposer_roster_id": { + "name": "proposer_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "opponent_roster_id": { + "name": "opponent_roster_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "week": { + "name": "week", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "season": { + "name": "season", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "prize_description": { + "name": "prize_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "side_bet_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "winner_id": { + "name": "winner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "settlement_note": { + "name": "settlement_note", + "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": { + "side_bets_huddle_idx": { + "name": "side_bets_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "side_bets_proposer_idx": { + "name": "side_bets_proposer_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "proposer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "side_bets_opponent_idx": { + "name": "side_bets_opponent_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "opponent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "side_bets_huddle_id_huddles_id_fk": { + "name": "side_bets_huddle_id_huddles_id_fk", + "tableFrom": "side_bets", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "side_bets_proposer_id_users_id_fk": { + "name": "side_bets_proposer_id_users_id_fk", + "tableFrom": "side_bets", + "columnsFrom": [ + "proposer_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "side_bets_opponent_id_users_id_fk": { + "name": "side_bets_opponent_id_users_id_fk", + "tableFrom": "side_bets", + "columnsFrom": [ + "opponent_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "side_bets_winner_id_users_id_fk": { + "name": "side_bets_winner_id_users_id_fk", + "tableFrom": "side_bets", + "columnsFrom": [ + "winner_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_answer_options": { + "name": "survey_answer_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "option_id": { + "name": "option_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "survey_answer_options_question_idx": { + "name": "survey_answer_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "survey_answer_options_response_option_uniq": { + "name": "survey_answer_options_response_option_uniq", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "survey_answer_options_response_id_survey_responses_id_fk": { + "name": "survey_answer_options_response_id_survey_responses_id_fk", + "tableFrom": "survey_answer_options", + "columnsFrom": [ + "response_id" + ], + "tableTo": "survey_responses", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "survey_answer_options_question_id_survey_questions_id_fk": { + "name": "survey_answer_options_question_id_survey_questions_id_fk", + "tableFrom": "survey_answer_options", + "columnsFrom": [ + "question_id" + ], + "tableTo": "survey_questions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "survey_answer_options_option_id_survey_options_id_fk": { + "name": "survey_answer_options_option_id_survey_options_id_fk", + "tableFrom": "survey_answer_options", + "columnsFrom": [ + "option_id" + ], + "tableTo": "survey_options", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_answers": { + "name": "survey_answers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "response_id": { + "name": "response_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "text_value": { + "name": "text_value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "survey_answers_response_idx": { + "name": "survey_answers_response_idx", + "columns": [ + { + "expression": "response_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "survey_answers_question_idx": { + "name": "survey_answers_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "survey_answers_response_id_survey_responses_id_fk": { + "name": "survey_answers_response_id_survey_responses_id_fk", + "tableFrom": "survey_answers", + "columnsFrom": [ + "response_id" + ], + "tableTo": "survey_responses", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "survey_answers_question_id_survey_questions_id_fk": { + "name": "survey_answers_question_id_survey_questions_id_fk", + "tableFrom": "survey_answers", + "columnsFrom": [ + "question_id" + ], + "tableTo": "survey_questions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_options": { + "name": "survey_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "question_id": { + "name": "question_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "survey_options_question_idx": { + "name": "survey_options_question_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "survey_options_question_id_survey_questions_id_fk": { + "name": "survey_options_question_id_survey_questions_id_fk", + "tableFrom": "survey_options", + "columnsFrom": [ + "question_id" + ], + "tableTo": "survey_questions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_questions": { + "name": "survey_questions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "survey_question_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "survey_questions_survey_idx": { + "name": "survey_questions_survey_idx", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "survey_questions_survey_id_surveys_id_fk": { + "name": "survey_questions_survey_id_surveys_id_fk", + "tableFrom": "survey_questions", + "columnsFrom": [ + "survey_id" + ], + "tableTo": "surveys", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.survey_responses": { + "name": "survey_responses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "survey_id": { + "name": "survey_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "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": { + "survey_responses_survey_user_uniq": { + "name": "survey_responses_survey_user_uniq", + "columns": [ + { + "expression": "survey_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "survey_responses_survey_id_surveys_id_fk": { + "name": "survey_responses_survey_id_surveys_id_fk", + "tableFrom": "survey_responses", + "columnsFrom": [ + "survey_id" + ], + "tableTo": "surveys", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "survey_responses_user_id_users_id_fk": { + "name": "survey_responses_user_id_users_id_fk", + "tableFrom": "survey_responses", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.surveys": { + "name": "surveys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "closes_at": { + "name": "closes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "results_published": { + "name": "results_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_publish_on_close": { + "name": "auto_publish_on_close", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "anonymity": { + "name": "anonymity", + "type": "survey_anonymity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "surveys_huddle_created_idx": { + "name": "surveys_huddle_created_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "surveys_huddle_id_huddles_id_fk": { + "name": "surveys_huddle_id_huddles_id_fk", + "tableFrom": "surveys", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "surveys_author_id_users_id_fk": { + "name": "surveys_author_id_users_id_fk", + "tableFrom": "surveys", + "columnsFrom": [ + "author_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_claims": { + "name": "team_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "huddle_id": { + "name": "huddle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "roster_id": { + "name": "roster_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "claim_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "team_claims_huddle_roster_approved_uniq": { + "name": "team_claims_huddle_roster_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "roster_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false + }, + "team_claims_huddle_user_approved_uniq": { + "name": "team_claims_huddle_user_approved_uniq", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"team_claims\".\"status\" = 'approved'", + "concurrently": false + }, + "team_claims_huddle_idx": { + "name": "team_claims_huddle_idx", + "columns": [ + { + "expression": "huddle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "team_claims_user_idx": { + "name": "team_claims_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "team_claims_huddle_id_huddles_id_fk": { + "name": "team_claims_huddle_id_huddles_id_fk", + "tableFrom": "team_claims", + "columnsFrom": [ + "huddle_id" + ], + "tableTo": "huddles", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "team_claims_user_id_users_id_fk": { + "name": "team_claims_user_id_users_id_fk", + "tableFrom": "team_claims", + "columnsFrom": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "team_claims_decided_by_users_id_fk": { + "name": "team_claims_decided_by_users_id_fk", + "tableFrom": "team_claims", + "columnsFrom": [ + "decided_by" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_username": { + "name": "sleeper_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sleeper_user_id": { + "name": "sleeper_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_league_ids": { + "name": "synced_league_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_placeholder": { + "name": "is_placeholder", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "identity_synced_at": { + "name": "identity_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "users_username_idx": { + "name": "users_username_idx", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.claim_status": { + "name": "claim_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected" + ] + }, + "public.poll_results_visibility": { + "name": "poll_results_visibility", + "schema": "public", + "values": [ + "always", + "after_vote", + "after_close" + ] + }, + "public.side_bet_status": { + "name": "side_bet_status", + "schema": "public", + "values": [ + "pending", + "accepted", + "rejected", + "cancelled", + "settled" + ] + }, + "public.survey_anonymity": { + "name": "survey_anonymity", + "schema": "public", + "values": [ + "none", + "anonymous_to_league", + "anonymous_to_all" + ] + }, + "public.survey_question_type": { + "name": "survey_question_type", + "schema": "public", + "values": [ + "short_text", + "paragraph", + "multiple_choice", + "checkboxes" + ] + } + }, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/drizzle/meta/_journal.json b/server/drizzle/meta/_journal.json index 6033dc1..ba2d22a 100644 --- a/server/drizzle/meta/_journal.json +++ b/server/drizzle/meta/_journal.json @@ -78,6 +78,48 @@ "when": 1785791787209, "tag": "0010_romantic_captain_america", "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1786314864383, + "tag": "0011_sticky_shockwave", + "breakpoints": true + }, + { + "idx": 12, + "version": "7", + "when": 1786315059916, + "tag": "0012_green_valkyrie", + "breakpoints": true + }, + { + "idx": 13, + "version": "7", + "when": 1786317198957, + "tag": "0013_hot_cannonball", + "breakpoints": true + }, + { + "idx": 14, + "version": "7", + "when": 1786317326520, + "tag": "0014_rename_legacy_pkeys", + "breakpoints": true + }, + { + "idx": 15, + "version": "7", + "when": 1786317846534, + "tag": "0015_drop_huddle_prefix", + "breakpoints": true + }, + { + "idx": 16, + "version": "7", + "when": 1786318032277, + "tag": "0016_fix_truncated_fk_name", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/package.json b/server/package.json index bd90671..dbe9922 100644 --- a/server/package.json +++ b/server/package.json @@ -10,7 +10,9 @@ "db:push": "dotenv -e ../.env -- drizzle-kit push", "db:studio": "dotenv -e ../.env -- drizzle-kit studio", "db:migrate": "dotenv -e ../.env -- node scripts/migrate.mjs", - "db:migrate:prod": "dotenv -e .env.production -- node scripts/migrate.mjs" + "db:migrate:prod": "dotenv -e .env.production -- node scripts/migrate.mjs", + "db:backfill-users": "dotenv -e ../.env -- node scripts/backfill-users.mjs", + "db:backfill-users:prod": "dotenv -e .env.production -e ../.env -- node scripts/backfill-users.mjs" }, "dependencies": { "@clerk/express": "^1.7.0", diff --git a/server/scripts/backfill-users.mjs b/server/scripts/backfill-users.mjs new file mode 100644 index 0000000..eb0e89b --- /dev/null +++ b/server/scripts/backfill-users.mjs @@ -0,0 +1,161 @@ +/** + * Mirrors Clerk's user list into our own `users` table. + * + * This serves two purposes, and is safe to run repeatedly for both: + * + * 1. **One-time backfill.** Before the foreign keys in migration 0012 can be + * applied, every user id already referenced by our data needs a `users` + * row. That includes ids belonging to *deleted* Clerk accounts, which get + * a placeholder row so attribution ("posted by") survives. + * + * 2. **Recurring snapshot.** Run it on a schedule and a Clerk outage — or a + * Clerk exit — never costs us the identity data. Nothing here is + * destructive. + * + * Re-run safety: identity fields (email, username) are always refreshed from + * Clerk, because Clerk owns them. Sleeper fields and syncedLeagueIds are only + * written when our row doesn't already have a value — after the cutover *we* + * own those, and Clerk's `unsafeMetadata` copy is frozen at migration time. + * Overwriting from it would silently roll back a user's later edits. + * + * Usage: + * npm run db:backfill-users --prefix server (dev DB, from ../.env) + * npm run db:backfill-users:prod --prefix server (prod DB, from .env.production) + */ +import { neon } from "@neondatabase/serverless"; +import { createClerkClient } from "@clerk/express"; + +const url = process.env.DATABASE_URL; +if (!url) throw new Error("Missing DATABASE_URL"); +const secretKey = process.env.CLERK_SECRET_KEY; +if (!secretKey) throw new Error("Missing CLERK_SECRET_KEY"); + +const sql = neon(url); +const clerk = createClerkClient({ secretKey }); + +/** + * Every column in the schema that holds a user id. Keep this in sync with the + * foreign keys in schema.ts — if you add a new user-referencing column, it + * belongs here too, or the next FK migration will fail on orphaned rows. + */ +const USER_ID_COLUMNS = [ + ["commissioners", "user_id"], + ["commissioners", "added_by"], + ["team_claims", "user_id"], + ["team_claims", "decided_by"], + ["announcements", "author_id"], + ["dues_payments", "marked_by"], + ["awards", "granted_by"], + ["side_bets", "proposer_id"], + ["side_bets", "opponent_id"], + ["side_bets", "winner_id"], + ["forum_topics", "author_id"], + ["forum_topics", "deleted_by"], + ["forum_replies", "author_id"], + ["forum_replies", "deleted_by"], + ["polls", "author_id"], + ["poll_votes", "user_id"], + ["surveys", "author_id"], + ["survey_responses", "user_id"], +]; + +const PAGE_SIZE = 500; + +async function fetchAllClerkUsers() { + const all = []; + for (let offset = 0; ; offset += PAGE_SIZE) { + const page = await clerk.users.getUserList({ limit: PAGE_SIZE, offset }); + all.push(...page.data); + if (page.data.length < PAGE_SIZE) break; + } + return all; +} + +async function upsertFromClerk(u) { + const meta = u.unsafeMetadata ?? {}; + const email = + u.primaryEmailAddress?.emailAddress ?? + u.emailAddresses?.[0]?.emailAddress ?? + null; + const syncedLeagueIds = Array.isArray(meta.syncedLeagueIds) + ? meta.syncedLeagueIds.filter((id) => typeof id === "string") + : []; + + await sql.query( + `insert into users + (id, email, username, sleeper_username, sleeper_user_id, + synced_league_ids, is_placeholder, identity_synced_at) + values ($1, $2, $3, $4, $5, $6, false, now()) + on conflict (id) do update set + email = excluded.email, + username = excluded.username, + -- ours wins: only fill what we don't already have + sleeper_username = coalesce(users.sleeper_username, excluded.sleeper_username), + sleeper_user_id = coalesce(users.sleeper_user_id, excluded.sleeper_user_id), + synced_league_ids = case + when coalesce(array_length(users.synced_league_ids, 1), 0) = 0 + then excluded.synced_league_ids + else users.synced_league_ids + end, + is_placeholder = false, + identity_synced_at = now(), + updated_at = now()`, + [ + u.id, + email, + u.username ?? null, + typeof meta.sleeperUsername === "string" ? meta.sleeperUsername : null, + typeof meta.sleeperUserId === "string" ? meta.sleeperUserId : null, + syncedLeagueIds, + ], + ); +} + +/** User ids that appear somewhere in our data but have no `users` row. */ +async function findOrphanIds() { + const union = USER_ID_COLUMNS.map( + ([table, col]) => + `select distinct "${col}" as id from "${table}" where "${col}" is not null`, + ).join(" union "); + + const rows = await sql.query( + `select id from (${union}) refs + where not exists (select 1 from users u where u.id = refs.id)`, + ); + return rows.map((r) => r.id); +} + +console.log("Fetching users from Clerk…"); +const clerkUsers = await fetchAllClerkUsers(); +console.log(` ${clerkUsers.length} Clerk user(s).`); + +let mirrored = 0; +for (const u of clerkUsers) { + await upsertFromClerk(u); + mirrored++; +} +console.log(` Mirrored ${mirrored} user(s) into "users".`); + +console.log("Scanning for user ids referenced by data but absent from Clerk…"); +const orphans = await findOrphanIds(); + +if (orphans.length === 0) { + console.log(" None — every referenced user id has a row."); +} else { + for (const id of orphans) { + // DO NOTHING, not DO UPDATE: if a row already exists we must never + // downgrade a real user to a placeholder. + await sql.query( + `insert into users (id, is_placeholder) values ($1, true) + on conflict (id) do nothing`, + [id], + ); + } + console.log( + ` Created ${orphans.length} placeholder row(s) for deleted/unknown accounts:`, + ); + for (const id of orphans) console.log(` ${id}`); +} + +const [{ n }] = await sql.query(`select count(*)::int as n from users`); +console.log(`\nDone. "users" now holds ${n} row(s).`); diff --git a/server/src/db/schema.ts b/server/src/db/schema.ts index 6bbb1e9..5fe1e35 100644 --- a/server/src/db/schema.ts +++ b/server/src/db/schema.ts @@ -18,6 +18,68 @@ export const claimStatus = pgEnum("claim_status", [ "rejected", ]); +// ── Users ───────────────────────────────────────────────────────────────────── +// Our own mirror of the identity provider's user record, and the home for the +// product data that used to live in Clerk's `unsafeMetadata`. +// +// `id` is deliberately the *Clerk* user id (`user_2ab...`), not a fresh uuid. +// Every other table stores that same string in its `user_id`/`author_id`/etc. +// column, so keeping it as the primary key means swapping auth providers later +// is "issue our own JWTs with the same `sub`" rather than a rewrite of 18 +// columns across 11 tables. Do not renumber these — see PLAYBOOK, "Users and +// auth ownership". +// +// Identity fields (email, username) are a cache of the provider's copy, +// refreshed by usersService.ensureUser on a TTL. Sleeper fields and +// syncedLeagueIds are *owned* here — Clerk no longer holds them. +// +// All 18 references to this table are `ON DELETE RESTRICT`, deliberately: a +// user's forum posts, votes and survey answers are league history and must +// outlive their account. To "delete" a user, blank the identity fields and set +// isPlaceholder — never DELETE the row, or Postgres will (correctly) refuse. + +export const users = pgTable( + "users", + { + /** Clerk user id. Also the join key used by every other table. */ + id: text("id").primaryKey(), + /** Mirrored from Clerk. Nullable because Clerk doesn't guarantee either. */ + email: text("email"), + username: text("username"), + /** Sleeper account link — migrated out of Clerk `unsafeMetadata`. */ + sleeperUsername: text("sleeper_username"), + sleeperUserId: text("sleeper_user_id"), + /** League ids the user has opted into syncing. */ + syncedLeagueIds: text("synced_league_ids") + .array() + .notNull() + .default(sql`'{}'::text[]`), + /** + * True for rows created by the backfill for a user id that appears in our + * data but no longer exists in Clerk (deleted account). Keeps the foreign + * keys satisfiable without inventing fake identity data. + */ + isPlaceholder: boolean("is_placeholder").notNull().default(false), + /** Last time email/username were refreshed from Clerk. */ + identitySyncedAt: timestamp("identity_synced_at", { withTimezone: true }) + .defaultNow() + .notNull(), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (t) => ({ + byEmail: index("users_email_idx").on(t.email), + byUsername: index("users_username_idx").on(t.username), + }), +); + +export type User = typeof users.$inferSelect; +export type NewUser = typeof users.$inferInsert; + export const huddles = pgTable( "huddles", { @@ -48,21 +110,25 @@ export const huddles = pgTable( }), ); -export const huddleCommissioners = pgTable( - "huddle_commissioners", +export const commissioners = pgTable( + "commissioners", { huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - userId: text("user_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), addedAt: timestamp("added_at", { withTimezone: true }) .defaultNow() .notNull(), - addedBy: text("added_by").notNull(), + addedBy: text("added_by") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), }, (t) => ({ pk: primaryKey({ columns: [t.huddleId, t.userId] }), - byUser: index("huddle_commissioners_user_idx").on(t.userId), + byUser: index("commissioners_user_idx").on(t.userId), }), ); @@ -73,7 +139,9 @@ export const teamClaims = pgTable( huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - userId: text("user_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), rosterId: integer("roster_id").notNull(), status: claimStatus("status").notNull().default("pending"), message: text("message"), @@ -81,7 +149,9 @@ export const teamClaims = pgTable( .defaultNow() .notNull(), decidedAt: timestamp("decided_at", { withTimezone: true }), - decidedBy: text("decided_by"), + decidedBy: text("decided_by").references(() => users.id, { + onDelete: "restrict", + }), }, (t) => ({ uniqApprovedRoster: uniqueIndex("team_claims_huddle_roster_approved_uniq") @@ -97,14 +167,16 @@ export const teamClaims = pgTable( // ── Announcements ───────────────────────────────────────────────────────────── -export const huddleAnnouncements = pgTable( - "huddle_announcements", +export const announcements = pgTable( + "announcements", { id: uuid("id").defaultRandom().primaryKey(), huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - authorId: text("author_id").notNull(), + authorId: text("author_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), title: text("title").notNull(), body: text("body").notNull(), createdAt: timestamp("created_at", { withTimezone: true }) @@ -115,13 +187,13 @@ export const huddleAnnouncements = pgTable( .notNull(), }, (t) => ({ - byHuddle: index("huddle_announcements_huddle_idx").on(t.huddleId), + byHuddle: index("announcements_huddle_idx").on(t.huddleId), }), ); // ── Dues tracker ────────────────────────────────────────────────────────────── -export const huddleDuesConfig = pgTable("huddle_dues_config", { +export const duesConfig = pgTable("dues_config", { huddleId: uuid("huddle_id") .primaryKey() .references(() => huddles.id, { onDelete: "cascade" }), @@ -133,8 +205,8 @@ export const huddleDuesConfig = pgTable("huddle_dues_config", { .notNull(), }); -export const huddleDuesPayments = pgTable( - "huddle_dues_payments", +export const duesPayments = pgTable( + "dues_payments", { id: uuid("id").defaultRandom().primaryKey(), huddleId: uuid("huddle_id") @@ -142,14 +214,16 @@ export const huddleDuesPayments = pgTable( .references(() => huddles.id, { onDelete: "cascade" }), rosterId: integer("roster_id").notNull(), paidAt: timestamp("paid_at", { withTimezone: true }), - markedBy: text("marked_by").notNull(), + markedBy: text("marked_by") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), note: text("note"), updatedAt: timestamp("updated_at", { withTimezone: true }) .defaultNow() .notNull(), }, (t) => ({ - uniqRoster: uniqueIndex("huddle_dues_payments_huddle_roster_uniq").on( + uniqRoster: uniqueIndex("dues_payments_huddle_roster_uniq").on( t.huddleId, t.rosterId, ), @@ -158,18 +232,18 @@ export const huddleDuesPayments = pgTable( export type Huddle = typeof huddles.$inferSelect; export type NewHuddle = typeof huddles.$inferInsert; -export type HuddleCommissioner = typeof huddleCommissioners.$inferSelect; +export type Commissioner = typeof commissioners.$inferSelect; export type TeamClaim = typeof teamClaims.$inferSelect; export type NewTeamClaim = typeof teamClaims.$inferInsert; -export type HuddleAnnouncement = typeof huddleAnnouncements.$inferSelect; -export type NewHuddleAnnouncement = typeof huddleAnnouncements.$inferInsert; -export type HuddleDuesConfig = typeof huddleDuesConfig.$inferSelect; -export type HuddleDuesPayment = typeof huddleDuesPayments.$inferSelect; +export type Announcement = typeof announcements.$inferSelect; +export type NewAnnouncement = typeof announcements.$inferInsert; +export type DuesConfig = typeof duesConfig.$inferSelect; +export type DuesPayment = typeof duesPayments.$inferSelect; // ── Custom awards ───────────────────────────────────────────────────────────── -export const huddleAwards = pgTable( - "huddle_awards", +export const awards = pgTable( + "awards", { id: uuid("id").defaultRandom().primaryKey(), huddleId: uuid("huddle_id") @@ -180,7 +254,9 @@ export const huddleAwards = pgTable( color: text("color").notNull(), title: text("title").notNull(), description: text("description"), - grantedBy: text("granted_by").notNull(), + grantedBy: text("granted_by") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), season: text("season"), createdAt: timestamp("created_at", { withTimezone: true }) .defaultNow() @@ -190,18 +266,18 @@ export const huddleAwards = pgTable( .notNull(), }, (t) => ({ - byHuddle: index("huddle_awards_huddle_idx").on(t.huddleId), - byHuddleRoster: index("huddle_awards_huddle_roster_idx").on(t.huddleId, t.rosterId), + byHuddle: index("awards_huddle_idx").on(t.huddleId), + byHuddleRoster: index("awards_huddle_roster_idx").on(t.huddleId, t.rosterId), }), ); -export type HuddleAward = typeof huddleAwards.$inferSelect; -export type NewHuddleAward = typeof huddleAwards.$inferInsert; +export type Award = typeof awards.$inferSelect; +export type NewAward = typeof awards.$inferInsert; // ── Payout structure ────────────────────────────────────────────────────────── -export const huddlePayoutEntries = pgTable( - "huddle_payout_entries", +export const payoutEntries = pgTable( + "payout_entries", { id: uuid("id").defaultRandom().primaryKey(), huddleId: uuid("huddle_id") @@ -218,27 +294,27 @@ export const huddlePayoutEntries = pgTable( .notNull(), }, (t) => ({ - byHuddle: index("huddle_payout_entries_huddle_idx").on(t.huddleId), + byHuddle: index("payout_entries_huddle_idx").on(t.huddleId), }), ); -export type HuddlePayoutEntry = typeof huddlePayoutEntries.$inferSelect; -export type NewHuddlePayoutEntry = typeof huddlePayoutEntries.$inferInsert; +export type PayoutEntry = typeof payoutEntries.$inferSelect; +export type NewPayoutEntry = typeof payoutEntries.$inferInsert; // ── Active trophy control ───────────────────────────────────────────────────── // Commissioners choose which built-in auto-trophies are shown for their league. // A row exists for each type the commissioner has explicitly toggled; if no row // exists, the trophy is considered enabled (opt-out model, not opt-in). -export const huddleActiveTrophies = pgTable( - "huddle_active_trophies", +export const activeTrophies = pgTable( + "active_trophies", { huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), /** Built-in trophy type key, e.g. "champion", "high_score". */ trophyType: text("trophy_type").notNull(), - enabled: integer("enabled").notNull().default(1), // 1 = on, 0 = off + enabled: boolean("enabled").notNull().default(true), updatedAt: timestamp("updated_at", { withTimezone: true }) .defaultNow() .notNull(), @@ -248,7 +324,7 @@ export const huddleActiveTrophies = pgTable( }), ); -export type HuddleActiveTrophy = typeof huddleActiveTrophies.$inferSelect; +export type ActiveTrophy = typeof activeTrophies.$inferSelect; // ── Side bets ────────────────────────────────────────────────────────────────── // League members propose bets against each other, tied to a specific week. @@ -269,8 +345,12 @@ export const sideBets = pgTable( huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - proposerId: text("proposer_id").notNull(), - opponentId: text("opponent_id").notNull(), + proposerId: text("proposer_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), + opponentId: text("opponent_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), proposerRosterId: integer("proposer_roster_id"), opponentRosterId: integer("opponent_roster_id"), week: integer("week").notNull(), @@ -280,8 +360,10 @@ export const sideBets = pgTable( /** Free-text prize description when the wager isn't cash (e.g. "loser buys dinner"). Null for cash bets. */ prizeDescription: text("prize_description"), status: sideBetStatus("status").notNull().default("pending"), - /** Clerk userId of the winner. Null until settled. */ - winnerId: text("winner_id"), + /** Winner. Null until settled. */ + winnerId: text("winner_id").references(() => users.id, { + onDelete: "restrict", + }), settlementNote: text("settlement_note"), createdAt: timestamp("created_at", { withTimezone: true }) .defaultNow() @@ -304,7 +386,7 @@ export type NewSideBet = typeof sideBets.$inferInsert; // Commissioner-configured countdown shown on the dashboard, e.g. counting down // to draft night or the trade deadline. One row per huddle. -export const huddleCountdownConfig = pgTable("huddle_countdown_config", { +export const countdownConfig = pgTable("countdown_config", { huddleId: uuid("huddle_id") .primaryKey() .references(() => huddles.id, { onDelete: "cascade" }), @@ -317,7 +399,7 @@ export const huddleCountdownConfig = pgTable("huddle_countdown_config", { .notNull(), }); -export type HuddleCountdownConfig = typeof huddleCountdownConfig.$inferSelect; +export type CountdownConfig = typeof countdownConfig.$inferSelect; // ── League forum ────────────────────────────────────────────────────────────── // Message-board style discussion, scoped to a huddle (persists across seasons). @@ -325,14 +407,16 @@ export type HuddleCountdownConfig = typeof huddleCountdownConfig.$inferSelect; // Deletes are soft (deletedAt/deletedBy) so pagination stays stable and // moderation leaves an audit trail. -export const huddleForumTopics = pgTable( - "huddle_forum_topics", +export const forumTopics = pgTable( + "forum_topics", { id: uuid("id").defaultRandom().primaryKey(), huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - authorId: text("author_id").notNull(), + authorId: text("author_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), title: text("title").notNull(), body: text("body").notNull(), /** Bumped whenever a reply is posted, for "recent activity" sort. */ @@ -345,42 +429,48 @@ export const huddleForumTopics = pgTable( .defaultNow() .notNull(), deletedAt: timestamp("deleted_at", { withTimezone: true }), - deletedBy: text("deleted_by"), + deletedBy: text("deleted_by").references(() => users.id, { + onDelete: "restrict", + }), }, (t) => ({ - byHuddleActivity: index("huddle_forum_topics_huddle_updated_idx").on( + byHuddleActivity: index("forum_topics_huddle_updated_idx").on( t.huddleId, t.updatedAt, ), }), ); -export const huddleForumReplies = pgTable( - "huddle_forum_replies", +export const forumReplies = pgTable( + "forum_replies", { id: uuid("id").defaultRandom().primaryKey(), topicId: uuid("topic_id") .notNull() - .references(() => huddleForumTopics.id, { onDelete: "cascade" }), + .references(() => forumTopics.id, { onDelete: "cascade" }), /** Denormalized from the topic so replies can be queried/indexed directly. */ huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - authorId: text("author_id").notNull(), + authorId: text("author_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), body: text("body").notNull(), createdAt: timestamp("created_at", { withTimezone: true }) .defaultNow() .notNull(), deletedAt: timestamp("deleted_at", { withTimezone: true }), - deletedBy: text("deleted_by"), + deletedBy: text("deleted_by").references(() => users.id, { + onDelete: "restrict", + }), }, (t) => ({ - byTopic: index("huddle_forum_replies_topic_idx").on(t.topicId, t.createdAt), + byTopic: index("forum_replies_topic_idx").on(t.topicId, t.createdAt), }), ); -export type HuddleForumTopic = typeof huddleForumTopics.$inferSelect; -export type HuddleForumReply = typeof huddleForumReplies.$inferSelect; +export type ForumTopic = typeof forumTopics.$inferSelect; +export type ForumReply = typeof forumReplies.$inferSelect; // ── Polls ───────────────────────────────────────────────────────────────────── // Two contexts share the same tables: a poll attached to a forum topic @@ -396,15 +486,17 @@ export const pollResultsVisibility = pgEnum("poll_results_visibility", [ "after_close", // results hidden until the poll's closesAt has passed ]); -export const huddlePolls = pgTable( - "huddle_polls", +export const polls = pgTable( + "polls", { id: uuid("id").defaultRandom().primaryKey(), huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - authorId: text("author_id").notNull(), - topicId: uuid("topic_id").references(() => huddleForumTopics.id, { onDelete: "cascade" }), + authorId: text("author_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), + topicId: uuid("topic_id").references(() => forumTopics.id, { onDelete: "cascade" }), isDashboardPoll: boolean("is_dashboard_poll").notNull().default(false), question: text("question").notNull(), allowMultiple: boolean("allow_multiple").notNull().default(false), @@ -417,63 +509,65 @@ export const huddlePolls = pgTable( .notNull(), }, (t) => ({ - byTopic: index("huddle_polls_topic_idx").on(t.topicId), + byTopic: index("polls_topic_idx").on(t.topicId), // Enforces "at most one active dashboard poll per huddle" at the DB level. - uniqActiveDashboardPoll: uniqueIndex("huddle_polls_dashboard_active_uniq") + uniqActiveDashboardPoll: uniqueIndex("polls_dashboard_active_uniq") .on(t.huddleId) .where(sql`${t.isDashboardPoll} = true`), }), ); -export const huddlePollOptions = pgTable( - "huddle_poll_options", +export const pollOptions = pgTable( + "poll_options", { id: uuid("id").defaultRandom().primaryKey(), pollId: uuid("poll_id") .notNull() - .references(() => huddlePolls.id, { onDelete: "cascade" }), + .references(() => polls.id, { onDelete: "cascade" }), label: text("label").notNull(), sortOrder: integer("sort_order").notNull().default(0), }, (t) => ({ - byPoll: index("huddle_poll_options_poll_idx").on(t.pollId), + byPoll: index("poll_options_poll_idx").on(t.pollId), }), ); -export const huddlePollVotes = pgTable( - "huddle_poll_votes", +export const pollVotes = pgTable( + "poll_votes", { id: uuid("id").defaultRandom().primaryKey(), pollId: uuid("poll_id") .notNull() - .references(() => huddlePolls.id, { onDelete: "cascade" }), + .references(() => polls.id, { onDelete: "cascade" }), optionId: uuid("option_id") .notNull() - .references(() => huddlePollOptions.id, { onDelete: "cascade" }), - userId: text("user_id").notNull(), + .references(() => pollOptions.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), createdAt: timestamp("created_at", { withTimezone: true }) .defaultNow() .notNull(), }, (t) => ({ - byPollUser: index("huddle_poll_votes_poll_user_idx").on(t.pollId, t.userId), - uniqOptionVote: uniqueIndex("huddle_poll_votes_option_user_uniq").on(t.optionId, t.userId), + byPollUser: index("poll_votes_poll_user_idx").on(t.pollId, t.userId), + uniqOptionVote: uniqueIndex("poll_votes_option_user_uniq").on(t.optionId, t.userId), }), ); -export type HuddlePoll = typeof huddlePolls.$inferSelect; -export type HuddlePollOption = typeof huddlePollOptions.$inferSelect; -export type HuddlePollVote = typeof huddlePollVotes.$inferSelect; +export type Poll = typeof polls.$inferSelect; +export type PollOption = typeof pollOptions.$inferSelect; +export type PollVote = typeof pollVotes.$inferSelect; // ── Surveys ─────────────────────────────────────────────────────────────────── // A commissioner-authored, multi-question form (short/long text or // single/multi-select), scoped to a huddle. Modeled as "a poll with several -// questions": huddleSurveyOptions plays the role of huddlePollOptions, and -// huddleSurveyAnswerOptions plays the role of huddlePollVotes (one row per -// selected option). Text-type answers live in the separate huddleSurveyAnswers +// questions": surveyOptions plays the role of pollOptions, and +// surveyAnswerOptions plays the role of pollVotes (one row per +// selected option). Text-type answers live in the separate surveyAnswers // table since they don't reference an option row. // -// Each member gets at most one huddleSurveyResponses row per survey; editing +// Each member gets at most one surveyResponses row per survey; editing // a response deletes and reinserts its answers (see surveyService.submitResponse), // mirroring pollService.castVote. Editing is only allowed before closesAt. @@ -498,14 +592,16 @@ export const surveyAnonymity = pgEnum("survey_anonymity", [ "anonymous_to_all", ]); -export const huddleSurveys = pgTable( - "huddle_surveys", +export const surveys = pgTable( + "surveys", { id: uuid("id").defaultRandom().primaryKey(), huddleId: uuid("huddle_id") .notNull() .references(() => huddles.id, { onDelete: "cascade" }), - authorId: text("author_id").notNull(), + authorId: text("author_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), title: text("title").notNull(), description: text("description"), /** Responses can no longer be submitted or edited once this passes. */ @@ -520,51 +616,53 @@ export const huddleSurveys = pgTable( .notNull(), }, (t) => ({ - byHuddleActivity: index("huddle_surveys_huddle_created_idx").on(t.huddleId, t.createdAt), + byHuddleActivity: index("surveys_huddle_created_idx").on(t.huddleId, t.createdAt), }), ); -export const huddleSurveyQuestions = pgTable( - "huddle_survey_questions", +export const surveyQuestions = pgTable( + "survey_questions", { id: uuid("id").defaultRandom().primaryKey(), surveyId: uuid("survey_id") .notNull() - .references(() => huddleSurveys.id, { onDelete: "cascade" }), + .references(() => surveys.id, { onDelete: "cascade" }), type: surveyQuestionType("type").notNull(), prompt: text("prompt").notNull(), required: boolean("required").notNull().default(true), sortOrder: integer("sort_order").notNull().default(0), }, (t) => ({ - bySurvey: index("huddle_survey_questions_survey_idx").on(t.surveyId, t.sortOrder), + bySurvey: index("survey_questions_survey_idx").on(t.surveyId, t.sortOrder), }), ); /** Choices for multiple_choice / checkboxes questions. */ -export const huddleSurveyOptions = pgTable( - "huddle_survey_options", +export const surveyOptions = pgTable( + "survey_options", { id: uuid("id").defaultRandom().primaryKey(), questionId: uuid("question_id") .notNull() - .references(() => huddleSurveyQuestions.id, { onDelete: "cascade" }), + .references(() => surveyQuestions.id, { onDelete: "cascade" }), label: text("label").notNull(), sortOrder: integer("sort_order").notNull().default(0), }, (t) => ({ - byQuestion: index("huddle_survey_options_question_idx").on(t.questionId), + byQuestion: index("survey_options_question_idx").on(t.questionId), }), ); -export const huddleSurveyResponses = pgTable( - "huddle_survey_responses", +export const surveyResponses = pgTable( + "survey_responses", { id: uuid("id").defaultRandom().primaryKey(), surveyId: uuid("survey_id") .notNull() - .references(() => huddleSurveys.id, { onDelete: "cascade" }), - userId: text("user_id").notNull(), + .references(() => surveys.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "restrict" }), createdAt: timestamp("created_at", { withTimezone: true }) .defaultNow() .notNull(), @@ -573,7 +671,7 @@ export const huddleSurveyResponses = pgTable( .notNull(), }, (t) => ({ - uniqSurveyUser: uniqueIndex("huddle_survey_responses_survey_user_uniq").on( + uniqSurveyUser: uniqueIndex("survey_responses_survey_user_uniq").on( t.surveyId, t.userId, ), @@ -581,51 +679,51 @@ export const huddleSurveyResponses = pgTable( ); /** short_text / paragraph answers — one row per question per response. */ -export const huddleSurveyAnswers = pgTable( - "huddle_survey_answers", +export const surveyAnswers = pgTable( + "survey_answers", { id: uuid("id").defaultRandom().primaryKey(), responseId: uuid("response_id") .notNull() - .references(() => huddleSurveyResponses.id, { onDelete: "cascade" }), + .references(() => surveyResponses.id, { onDelete: "cascade" }), questionId: uuid("question_id") .notNull() - .references(() => huddleSurveyQuestions.id, { onDelete: "cascade" }), + .references(() => surveyQuestions.id, { onDelete: "cascade" }), textValue: text("text_value").notNull(), }, (t) => ({ - byResponse: index("huddle_survey_answers_response_idx").on(t.responseId), - byQuestion: index("huddle_survey_answers_question_idx").on(t.questionId), + byResponse: index("survey_answers_response_idx").on(t.responseId), + byQuestion: index("survey_answers_question_idx").on(t.questionId), }), ); /** multiple_choice / checkboxes answers — one row per selected option. */ -export const huddleSurveyAnswerOptions = pgTable( - "huddle_survey_answer_options", +export const surveyAnswerOptions = pgTable( + "survey_answer_options", { id: uuid("id").defaultRandom().primaryKey(), responseId: uuid("response_id") .notNull() - .references(() => huddleSurveyResponses.id, { onDelete: "cascade" }), + .references(() => surveyResponses.id, { onDelete: "cascade" }), questionId: uuid("question_id") .notNull() - .references(() => huddleSurveyQuestions.id, { onDelete: "cascade" }), + .references(() => surveyQuestions.id, { onDelete: "cascade" }), optionId: uuid("option_id") .notNull() - .references(() => huddleSurveyOptions.id, { onDelete: "cascade" }), + .references(() => surveyOptions.id, { onDelete: "cascade" }), }, (t) => ({ - byQuestion: index("huddle_survey_answer_options_question_idx").on(t.questionId), - uniqResponseOption: uniqueIndex("huddle_survey_answer_options_response_option_uniq").on( + byQuestion: index("survey_answer_options_question_idx").on(t.questionId), + uniqResponseOption: uniqueIndex("survey_answer_options_response_option_uniq").on( t.responseId, t.optionId, ), }), ); -export type HuddleSurvey = typeof huddleSurveys.$inferSelect; -export type HuddleSurveyQuestion = typeof huddleSurveyQuestions.$inferSelect; -export type HuddleSurveyOption = typeof huddleSurveyOptions.$inferSelect; -export type HuddleSurveyResponse = typeof huddleSurveyResponses.$inferSelect; -export type HuddleSurveyAnswer = typeof huddleSurveyAnswers.$inferSelect; -export type HuddleSurveyAnswerOption = typeof huddleSurveyAnswerOptions.$inferSelect; +export type Survey = typeof surveys.$inferSelect; +export type SurveyQuestion = typeof surveyQuestions.$inferSelect; +export type SurveyOption = typeof surveyOptions.$inferSelect; +export type SurveyResponse = typeof surveyResponses.$inferSelect; +export type SurveyAnswer = typeof surveyAnswers.$inferSelect; +export type SurveyAnswerOption = typeof surveyAnswerOptions.$inferSelect; diff --git a/server/src/routes/huddleRoutes.ts b/server/src/routes/huddleRoutes.ts index e243d9a..9304f41 100644 --- a/server/src/routes/huddleRoutes.ts +++ b/server/src/routes/huddleRoutes.ts @@ -1,5 +1,5 @@ import { type Express, type Request, type Response } from "express"; -import { getAuth, createClerkClient } from "@clerk/express"; +import { getAuth } from "@clerk/express"; import { requireAuth } from "../middleware/requireAuth.js"; import { HuddlesServiceError, @@ -44,41 +44,16 @@ import { import { listPayouts, setPayouts } from "../services/payoutsService.js"; import { getActiveTrophies, setTrophyEnabled } from "../services/trophyControlService.js"; import { getCountdownConfig, setCountdownConfig } from "../services/countdownService.js"; +import { + displayHandle, + ensureUser, + getUserSummaries, +} from "../services/usersService.js"; -const clerkSecretKey = process.env["CLERK_SECRET_KEY"]; -if (!clerkSecretKey) { - throw new Error("Missing required environment variable: CLERK_SECRET_KEY"); -} -const clerkClient = createClerkClient({ secretKey: clerkSecretKey }); - -interface ClerkUserSummary { - id: string; - username: string | null; - email: string | null; -} - -async function fetchUserSummaries( - userIds: string[], -): Promise> { - const unique = [...new Set(userIds.filter(Boolean))]; - if (unique.length === 0) return new Map(); - const result = new Map(); - const list = await clerkClient.users.getUserList({ - userId: unique, - limit: unique.length, - }); - for (const u of list.data) { - result.set(u.id, { - id: u.id, - username: u.username, - email: - u.primaryEmailAddress?.emailAddress ?? - u.emailAddresses[0]?.emailAddress ?? - null, - }); - } - return result; -} +// Display names come from our own `users` table (one SQL round-trip), not from +// Clerk's API. These used to be `clerkClient.users.getUserList()` calls made +// inside the request handler — an HTTP hop to a third party on the critical +// path of rendering a huddle. See services/usersService.ts. function handleError(err: unknown, res: Response): void { if (err instanceof HuddlesServiceError) { @@ -145,17 +120,12 @@ function serializeClaim(c: { } export function initHuddleRoutes(app: Express) { - // POST /api/huddles — name is auto-generated from the user's Clerk display name + // POST /api/huddles — name is auto-generated from the user's display name app.post("/api/huddles", requireAuth, async (req: Request, res: Response) => { try { const { userId } = getAuth(req); - const clerkUser = await clerkClient.users.getUser(userId!); - const handle = - clerkUser.username ?? - clerkUser.firstName ?? - clerkUser.emailAddresses[0]?.emailAddress?.split("@")[0] ?? - "Your"; - const name = `${handle}'s Huddle`; + const user = await ensureUser(userId!); + const name = `${displayHandle(user)}'s Huddle`; const huddle = await createHuddle({ name, commissionerUserId: userId! }); res.status(201).json({ huddle: serializeHuddle(huddle, true) }); } catch (err) { @@ -273,7 +243,7 @@ export function initHuddleRoutes(app: Express) { ...commissioners.map((c) => c.userId), ...claims.map((c) => c.userId), ]; - const userMap = await fetchUserSummaries(allUserIds); + const userMap = await getUserSummaries(allUserIds); const myClaim = claims.find((c) => c.userId === userId) ?? null; res.json({ @@ -361,7 +331,7 @@ export function initHuddleRoutes(app: Express) { return; } const claims = await listClaimsForHuddle(huddleId); - const userMap = await fetchUserSummaries(claims.map((c) => c.userId)); + const userMap = await getUserSummaries(claims.map((c) => c.userId)); res.json({ claims: claims.map((c) => ({ ...serializeClaim(c), @@ -450,7 +420,7 @@ export function initHuddleRoutes(app: Express) { async (req: Request, res: Response) => { try { const commissioners = await listCommissioners(req.params.id!); - const userMap = await fetchUserSummaries( + const userMap = await getUserSummaries( commissioners.map((c) => c.userId), ); res.json({ @@ -487,7 +457,7 @@ export function initHuddleRoutes(app: Express) { newUserId, actingUserId: userId!, }); - const userMap = await fetchUserSummaries([row.userId]); + const userMap = await getUserSummaries([row.userId]); res.status(201).json({ commissioner: { userId: row.userId, diff --git a/server/src/routes/userRoutes.ts b/server/src/routes/userRoutes.ts index 925ac6b..474b5e1 100644 --- a/server/src/routes/userRoutes.ts +++ b/server/src/routes/userRoutes.ts @@ -1,16 +1,52 @@ +/** + * User routes — the current user's own profile. + * + * `sleeperUsername` / `sleeperUserId` / `syncedLeagueIds` used to live in + * Clerk's `unsafeMetadata`. They now live in our `users` table; Clerk is only + * consulted for identity (email, username), and only through + * usersService.ensureUser's TTL cache. Nothing here writes to Clerk anymore. + */ import { type Express, type Request, type Response } from "express"; -import { getAuth, createClerkClient } from "@clerk/express"; +import { getAuth } from "@clerk/express"; import { requireAuth } from "../middleware/requireAuth.js"; import { getProvider } from "../providers/registry.js"; +import { + ensureUser, + setSleeperLink, + setSyncedLeagues, +} from "../services/usersService.js"; +import type { User } from "../db/schema.js"; -const clerkSecretKey = process.env["CLERK_SECRET_KEY"]; -if (!clerkSecretKey) { - throw new Error("Missing required environment variable: CLERK_SECRET_KEY"); +function serializeUser(u: User) { + return { + id: u.id, + email: u.email, + username: u.username, + sleeperUsername: u.sleeperUsername, + sleeperUserId: u.sleeperUserId, + syncedLeagueIds: u.syncedLeagueIds, + }; } -const clerkClient = createClerkClient({ secretKey: clerkSecretKey }); - export function initUserRoutes(app: Express) { + /** + * GET /api/user/me — the client's source of truth for the signed-in user. + * + * Also the only place a `users` row gets created. AuthGuard calls this on + * every app load before any other request fires, which is what lets the + * foreign keys on every `user_id` column assume a row already exists. + */ + app.get("/api/user/me", requireAuth, async (req: Request, res: Response) => { + const { userId } = getAuth(req); + try { + const user = await ensureUser(userId!); + res.json({ user: serializeUser(user) }); + } catch (err) { + console.error("GET /api/user/me failed:", err); + res.status(503).json({ error: "Failed to load user" }); + } + }); + // PATCH /api/user/sleeper-username — link or clear Sleeper username app.patch( "/api/user/sleeper-username", @@ -57,26 +93,21 @@ export function initUserRoutes(app: Express) { } const { userId } = getAuth(req); - if (!userId) { - res.status(401).json({ error: "Unauthorized" }); - return; - } try { - await clerkClient.users.updateUserMetadata(userId, { - unsafeMetadata: { - sleeperUsername: username, - sleeperUserId, - // Clear synced leagues when unlinking so re-linking starts fresh - ...(isUnlink ? { syncedLeagueIds: [] } : {}), - }, + await ensureUser(userId!); + const updated = await setSleeperLink(userId!, { + sleeperUsername: username, + sleeperUserId, }); - } catch { - res.status(503).json({ error: "Failed to update user metadata" }); - return; + res.json({ + sleeperUsername: updated.sleeperUsername, + sleeperUserId: updated.sleeperUserId, + }); + } catch (err) { + console.error("PATCH /api/user/sleeper-username failed:", err); + res.status(503).json({ error: "Failed to update Sleeper link" }); } - - res.json({ sleeperUsername: username, sleeperUserId }); }, ); @@ -98,21 +129,15 @@ export function initUserRoutes(app: Express) { } const { userId } = getAuth(req); - if (!userId) { - res.status(401).json({ error: "Unauthorized" }); - return; - } try { - await clerkClient.users.updateUserMetadata(userId, { - unsafeMetadata: { syncedLeagueIds }, - }); - } catch { - res.status(503).json({ error: "Failed to update user metadata" }); - return; + await ensureUser(userId!); + const updated = await setSyncedLeagues(userId!, syncedLeagueIds); + res.json({ syncedLeagueIds: updated.syncedLeagueIds }); + } catch (err) { + console.error("PATCH /api/user/synced-leagues failed:", err); + res.status(503).json({ error: "Failed to update synced leagues" }); } - - res.json({ syncedLeagueIds }); }, ); } diff --git a/server/src/services/awardsService.ts b/server/src/services/awardsService.ts index e55f78d..68aba00 100644 --- a/server/src/services/awardsService.ts +++ b/server/src/services/awardsService.ts @@ -6,7 +6,7 @@ */ import { and, desc, eq } from "drizzle-orm"; import { db } from "../db/client.js"; -import { huddleAwards, type HuddleAward } from "../db/schema.js"; +import { awards, type Award } from "../db/schema.js"; import { HuddlesServiceError, isCommissioner } from "./huddlesService.js"; const fail = (status: number, message: string): never => { @@ -95,12 +95,12 @@ function validateAwardInput(input: { /** * All awards for a huddle, newest first. */ -export async function listAwards(huddleId: string): Promise { +export async function listAwards(huddleId: string): Promise { return db .select() - .from(huddleAwards) - .where(eq(huddleAwards.huddleId, huddleId)) - .orderBy(desc(huddleAwards.createdAt)); + .from(awards) + .where(eq(awards.huddleId, huddleId)) + .orderBy(desc(awards.createdAt)); } /** @@ -109,17 +109,17 @@ export async function listAwards(huddleId: string): Promise { export async function listAwardsForRoster( huddleId: string, rosterId: number, -): Promise { +): Promise { return db .select() - .from(huddleAwards) + .from(awards) .where( and( - eq(huddleAwards.huddleId, huddleId), - eq(huddleAwards.rosterId, rosterId), + eq(awards.huddleId, huddleId), + eq(awards.rosterId, rosterId), ), ) - .orderBy(desc(huddleAwards.createdAt)); + .orderBy(desc(awards.createdAt)); } // ── Mutations ───────────────────────────────────────────────────────────────── @@ -138,14 +138,14 @@ export async function createAward( description?: unknown; season?: unknown; }, -): Promise { +): Promise { if (!(await isCommissioner(huddleId, userId))) fail(403, "Only a commissioner can grant awards"); const validated = validateAwardInput(input); const [created] = await db - .insert(huddleAwards) + .insert(awards) .values({ huddleId, grantedBy: userId, @@ -172,14 +172,14 @@ export async function updateAward( description?: unknown; season?: unknown; }, -): Promise { +): Promise { if (!(await isCommissioner(huddleId, userId))) fail(403, "Only a commissioner can update awards"); const rows = await db .select() - .from(huddleAwards) - .where(and(eq(huddleAwards.id, awardId), eq(huddleAwards.huddleId, huddleId))) + .from(awards) + .where(and(eq(awards.id, awardId), eq(awards.huddleId, huddleId))) .limit(1); if (!rows[0]) fail(404, "Award not found"); @@ -187,9 +187,9 @@ export async function updateAward( const validated = validateAwardInput(input); const [updated] = await db - .update(huddleAwards) + .update(awards) .set({ ...validated, updatedAt: new Date() }) - .where(eq(huddleAwards.id, awardId)) + .where(eq(awards.id, awardId)) .returning(); if (!updated) fail(500, "Failed to update award"); @@ -209,11 +209,11 @@ export async function deleteAward( const rows = await db .select() - .from(huddleAwards) + .from(awards) .where( and( - eq(huddleAwards.id, awardId), - eq(huddleAwards.huddleId, huddleId), + eq(awards.id, awardId), + eq(awards.huddleId, huddleId), ), ) .limit(1); @@ -221,6 +221,6 @@ export async function deleteAward( if (!rows[0]) fail(404, "Award not found"); await db - .delete(huddleAwards) - .where(eq(huddleAwards.id, awardId)); + .delete(awards) + .where(eq(awards.id, awardId)); } diff --git a/server/src/services/countdownService.ts b/server/src/services/countdownService.ts index baf3140..9255f98 100644 --- a/server/src/services/countdownService.ts +++ b/server/src/services/countdownService.ts @@ -7,8 +7,8 @@ import { eq } from "drizzle-orm"; import { db } from "../db/client.js"; import { - huddleCountdownConfig, - type HuddleCountdownConfig, + countdownConfig, + type CountdownConfig, } from "../db/schema.js"; import { HuddlesServiceError, isCommissioner } from "./huddlesService.js"; @@ -24,11 +24,11 @@ const MAX_SUBTITLE_LEN = 120; /** Returns the current countdown config for a huddle, or null if none is set. */ export async function getCountdownConfig( huddleId: string, -): Promise { +): Promise { const rows = await db .select() - .from(huddleCountdownConfig) - .where(eq(huddleCountdownConfig.huddleId, huddleId)) + .from(countdownConfig) + .where(eq(countdownConfig.huddleId, huddleId)) .limit(1); return rows[0] ?? null; } @@ -48,7 +48,7 @@ export async function setCountdownConfig( targetAt: string; enabled: boolean; }, -): Promise { +): Promise { if (!(await isCommissioner(huddleId, userId))) fail(403, "Only a commissioner can set the countdown"); @@ -64,7 +64,7 @@ export async function setCountdownConfig( if (isNaN(targetAt.getTime())) fail(400, "targetAt must be a valid date"); const [row] = await db - .insert(huddleCountdownConfig) + .insert(countdownConfig) .values({ huddleId, title, @@ -74,7 +74,7 @@ export async function setCountdownConfig( updatedAt: new Date(), }) .onConflictDoUpdate({ - target: huddleCountdownConfig.huddleId, + target: countdownConfig.huddleId, set: { title, subtitle, diff --git a/server/src/services/duesService.ts b/server/src/services/duesService.ts index 677e5d2..9d2c7a8 100644 --- a/server/src/services/duesService.ts +++ b/server/src/services/duesService.ts @@ -9,10 +9,10 @@ import { and, eq } from "drizzle-orm"; import { db } from "../db/client.js"; import { - huddleDuesConfig, - huddleDuesPayments, - type HuddleDuesConfig, - type HuddleDuesPayment, + duesConfig, + duesPayments, + type DuesConfig, + type DuesPayment, } from "../db/schema.js"; import { HuddlesServiceError, isCommissioner } from "./huddlesService.js"; @@ -25,11 +25,11 @@ const fail = (status: number, message: string): never => { /** Returns the current dues config for a huddle, or null if none is set. */ export async function getDuesConfig( huddleId: string, -): Promise { +): Promise { const rows = await db .select() - .from(huddleDuesConfig) - .where(eq(huddleDuesConfig.huddleId, huddleId)) + .from(duesConfig) + .where(eq(duesConfig.huddleId, huddleId)) .limit(1); return rows[0] ?? null; } @@ -40,11 +40,11 @@ export async function getDuesConfig( */ export async function getDuesPayments( huddleId: string, -): Promise { +): Promise { return db .select() - .from(huddleDuesPayments) - .where(eq(huddleDuesPayments.huddleId, huddleId)); + .from(duesPayments) + .where(eq(duesPayments.huddleId, huddleId)); } // ---- Mutations ---- @@ -57,7 +57,7 @@ export async function setDuesConfig( huddleId: string, userId: string, opts: { amount: number; season?: string | null; note?: string | null }, -): Promise { +): Promise { if (!(await isCommissioner(huddleId, userId))) fail(403, "Only a commissioner can set dues"); @@ -65,7 +65,7 @@ export async function setDuesConfig( fail(400, "amount must be a non-negative integer (cents)"); const [row] = await db - .insert(huddleDuesConfig) + .insert(duesConfig) .values({ huddleId, amount: opts.amount, @@ -74,7 +74,7 @@ export async function setDuesConfig( updatedAt: new Date(), }) .onConflictDoUpdate({ - target: huddleDuesConfig.huddleId, + target: duesConfig.huddleId, set: { amount: opts.amount, season: opts.season ?? null, @@ -98,7 +98,7 @@ export async function setDuesPaid( rosterId: number, paid: boolean, note?: string | null, -): Promise { +): Promise { if (!(await isCommissioner(huddleId, userId))) fail(403, "Only a commissioner can mark dues as paid"); @@ -108,7 +108,7 @@ export async function setDuesPaid( const paidAt = paid ? new Date() : null; const [row] = await db - .insert(huddleDuesPayments) + .insert(duesPayments) .values({ huddleId, rosterId, @@ -118,7 +118,7 @@ export async function setDuesPaid( updatedAt: new Date(), }) .onConflictDoUpdate({ - target: [huddleDuesPayments.huddleId, huddleDuesPayments.rosterId], + target: [duesPayments.huddleId, duesPayments.rosterId], set: { paidAt, markedBy: userId, diff --git a/server/src/services/forumService.ts b/server/src/services/forumService.ts index fc2e110..513cbe6 100644 --- a/server/src/services/forumService.ts +++ b/server/src/services/forumService.ts @@ -10,10 +10,10 @@ import { and, asc, desc, eq, gt, isNull, lt } from "drizzle-orm"; import { db } from "../db/client.js"; import { - huddleForumTopics, - huddleForumReplies, - type HuddleForumTopic, - type HuddleForumReply, + forumTopics, + forumReplies, + type ForumTopic, + type ForumReply, } from "../db/schema.js"; import { HuddlesServiceError, isCommissioner, hasApprovedClaim } from "./huddlesService.js"; import { createPoll, type NewPollInput } from "./pollService.js"; @@ -33,16 +33,16 @@ const MAX_PAGE_SIZE = 50; export async function listTopics( huddleId: string, opts: { limit?: number; before?: string } = {}, -): Promise { +): Promise { const limit = Math.min(opts.limit ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE); - const conditions = [eq(huddleForumTopics.huddleId, huddleId), isNull(huddleForumTopics.deletedAt)]; - if (opts.before) conditions.push(lt(huddleForumTopics.updatedAt, new Date(opts.before))); + const conditions = [eq(forumTopics.huddleId, huddleId), isNull(forumTopics.deletedAt)]; + if (opts.before) conditions.push(lt(forumTopics.updatedAt, new Date(opts.before))); return db .select() - .from(huddleForumTopics) + .from(forumTopics) .where(and(...conditions)) - .orderBy(desc(huddleForumTopics.updatedAt)) + .orderBy(desc(forumTopics.updatedAt)) .limit(limit); } @@ -50,15 +50,15 @@ export async function listTopics( export async function getTopic( huddleId: string, topicId: string, -): Promise { +): Promise { const rows = await db .select() - .from(huddleForumTopics) + .from(forumTopics) .where( and( - eq(huddleForumTopics.id, topicId), - eq(huddleForumTopics.huddleId, huddleId), - isNull(huddleForumTopics.deletedAt), + eq(forumTopics.id, topicId), + eq(forumTopics.huddleId, huddleId), + isNull(forumTopics.deletedAt), ), ) .limit(1); @@ -70,20 +70,20 @@ export async function listReplies( huddleId: string, topicId: string, opts: { limit?: number; after?: string } = {}, -): Promise { +): Promise { const limit = Math.min(opts.limit ?? MAX_PAGE_SIZE, MAX_PAGE_SIZE); const conditions = [ - eq(huddleForumReplies.topicId, topicId), - eq(huddleForumReplies.huddleId, huddleId), - isNull(huddleForumReplies.deletedAt), + eq(forumReplies.topicId, topicId), + eq(forumReplies.huddleId, huddleId), + isNull(forumReplies.deletedAt), ]; - if (opts.after) conditions.push(gt(huddleForumReplies.createdAt, new Date(opts.after))); + if (opts.after) conditions.push(gt(forumReplies.createdAt, new Date(opts.after))); return db .select() - .from(huddleForumReplies) + .from(forumReplies) .where(and(...conditions)) - .orderBy(asc(huddleForumReplies.createdAt)) + .orderBy(asc(forumReplies.createdAt)) .limit(limit); } @@ -95,7 +95,7 @@ export async function createTopic(opts: { title: string; body: string; poll?: NewPollInput; -}): Promise { +}): Promise { if (!(await hasApprovedClaim(opts.huddleId, opts.userId))) fail(403, "Only approved members can post to the forum"); @@ -108,7 +108,7 @@ export async function createTopic(opts: { fail(400, `body is required (max ${MAX_BODY_LEN} chars)`); const [row] = await db - .insert(huddleForumTopics) + .insert(forumTopics) .values({ huddleId: opts.huddleId, authorId: opts.userId, @@ -137,7 +137,7 @@ export async function createReply(opts: { topicId: string; userId: string; body: string; -}): Promise { +}): Promise { if (!(await hasApprovedClaim(opts.huddleId, opts.userId))) fail(403, "Only approved members can post to the forum"); @@ -149,7 +149,7 @@ export async function createReply(opts: { fail(400, `body is required (max ${MAX_BODY_LEN} chars)`); const [row] = await db - .insert(huddleForumReplies) + .insert(forumReplies) .values({ topicId: opts.topicId, huddleId: opts.huddleId, @@ -161,9 +161,9 @@ export async function createReply(opts: { if (!row) fail(500, "Failed to post reply"); await db - .update(huddleForumTopics) + .update(forumTopics) .set({ updatedAt: new Date(), replyCount: topic.replyCount + 1 }) - .where(eq(huddleForumTopics.id, opts.topicId)); + .where(eq(forumTopics.id, opts.topicId)); return row!; } @@ -186,9 +186,9 @@ export async function deleteTopic(opts: { ); await db - .update(huddleForumTopics) + .update(forumTopics) .set({ deletedAt: new Date(), deletedBy: opts.userId }) - .where(eq(huddleForumTopics.id, opts.topicId)); + .where(eq(forumTopics.id, opts.topicId)); } export async function deleteReply(opts: { @@ -199,13 +199,13 @@ export async function deleteReply(opts: { }): Promise { const rows = await db .select() - .from(huddleForumReplies) + .from(forumReplies) .where( and( - eq(huddleForumReplies.id, opts.replyId), - eq(huddleForumReplies.topicId, opts.topicId), - eq(huddleForumReplies.huddleId, opts.huddleId), - isNull(huddleForumReplies.deletedAt), + eq(forumReplies.id, opts.replyId), + eq(forumReplies.topicId, opts.topicId), + eq(forumReplies.huddleId, opts.huddleId), + isNull(forumReplies.deletedAt), ), ) .limit(1); @@ -217,19 +217,19 @@ export async function deleteReply(opts: { fail(403, "Only the author or a commissioner can remove this reply"); await db - .update(huddleForumReplies) + .update(forumReplies) .set({ deletedAt: new Date(), deletedBy: opts.userId }) - .where(eq(huddleForumReplies.id, opts.replyId)); + .where(eq(forumReplies.id, opts.replyId)); const [topic] = await db .select() - .from(huddleForumTopics) - .where(eq(huddleForumTopics.id, opts.topicId)) + .from(forumTopics) + .where(eq(forumTopics.id, opts.topicId)) .limit(1); if (topic) { await db - .update(huddleForumTopics) + .update(forumTopics) .set({ replyCount: Math.max(0, topic.replyCount - 1) }) - .where(eq(huddleForumTopics.id, opts.topicId)); + .where(eq(forumTopics.id, opts.topicId)); } } diff --git a/server/src/services/huddlesService.ts b/server/src/services/huddlesService.ts index ddec00a..5b594de 100644 --- a/server/src/services/huddlesService.ts +++ b/server/src/services/huddlesService.ts @@ -3,12 +3,12 @@ import { and, eq, count, inArray, desc } from "drizzle-orm"; import { db } from "../db/client.js"; import { huddles, - huddleCommissioners, - huddleAnnouncements, + commissioners, + announcements, teamClaims, type Huddle, - type HuddleCommissioner, - type HuddleAnnouncement, + type Commissioner, + type Announcement, type TeamClaim, } from "../db/schema.js"; @@ -82,11 +82,11 @@ export async function isCommissioner( ): Promise { const rows = await db .select() - .from(huddleCommissioners) + .from(commissioners) .where( and( - eq(huddleCommissioners.huddleId, huddleId), - eq(huddleCommissioners.userId, userId), + eq(commissioners.huddleId, huddleId), + eq(commissioners.userId, userId), ), ) .limit(1); @@ -113,18 +113,18 @@ export async function hasApprovedClaim(huddleId: string, userId: string): Promis async function commissionerCount(huddleId: string): Promise { const rows = await db .select({ n: count() }) - .from(huddleCommissioners) - .where(eq(huddleCommissioners.huddleId, huddleId)); + .from(commissioners) + .where(eq(commissioners.huddleId, huddleId)); return Number(rows[0]?.n ?? 0); } export async function listCommissioners( huddleId: string, -): Promise { +): Promise { return db .select() - .from(huddleCommissioners) - .where(eq(huddleCommissioners.huddleId, huddleId)); + .from(commissioners) + .where(eq(commissioners.huddleId, huddleId)); } // ---- Huddle CRUD ---- @@ -144,7 +144,7 @@ export async function createHuddle(opts: { if (!created) fail(500, "Failed to create huddle"); // Seed the commissioner row - await db.insert(huddleCommissioners).values({ + await db.insert(commissioners).values({ huddleId: created!.id, userId: opts.commissionerUserId, addedBy: opts.commissionerUserId, @@ -161,9 +161,9 @@ export async function listHuddlesForUser( // Include pending claims so the user can see huddles they've requested to join const [commishRows, claimRows] = await Promise.all([ db - .select({ huddleId: huddleCommissioners.huddleId }) - .from(huddleCommissioners) - .where(eq(huddleCommissioners.userId, userId)), + .select({ huddleId: commissioners.huddleId }) + .from(commissioners) + .where(eq(commissioners.userId, userId)), db .select({ huddleId: teamClaims.huddleId, status: teamClaims.status }) .from(teamClaims) @@ -398,7 +398,7 @@ export async function addCommissioner(opts: { huddleId: string; newUserId: string; actingUserId: string; -}): Promise { +}): Promise { if (!(await isCommissioner(opts.huddleId, opts.actingUserId))) fail(403, "Only a commissioner can add co-commissioners"); @@ -418,7 +418,7 @@ export async function addCommissioner(opts: { fail(400, "User must have an approved team claim to become a commissioner"); const [row] = await db - .insert(huddleCommissioners) + .insert(commissioners) .values({ huddleId: opts.huddleId, userId: opts.newUserId, @@ -431,11 +431,11 @@ export async function addCommissioner(opts: { if (!row) { const existing = await db .select() - .from(huddleCommissioners) + .from(commissioners) .where( and( - eq(huddleCommissioners.huddleId, opts.huddleId), - eq(huddleCommissioners.userId, opts.newUserId), + eq(commissioners.huddleId, opts.huddleId), + eq(commissioners.userId, opts.newUserId), ), ) .limit(1); @@ -458,11 +458,11 @@ export async function removeCommissioner(opts: { fail(409, "Cannot remove the last commissioner — assign another first"); await db - .delete(huddleCommissioners) + .delete(commissioners) .where( and( - eq(huddleCommissioners.huddleId, opts.huddleId), - eq(huddleCommissioners.userId, opts.targetUserId), + eq(commissioners.huddleId, opts.huddleId), + eq(commissioners.userId, opts.targetUserId), ), ); } @@ -581,7 +581,7 @@ export async function createAnnouncement(opts: { userId: string; title: unknown; body: unknown; -}): Promise { +}): Promise { if (!(await isCommissioner(opts.huddleId, opts.userId))) fail(403, "Only a commissioner can post announcements"); @@ -600,7 +600,7 @@ export async function createAnnouncement(opts: { fail(400, `body is required (max ${MAX_ANNOUNCEMENT_BODY_LEN} chars)`); const [created] = await db - .insert(huddleAnnouncements) + .insert(announcements) .values({ huddleId: opts.huddleId, authorId: opts.userId, @@ -615,12 +615,12 @@ export async function createAnnouncement(opts: { export async function listAnnouncements( huddleId: string, limit = 10, -): Promise { +): Promise { return db .select() - .from(huddleAnnouncements) - .where(eq(huddleAnnouncements.huddleId, huddleId)) - .orderBy(desc(huddleAnnouncements.createdAt)) + .from(announcements) + .where(eq(announcements.huddleId, huddleId)) + .orderBy(desc(announcements.createdAt)) .limit(limit); } @@ -634,19 +634,19 @@ export async function deleteAnnouncement(opts: { const rows = await db .select() - .from(huddleAnnouncements) + .from(announcements) .where( and( - eq(huddleAnnouncements.id, opts.announcementId), - eq(huddleAnnouncements.huddleId, opts.huddleId), + eq(announcements.id, opts.announcementId), + eq(announcements.huddleId, opts.huddleId), ), ) .limit(1); if (!rows[0]) fail(404, "Announcement not found"); await db - .delete(huddleAnnouncements) - .where(eq(huddleAnnouncements.id, opts.announcementId)); + .delete(announcements) + .where(eq(announcements.id, opts.announcementId)); } // Self-unclaim: user removes their own pending or approved claim diff --git a/server/src/services/payoutsService.ts b/server/src/services/payoutsService.ts index ecb433b..a1a6eb0 100644 --- a/server/src/services/payoutsService.ts +++ b/server/src/services/payoutsService.ts @@ -7,7 +7,7 @@ */ import { eq, asc } from "drizzle-orm"; import { db } from "../db/client.js"; -import { huddlePayoutEntries } from "../db/schema.js"; +import { payoutEntries } from "../db/schema.js"; import { isCommissioner, HuddlesServiceError } from "./huddlesService.js"; const fail = (status: number, msg: string): never => { @@ -23,9 +23,9 @@ export interface PayoutEntryInput { export async function listPayouts(huddleId: string) { return db .select() - .from(huddlePayoutEntries) - .where(eq(huddlePayoutEntries.huddleId, huddleId)) - .orderBy(asc(huddlePayoutEntries.sortOrder), asc(huddlePayoutEntries.createdAt)); + .from(payoutEntries) + .where(eq(payoutEntries.huddleId, huddleId)) + .orderBy(asc(payoutEntries.sortOrder), asc(payoutEntries.createdAt)); } /** @@ -56,11 +56,11 @@ export async function setPayouts( // Full replace: delete existing then insert new set. await db - .delete(huddlePayoutEntries) - .where(eq(huddlePayoutEntries.huddleId, huddleId)); + .delete(payoutEntries) + .where(eq(payoutEntries.huddleId, huddleId)); if (entries.length > 0) { - await db.insert(huddlePayoutEntries).values( + await db.insert(payoutEntries).values( entries.map((e, i) => ({ huddleId, label: e.label.trim(), diff --git a/server/src/services/pollService.ts b/server/src/services/pollService.ts index f75e288..4757604 100644 --- a/server/src/services/pollService.ts +++ b/server/src/services/pollService.ts @@ -10,10 +10,10 @@ import { and, asc, count, countDistinct, eq, inArray } from "drizzle-orm"; import { db } from "../db/client.js"; import { - huddlePolls, - huddlePollOptions, - huddlePollVotes, - type HuddlePoll, + polls, + pollOptions, + pollVotes, + type Poll, } from "../db/schema.js"; import { HuddlesServiceError, isCommissioner, hasApprovedClaim } from "./huddlesService.js"; @@ -66,19 +66,19 @@ export async function getPollWithResults( pollId: string, userId: string, ): Promise { - const [poll] = await db.select().from(huddlePolls).where(eq(huddlePolls.id, pollId)).limit(1); + const [poll] = await db.select().from(polls).where(eq(polls.id, pollId)).limit(1); if (!poll) return null; const optionRows = await db .select() - .from(huddlePollOptions) - .where(eq(huddlePollOptions.pollId, pollId)) - .orderBy(asc(huddlePollOptions.sortOrder)); + .from(pollOptions) + .where(eq(pollOptions.pollId, pollId)) + .orderBy(asc(pollOptions.sortOrder)); const myVoteRows = await db .select() - .from(huddlePollVotes) - .where(and(eq(huddlePollVotes.pollId, pollId), eq(huddlePollVotes.userId, userId))); + .from(pollVotes) + .where(and(eq(pollVotes.pollId, pollId), eq(pollVotes.userId, userId))); const myOptionIds = myVoteRows.map((v) => v.optionId); const hasVoted = myOptionIds.length > 0; @@ -93,16 +93,16 @@ export async function getPollWithResults( let totalVoters = 0; if (resultsVisible) { const countRows = await db - .select({ optionId: huddlePollVotes.optionId, n: count() }) - .from(huddlePollVotes) - .where(eq(huddlePollVotes.pollId, pollId)) - .groupBy(huddlePollVotes.optionId); + .select({ optionId: pollVotes.optionId, n: count() }) + .from(pollVotes) + .where(eq(pollVotes.pollId, pollId)) + .groupBy(pollVotes.optionId); voteCounts = new Map(countRows.map((r) => [r.optionId, Number(r.n)])); const [totalRow] = await db - .select({ n: countDistinct(huddlePollVotes.userId) }) - .from(huddlePollVotes) - .where(eq(huddlePollVotes.pollId, pollId)); + .select({ n: countDistinct(pollVotes.userId) }) + .from(pollVotes) + .where(eq(pollVotes.pollId, pollId)); totalVoters = Number(totalRow?.n ?? 0); } @@ -138,8 +138,8 @@ export async function getPollForTopic( ): Promise { const [poll] = await db .select() - .from(huddlePolls) - .where(and(eq(huddlePolls.huddleId, huddleId), eq(huddlePolls.topicId, topicId))) + .from(polls) + .where(and(eq(polls.huddleId, huddleId), eq(polls.topicId, topicId))) .limit(1); if (!poll) return null; return getPollWithResults(poll.id, userId); @@ -148,9 +148,9 @@ export async function getPollForTopic( /** Lightweight lookup for the poll ID attached to a topic, without loading results. */ export async function getPollIdForTopic(huddleId: string, topicId: string): Promise { const [poll] = await db - .select({ id: huddlePolls.id }) - .from(huddlePolls) - .where(and(eq(huddlePolls.huddleId, huddleId), eq(huddlePolls.topicId, topicId))) + .select({ id: polls.id }) + .from(polls) + .where(and(eq(polls.huddleId, huddleId), eq(polls.topicId, topicId))) .limit(1); return poll?.id ?? null; } @@ -162,8 +162,8 @@ export async function getDashboardPoll( ): Promise { const [poll] = await db .select() - .from(huddlePolls) - .where(and(eq(huddlePolls.huddleId, huddleId), eq(huddlePolls.isDashboardPoll, true))) + .from(polls) + .where(and(eq(polls.huddleId, huddleId), eq(polls.isDashboardPoll, true))) .limit(1); if (!poll) return null; return getPollWithResults(poll.id, userId); @@ -213,7 +213,7 @@ export async function createPoll(opts: { const { question, options, closesAt } = validateNewPoll(opts); const [pollRow] = await db - .insert(huddlePolls) + .insert(polls) .values({ huddleId: opts.huddleId, authorId: opts.authorId, @@ -229,7 +229,7 @@ export async function createPoll(opts: { if (!pollRow) fail(500, "Failed to create poll"); await db - .insert(huddlePollOptions) + .insert(pollOptions) .values(options.map((label, i) => ({ pollId: pollRow!.id, label, sortOrder: i }))); const result = await getPollWithResults(pollRow!.id, opts.authorId); @@ -248,9 +248,9 @@ export async function setDashboardPoll( fail(403, "Only a commissioner can set the dashboard poll"); await db - .update(huddlePolls) + .update(polls) .set({ isDashboardPoll: false }) - .where(and(eq(huddlePolls.huddleId, huddleId), eq(huddlePolls.isDashboardPoll, true))); + .where(and(eq(polls.huddleId, huddleId), eq(polls.isDashboardPoll, true))); return createPoll({ huddleId, @@ -272,8 +272,8 @@ export async function castVote(opts: { const [poll] = await db .select() - .from(huddlePolls) - .where(and(eq(huddlePolls.id, opts.pollId), eq(huddlePolls.huddleId, opts.huddleId))) + .from(polls) + .where(and(eq(polls.id, opts.pollId), eq(polls.huddleId, opts.huddleId))) .limit(1); if (!poll) return fail(404, "Poll not found"); if (poll.closesAt && poll.closesAt.getTime() <= Date.now()) @@ -286,25 +286,25 @@ export async function castVote(opts: { const validOptions = await db .select() - .from(huddlePollOptions) - .where(and(eq(huddlePollOptions.pollId, opts.pollId), inArray(huddlePollOptions.id, optionIds))); + .from(pollOptions) + .where(and(eq(pollOptions.pollId, opts.pollId), inArray(pollOptions.id, optionIds))); if (validOptions.length !== optionIds.length) fail(400, "One or more options are invalid"); const existingVotes = await db .select() - .from(huddlePollVotes) - .where(and(eq(huddlePollVotes.pollId, opts.pollId), eq(huddlePollVotes.userId, opts.userId))); + .from(pollVotes) + .where(and(eq(pollVotes.pollId, opts.pollId), eq(pollVotes.userId, opts.userId))); if (existingVotes.length > 0 && !poll.allowVoteChanges) fail(409, "You've already voted on this poll"); if (existingVotes.length > 0) { await db - .delete(huddlePollVotes) - .where(and(eq(huddlePollVotes.pollId, opts.pollId), eq(huddlePollVotes.userId, opts.userId))); + .delete(pollVotes) + .where(and(eq(pollVotes.pollId, opts.pollId), eq(pollVotes.userId, opts.userId))); } await db - .insert(huddlePollVotes) + .insert(pollVotes) .values(optionIds.map((optionId) => ({ pollId: opts.pollId, optionId, userId: opts.userId }))); const result = await getPollWithResults(opts.pollId, opts.userId); @@ -312,4 +312,4 @@ export async function castVote(opts: { return result; } -export type { HuddlePoll }; +export type { Poll }; diff --git a/server/src/services/surveyService.ts b/server/src/services/surveyService.ts index 1aa773b..5deed3f 100644 --- a/server/src/services/surveyService.ts +++ b/server/src/services/surveyService.ts @@ -1,7 +1,7 @@ /** * Survey service — commissioner-authored, multi-question forms scoped to a * huddle. Modeled as "a poll with several questions": see the schema comment - * on `huddleSurveys` in `db/schema.ts` for how the tables map onto the poll + * on `surveys` in `db/schema.ts` for how the tables map onto the poll * tables this mirrors. * * Only commissioners can create/publish-results/delete a survey. Any approved @@ -12,13 +12,13 @@ import { and, asc, desc, eq, inArray, count } from "drizzle-orm"; import { db } from "../db/client.js"; import { - huddleSurveys, - huddleSurveyQuestions, - huddleSurveyOptions, - huddleSurveyResponses, - huddleSurveyAnswers, - huddleSurveyAnswerOptions, - type HuddleSurvey, + surveys, + surveyQuestions, + surveyOptions, + surveyResponses, + surveyAnswers, + surveyAnswerOptions, + type Survey, } from "../db/schema.js"; import { HuddlesServiceError, isCommissioner, hasApprovedClaim } from "./huddlesService.js"; @@ -143,7 +143,7 @@ export interface SurveyResults { questions: SurveyResultsQuestion[]; } -function isClosed(survey: Pick): boolean { +function isClosed(survey: Pick): boolean { return survey.closesAt.getTime() <= Date.now(); } @@ -153,18 +153,18 @@ function isClosed(survey: Pick): boolean { * background job in this app (serverless, no cron) — instead every read * path self-heals the flag the first time it notices the survey has closed. */ -async function applyAutoPublish(surveys: HuddleSurvey[]): Promise { +async function applyAutoPublish(rows: Survey[]): Promise { const now = Date.now(); - const toPublish = surveys.filter( + const toPublish = rows.filter( (s) => !s.resultsPublished && s.autoPublishOnClose && s.closesAt.getTime() <= now, ); - if (toPublish.length === 0) return surveys; + if (toPublish.length === 0) return rows; const ids = toPublish.map((s) => s.id); - await db.update(huddleSurveys).set({ resultsPublished: true }).where(inArray(huddleSurveys.id, ids)); + await db.update(surveys).set({ resultsPublished: true }).where(inArray(surveys.id, ids)); const publishedIds = new Set(ids); - return surveys.map((s) => (publishedIds.has(s.id) ? { ...s, resultsPublished: true } : s)); + return rows.map((s) => (publishedIds.has(s.id) ? { ...s, resultsPublished: true } : s)); } // ── Queries ──────────────────────────────────────────────────────────────────── @@ -173,28 +173,28 @@ async function applyAutoPublish(surveys: HuddleSurvey[]): Promise { const rawSurveys = await db .select() - .from(huddleSurveys) - .where(eq(huddleSurveys.huddleId, huddleId)) - .orderBy(desc(huddleSurveys.createdAt)); + .from(surveys) + .where(eq(surveys.huddleId, huddleId)) + .orderBy(desc(surveys.createdAt)); if (rawSurveys.length === 0) return []; - const surveys = await applyAutoPublish(rawSurveys); - const ids = surveys.map((s) => s.id); + const published = await applyAutoPublish(rawSurveys); + const ids = published.map((s) => s.id); const countRows = await db - .select({ surveyId: huddleSurveyResponses.surveyId, n: count() }) - .from(huddleSurveyResponses) - .where(inArray(huddleSurveyResponses.surveyId, ids)) - .groupBy(huddleSurveyResponses.surveyId); + .select({ surveyId: surveyResponses.surveyId, n: count() }) + .from(surveyResponses) + .where(inArray(surveyResponses.surveyId, ids)) + .groupBy(surveyResponses.surveyId); const countBySurvey = new Map(countRows.map((r) => [r.surveyId, Number(r.n)])); const myResponseRows = await db - .select({ surveyId: huddleSurveyResponses.surveyId }) - .from(huddleSurveyResponses) - .where(and(inArray(huddleSurveyResponses.surveyId, ids), eq(huddleSurveyResponses.userId, userId))); + .select({ surveyId: surveyResponses.surveyId }) + .from(surveyResponses) + .where(and(inArray(surveyResponses.surveyId, ids), eq(surveyResponses.userId, userId))); const respondedSurveyIds = new Set(myResponseRows.map((r) => r.surveyId)); - return surveys.map((s) => ({ + return published.map((s) => ({ id: s.id, huddleId: s.huddleId, authorId: s.authorId, @@ -211,11 +211,11 @@ export async function listSurveys(huddleId: string, userId: string): Promise { +async function getSurveyRow(huddleId: string, surveyId: string): Promise { const rows = await db .select() - .from(huddleSurveys) - .where(and(eq(huddleSurveys.id, surveyId), eq(huddleSurveys.huddleId, huddleId))) + .from(surveys) + .where(and(eq(surveys.id, surveyId), eq(surveys.huddleId, huddleId))) .limit(1); const survey = rows[0] ?? null; if (!survey) return null; @@ -226,17 +226,17 @@ async function getSurveyRow(huddleId: string, surveyId: string): Promise { const questionRows = await db .select() - .from(huddleSurveyQuestions) - .where(eq(huddleSurveyQuestions.surveyId, surveyId)) - .orderBy(asc(huddleSurveyQuestions.sortOrder)); + .from(surveyQuestions) + .where(eq(surveyQuestions.surveyId, surveyId)) + .orderBy(asc(surveyQuestions.sortOrder)); if (questionRows.length === 0) return []; const questionIds = questionRows.map((q) => q.id); const optionRows = await db .select() - .from(huddleSurveyOptions) - .where(inArray(huddleSurveyOptions.questionId, questionIds)) - .orderBy(asc(huddleSurveyOptions.sortOrder)); + .from(surveyOptions) + .where(inArray(surveyOptions.questionId, questionIds)) + .orderBy(asc(surveyOptions.sortOrder)); const optionsByQuestion = new Map(); for (const o of optionRows) { @@ -257,19 +257,19 @@ async function getQuestionsWithOptions(surveyId: string): Promise { const [response] = await db .select() - .from(huddleSurveyResponses) - .where(and(eq(huddleSurveyResponses.surveyId, surveyId), eq(huddleSurveyResponses.userId, userId))) + .from(surveyResponses) + .where(and(eq(surveyResponses.surveyId, surveyId), eq(surveyResponses.userId, userId))) .limit(1); if (!response) return null; const textRows = await db .select() - .from(huddleSurveyAnswers) - .where(eq(huddleSurveyAnswers.responseId, response.id)); + .from(surveyAnswers) + .where(eq(surveyAnswers.responseId, response.id)); const optionRows = await db .select() - .from(huddleSurveyAnswerOptions) - .where(eq(huddleSurveyAnswerOptions.responseId, response.id)); + .from(surveyAnswerOptions) + .where(eq(surveyAnswerOptions.responseId, response.id)); const optionIdsByQuestion = new Map(); for (const o of optionRows) { @@ -342,18 +342,18 @@ export async function getResults( const [totalRow] = await db .select({ n: count() }) - .from(huddleSurveyResponses) - .where(eq(huddleSurveyResponses.surveyId, survey.id)); + .from(surveyResponses) + .where(eq(surveyResponses.surveyId, survey.id)); const totalResponses = Number(totalRow?.n ?? 0); const results: SurveyResultsQuestion[] = []; for (const q of questions) { if (CHOICE_TYPES.has(q.type)) { const countRows = await db - .select({ optionId: huddleSurveyAnswerOptions.optionId, n: count() }) - .from(huddleSurveyAnswerOptions) - .where(eq(huddleSurveyAnswerOptions.questionId, q.id)) - .groupBy(huddleSurveyAnswerOptions.optionId); + .select({ optionId: surveyAnswerOptions.optionId, n: count() }) + .from(surveyAnswerOptions) + .where(eq(surveyAnswerOptions.questionId, q.id)) + .groupBy(surveyAnswerOptions.optionId); const countByOption = new Map(countRows.map((r) => [r.optionId, Number(r.n)])); results.push({ questionId: q.id, @@ -368,10 +368,10 @@ export async function getResults( }); } else { const rows = await db - .select({ userId: huddleSurveyResponses.userId, textValue: huddleSurveyAnswers.textValue }) - .from(huddleSurveyAnswers) - .innerJoin(huddleSurveyResponses, eq(huddleSurveyAnswers.responseId, huddleSurveyResponses.id)) - .where(eq(huddleSurveyAnswers.questionId, q.id)); + .select({ userId: surveyResponses.userId, textValue: surveyAnswers.textValue }) + .from(surveyAnswers) + .innerJoin(surveyResponses, eq(surveyAnswers.responseId, surveyResponses.id)) + .where(eq(surveyAnswers.questionId, q.id)); results.push({ questionId: q.id, prompt: q.prompt, @@ -463,14 +463,14 @@ async function insertQuestions(surveyId: string, questions: ValidatedQuestion[]) for (let i = 0; i < questions.length; i++) { const q = questions[i]!; const [questionRow] = await db - .insert(huddleSurveyQuestions) + .insert(surveyQuestions) .values({ surveyId, type: q.type, prompt: q.prompt, required: q.required, sortOrder: i }) .returning(); if (!questionRow) fail(500, "Failed to save question"); if (q.options.length > 0) { await db - .insert(huddleSurveyOptions) + .insert(surveyOptions) .values(q.options.map((label, j) => ({ questionId: questionRow!.id, label, sortOrder: j }))); } } @@ -480,7 +480,7 @@ async function insertQuestions(surveyId: string, questions: ValidatedQuestion[]) * Reconciles a choice question's options by label: a label that already * exists keeps its row (and thus its collected votes), a new label gets a * fresh row, and an existing label no longer present is deleted (dropping - * only the votes for that specific option, via FK cascade on huddleSurveyAnswerOptions). + * only the votes for that specific option, via FK cascade on surveyAnswerOptions). */ async function syncQuestionOptions( questionId: string, @@ -492,16 +492,16 @@ async function syncQuestionOptions( const removeIds = existingOptions.filter((o) => !keepLabels.has(o.label)).map((o) => o.id); if (removeIds.length > 0) { - await db.delete(huddleSurveyOptions).where(inArray(huddleSurveyOptions.id, removeIds)); + await db.delete(surveyOptions).where(inArray(surveyOptions.id, removeIds)); } for (let j = 0; j < incomingLabels.length; j++) { const label = incomingLabels[j]!; const existingId = existingByLabel.get(label); if (existingId) { - await db.update(huddleSurveyOptions).set({ sortOrder: j }).where(eq(huddleSurveyOptions.id, existingId)); + await db.update(surveyOptions).set({ sortOrder: j }).where(eq(surveyOptions.id, existingId)); } else { - await db.insert(huddleSurveyOptions).values({ questionId, label, sortOrder: j }); + await db.insert(surveyOptions).values({ questionId, label, sortOrder: j }); } } } @@ -520,7 +520,7 @@ export async function createSurvey(opts: { ); const [surveyRow] = await db - .insert(huddleSurveys) + .insert(surveys) .values({ huddleId: opts.huddleId, authorId: opts.userId, @@ -568,9 +568,9 @@ export async function updateSurvey(opts: { ); await db - .update(huddleSurveys) + .update(surveys) .set({ title, description, closesAt, anonymity, autoPublishOnClose }) - .where(eq(huddleSurveys.id, opts.surveyId)); + .where(eq(surveys.id, opts.surveyId)); const existingQuestions = await getQuestionsWithOptions(opts.surveyId); const existingById = new Map(existingQuestions.map((q) => [q.id, q])); @@ -578,7 +578,7 @@ export async function updateSurvey(opts: { const removedIds = existingQuestions.map((q) => q.id).filter((id) => !incomingIds.has(id)); if (removedIds.length > 0) { - await db.delete(huddleSurveyQuestions).where(inArray(huddleSurveyQuestions.id, removedIds)); + await db.delete(surveyQuestions).where(inArray(surveyQuestions.id, removedIds)); } for (let i = 0; i < questions.length; i++) { @@ -587,31 +587,31 @@ export async function updateSurvey(opts: { if (!match) { const [questionRow] = await db - .insert(huddleSurveyQuestions) + .insert(surveyQuestions) .values({ surveyId: opts.surveyId, type: q.type, prompt: q.prompt, required: q.required, sortOrder: i }) .returning(); if (!questionRow) fail(500, "Failed to save question"); if (q.options.length > 0) { await db - .insert(huddleSurveyOptions) + .insert(surveyOptions) .values(q.options.map((label, j) => ({ questionId: questionRow!.id, label, sortOrder: j }))); } continue; } await db - .update(huddleSurveyQuestions) + .update(surveyQuestions) .set({ type: q.type, prompt: q.prompt, required: q.required, sortOrder: i }) - .where(eq(huddleSurveyQuestions.id, match.id)); + .where(eq(surveyQuestions.id, match.id)); if (match.type !== q.type) { // Shape changed — the old answers/options no longer apply to this question. - await db.delete(huddleSurveyAnswers).where(eq(huddleSurveyAnswers.questionId, match.id)); - await db.delete(huddleSurveyAnswerOptions).where(eq(huddleSurveyAnswerOptions.questionId, match.id)); - await db.delete(huddleSurveyOptions).where(eq(huddleSurveyOptions.questionId, match.id)); + await db.delete(surveyAnswers).where(eq(surveyAnswers.questionId, match.id)); + await db.delete(surveyAnswerOptions).where(eq(surveyAnswerOptions.questionId, match.id)); + await db.delete(surveyOptions).where(eq(surveyOptions.questionId, match.id)); if (q.options.length > 0) { await db - .insert(huddleSurveyOptions) + .insert(surveyOptions) .values(q.options.map((label, j) => ({ questionId: match.id, label, sortOrder: j }))); } } else if (CHOICE_TYPES.has(q.type)) { @@ -664,22 +664,22 @@ export async function submitResponse(opts: { const [existing] = await db .select() - .from(huddleSurveyResponses) - .where(and(eq(huddleSurveyResponses.surveyId, survey.id), eq(huddleSurveyResponses.userId, opts.userId))) + .from(surveyResponses) + .where(and(eq(surveyResponses.surveyId, survey.id), eq(surveyResponses.userId, opts.userId))) .limit(1); let responseId: string; if (existing) { responseId = existing.id; - await db.delete(huddleSurveyAnswers).where(eq(huddleSurveyAnswers.responseId, responseId)); - await db.delete(huddleSurveyAnswerOptions).where(eq(huddleSurveyAnswerOptions.responseId, responseId)); + await db.delete(surveyAnswers).where(eq(surveyAnswers.responseId, responseId)); + await db.delete(surveyAnswerOptions).where(eq(surveyAnswerOptions.responseId, responseId)); await db - .update(huddleSurveyResponses) + .update(surveyResponses) .set({ updatedAt: new Date() }) - .where(eq(huddleSurveyResponses.id, responseId)); + .where(eq(surveyResponses.id, responseId)); } else { const [responseRow] = await db - .insert(huddleSurveyResponses) + .insert(surveyResponses) .values({ surveyId: survey.id, userId: opts.userId }) .returning(); if (!responseRow) return fail(500, "Failed to record response"); @@ -694,13 +694,13 @@ export async function submitResponse(opts: { const optionIds = [...new Set(answer.optionIds ?? [])]; if (optionIds.length > 0) { await db - .insert(huddleSurveyAnswerOptions) + .insert(surveyAnswerOptions) .values(optionIds.map((optionId) => ({ responseId, questionId: q.id, optionId }))); } } else { const textValue = answer.textValue?.trim() ?? ""; if (textValue) { - await db.insert(huddleSurveyAnswers).values({ responseId, questionId: q.id, textValue }); + await db.insert(surveyAnswers).values({ responseId, questionId: q.id, textValue }); } } } @@ -723,9 +723,9 @@ export async function setResultsPublished( if (!survey) fail(404, "Survey not found"); await db - .update(huddleSurveys) + .update(surveys) .set({ resultsPublished: published }) - .where(eq(huddleSurveys.id, surveyId)); + .where(eq(surveys.id, surveyId)); } export async function deleteSurvey(huddleId: string, surveyId: string, userId: string): Promise { @@ -735,5 +735,5 @@ export async function deleteSurvey(huddleId: string, surveyId: string, userId: s const survey = await getSurveyRow(huddleId, surveyId); if (!survey) fail(404, "Survey not found"); - await db.delete(huddleSurveys).where(eq(huddleSurveys.id, surveyId)); + await db.delete(surveys).where(eq(surveys.id, surveyId)); } diff --git a/server/src/services/trophyControlService.ts b/server/src/services/trophyControlService.ts index 9ab7a7a..f60762b 100644 --- a/server/src/services/trophyControlService.ts +++ b/server/src/services/trophyControlService.ts @@ -7,7 +7,7 @@ */ import { eq, and } from "drizzle-orm"; import { db } from "../db/client.js"; -import { huddleActiveTrophies } from "../db/schema.js"; +import { activeTrophies } from "../db/schema.js"; import { HuddlesServiceError, isCommissioner } from "./huddlesService.js"; const fail = (status: number, message: string): never => { @@ -34,8 +34,8 @@ export async function getActiveTrophies( ): Promise> { const rows = await db .select() - .from(huddleActiveTrophies) - .where(eq(huddleActiveTrophies.huddleId, huddleId)); + .from(activeTrophies) + .where(eq(activeTrophies.huddleId, huddleId)); const result: Record = {}; // Default all types to enabled @@ -44,7 +44,7 @@ export async function getActiveTrophies( } // Apply saved overrides for (const row of rows) { - result[row.trophyType] = row.enabled === 1; + result[row.trophyType] = row.enabled; } return result; } @@ -65,15 +65,15 @@ export async function setTrophyEnabled( fail(400, `Unknown trophy type: ${trophyType}`); await db - .insert(huddleActiveTrophies) + .insert(activeTrophies) .values({ huddleId, trophyType, - enabled: enabled ? 1 : 0, + enabled, updatedAt: new Date(), }) .onConflictDoUpdate({ - target: [huddleActiveTrophies.huddleId, huddleActiveTrophies.trophyType], - set: { enabled: enabled ? 1 : 0, updatedAt: new Date() }, + target: [activeTrophies.huddleId, activeTrophies.trophyType], + set: { enabled, updatedAt: new Date() }, }); } diff --git a/server/src/services/usersService.ts b/server/src/services/usersService.ts new file mode 100644 index 0000000..98b82ed --- /dev/null +++ b/server/src/services/usersService.ts @@ -0,0 +1,162 @@ +/** + * usersService — the `users` table is our own copy of who's who. + * + * Two kinds of data live on a user row: + * + * 1. **Identity** (email, username) — a *cache* of Clerk's copy. Refreshed + * lazily by `ensureUser` when it's older than IDENTITY_TTL_MS. Nothing + * else in the app is allowed to call Clerk for this; if you need a + * display name for a set of user ids, use `getUserSummaries`, which is a + * single SQL round-trip instead of an HTTP call to Clerk. + * + * 2. **Product data** (sleeperUsername, sleeperUserId, syncedLeagueIds) — + * *owned* here. This used to live in Clerk's `unsafeMetadata`, which made + * it the one piece of product state we couldn't recover without an export + * from a third party. It is now plain Postgres. + * + * Row lifecycle: `ensureUser` is called by `GET /api/user/me`, which AuthGuard + * hits on every app load before any other request goes out. That's what + * guarantees a row exists for the caller, which in turn is what makes the + * foreign keys on every `user_id` column safe. + */ +import { eq, inArray } from "drizzle-orm"; +import { createClerkClient } from "@clerk/express"; +import { db } from "../db/client.js"; +import { users, type User } from "../db/schema.js"; + +const clerkSecretKey = process.env["CLERK_SECRET_KEY"]; +if (!clerkSecretKey) { + throw new Error("Missing required environment variable: CLERK_SECRET_KEY"); +} + +export const clerkClient = createClerkClient({ secretKey: clerkSecretKey }); + +/** How stale a cached email/username may get before we re-read it from Clerk. */ +const IDENTITY_TTL_MS = 24 * 60 * 60 * 1000; + +export interface UserSummary { + id: string; + username: string | null; + email: string | null; +} + +function toSummary(u: Pick): UserSummary { + return { id: u.id, username: u.username, email: u.email }; +} + +/** + * Display info for a set of user ids, as a Map for O(1) lookup while + * serializing. Ids with no row are simply absent — callers already fall back + * to `{ id, username: null, email: null }`. + */ +export async function getUserSummaries( + userIds: string[], +): Promise> { + const unique = [...new Set(userIds.filter(Boolean))]; + if (unique.length === 0) return new Map(); + + const rows = await db + .select({ + id: users.id, + username: users.username, + email: users.email, + }) + .from(users) + .where(inArray(users.id, unique)); + + return new Map(rows.map((r) => [r.id, toSummary(r)])); +} + +export async function getUser(userId: string): Promise { + const [row] = await db + .select() + .from(users) + .where(eq(users.id, userId)) + .limit(1); + return row ?? null; +} + +/** + * Guarantees a `users` row for `userId` and returns it, refreshing the cached + * identity fields from Clerk if the row is new or its cache has expired. + * + * If Clerk is unreachable but we already have a row, the stale row is returned + * rather than failing the request — identity is a cache, and a day-old username + * is better than a 503. A missing row with Clerk down does throw, because we + * have nothing to serve. + */ +export async function ensureUser(userId: string): Promise { + const existing = await getUser(userId); + + const isFresh = + existing !== null && + Date.now() - existing.identitySyncedAt.getTime() < IDENTITY_TTL_MS; + if (isFresh) return existing; + + let email: string | null = null; + let username: string | null = null; + try { + const clerkUser = await clerkClient.users.getUser(userId); + username = clerkUser.username; + email = + clerkUser.primaryEmailAddress?.emailAddress ?? + clerkUser.emailAddresses[0]?.emailAddress ?? + null; + } catch (err) { + if (existing) return existing; + throw err; + } + + const [row] = await db + .insert(users) + .values({ id: userId, email, username }) + .onConflictDoUpdate({ + target: users.id, + set: { + email, + username, + // A real sign-in proves the account exists again. + isPlaceholder: false, + identitySyncedAt: new Date(), + updatedAt: new Date(), + }, + }) + .returning(); + + return row!; +} + +/** Best-effort display handle, used for auto-naming a new huddle. */ +export function displayHandle(user: User): string { + return user.username ?? user.email?.split("@")[0] ?? "Your"; +} + +export async function setSleeperLink( + userId: string, + opts: { sleeperUsername: string | null; sleeperUserId: string | null }, +): Promise { + const [row] = await db + .update(users) + .set({ + sleeperUsername: opts.sleeperUsername, + sleeperUserId: opts.sleeperUserId, + // Unlinking clears the synced leagues so re-linking starts fresh. + ...(opts.sleeperUsername === null ? { syncedLeagueIds: [] } : {}), + updatedAt: new Date(), + }) + .where(eq(users.id, userId)) + .returning(); + return row!; +} + +export async function setSyncedLeagues( + userId: string, + syncedLeagueIds: string[], +): Promise { + const [row] = await db + .update(users) + .set({ syncedLeagueIds, updatedAt: new Date() }) + .where(eq(users.id, userId)) + .returning(); + return row!; +}