DB Refactoring - concise titles - #116
Merged
Merged
Conversation
Clerk was acting as three things at once: the session provider, our user database, and the store for product data (Sleeper link, synced leagues) in `unsafeMetadata`. The last two are the parts that would be expensive to get back if we ever move off it, and the metadata had no Postgres copy at all. Clerk now owns sessions only. - Add a `users` table keyed by the *Clerk* user id, not a fresh uuid. All 18 user-referencing columns across 11 tables already store that string, so keeping it as the PK means a future auth swap is "issue our own JWTs with the same sub" rather than a renumbering of the whole database. - Move sleeperUsername / sleeperUserId / syncedLeagueIds out of `unsafeMetadata` into that table. Nothing writes to Clerk metadata anymore. - Add FKs on all 18 columns, `ON DELETE RESTRICT` — league history outlives an account, and a missed id in a future migration now errors instead of silently orphaning a forum post. Migration 0012 seeds placeholder rows from the data itself before adding the constraints, so it can't fail on an unseeded DB. - Replace `clerkClient.users.getUserList()` — an HTTP call to Clerk on the critical path of five request handlers — with a single SQL read. - Add `GET /api/user/me` as the client's source of truth; AuthGuard blocks on it (which is also what guarantees the row exists) instead of reading Clerk metadata, and `useInvalidateCurrentUser()` replaces `await user.reload()`. - Add scripts/backfill-users.mjs: idempotent Clerk -> Postgres mirror, usable both as the one-time backfill and as a recurring snapshot. Verified against the dev DB: both migrations apply, the backfill moved all 5 users' Sleeper links across, and the FKs reject both an orphaned insert and a delete of a referenced user. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e users The prefix looked like it meant "scoped to a huddle" but was wrong in both directions: team_claims and side_bets carried huddle_id without it, while the seven poll/survey child tables carried it without a huddle_id. ~80% consistent is worse than no convention — it invites an inference that doesn't hold. Settles on: no prefix = global, huddle_ = lives inside one huddle, directly or transitively. That makes `users` (and future auth tables) the meaningful exception rather than an eighteenth arbitrary case. - 0013: rename team_claims -> huddle_team_claims, side_bets -> huddle_side_bets (real ALTER TABLE ... RENAME, resolved through drizzle-kit's TTY prompt, so rows are preserved rather than dropped and recreated). - 0013: huddle_active_trophies.enabled from integer 1/0 to boolean — the schema uses boolean() in 8 other places. Drizzle's bare SET DATA TYPE would have failed; hand-edited to DROP DEFAULT / SET DATA TYPE ... USING / SET DEFAULT. - 0014: rename the leftover primary keys. Postgres keeps constraint names across a table rename and Drizzle doesn't track them, so groups_pkey had survived since 0001 and 0013 had just created two more of the same. Every PK now matches its table. - Drop the 1/0 translation in trophyControlService; the API already spoke boolean, only storage didn't. - Point backfill-users.mjs at the renamed tables. Verified on the dev DB: 3 team-claim rows survived the rename, enabled is boolean defaulting true, all 23 PKs match their table, 18 FKs intact, and the services reading these tables plus a live trophy toggle all still work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reverses the direction of 692a3f0, which made the prefix consistent when the right call was to remove it. Every table in this database belongs to Huddle, so the prefix carried no information — it just made `\dt` and autocomplete on `hud` return almost the entire schema. 21 tables renamed (huddle_commissioners -> commissioners, huddle_polls -> polls, ...). `huddles` and `users` are unchanged. team_claims and side_bets end up back at their original names, so 0013's renames were round-tripped; the rest of 0013 (the boolean conversion, the groups_pkey fix) still stands. The structural distinction the prefix pretended to encode isn't in the names and doesn't need to be: `users` is global, everything else is reachable from a huddle, directly or through a parent. Mechanics worth knowing (now in PLAYBOOK): - Postgres renames the table and nothing else, and Drizzle doesn't track constraint or index names, so they'd rot invisibly. 0015 renames 115 objects, generated from pg_constraint/pg_index against the live schema rather than by hand. - drizzle-kit can't be driven through 21 "created or renamed?" prompts reliably, so it was answered "create" — the snapshot records end state and is correct either way — and its destructive DROP/CREATE output replaced. - 0016 fixes a constraint name that had been silently truncated at Postgres's 63-byte limit when first created, losing its `_fk` suffix; 0015 carried the truncation forward. Caught by diffing the snapshot against pg_constraint, which is a stronger check than "no schema changes" from db:generate. Verified on the dev DB: row counts unchanged across the rename (3 claims, 3 huddles, 3 commissioners, 5 users), no table/index/constraint still carries the prefix, all 18 FKs to users intact, zero drift across all 77 FK and index names, and every service that reads these tables plus a live trophy toggle still works. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follows 6120cd0 through to the TypeScript layer. `HuddleAward` -> `Award`, `HuddlePoll` -> `Poll`, `HuddleSurveyResponse` -> `SurveyResponse`, and so on for 21 types across schema.ts, the services and the client. This mostly brings the server in line with the client rather than the reverse: client/src/types/huddle.ts had already settled on DuesConfig, ForumTopic, Poll, PollOption, SideBet and PayoutEntry, with only HuddleAward and HuddleAnnouncement still carrying the prefix. Both are now unprefixed too. Kept where `Huddle` is load-bearing rather than decorative: Huddle, HuddleClaim, HuddleClaimSummary, HuddleDetail, HuddleDetailResponse, HuddleMemberStatus. One collision: lucide-react exports an `Award` icon that CommissionerPage passes to StubSection, and `AwardIcon` was already taken by the award-glyph asset type from useHuddles — so the icon is imported as `LucideAward`. Server and client both typecheck; services reading every renamed table still return the same rows (1 claim, 1 commissioner, 2 announcements, 1 award, 5 payouts, 5 trophies) and the API responds as expected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request migrates user profile ownership from Clerk metadata to the app's own
userstable and API, and updates the client to fetch and manage user profile data from/api/user/meinstead of Clerk'sunsafeMetadata. It also introduces a new React Query hook for accessing and invalidating the current user's profile, updates documentation to reflect these changes, and improves type naming consistency. The most important changes are summarized below.User profile ownership and fetching:
The app now stores and manages user profile data (email, username, Sleeper link, synced leagues) in its own
userstable, keyed by Clerk user id. Clerk remains responsible for session management only. The client fetches the profile from/api/user/meand mirrors it into Redux, replacing all previous usage of Clerk'sunsafeMetadata. [1] [2] [3]Introduced
useCurrentUseranduseInvalidateCurrentUserhooks for fetching and invalidating the signed-in user's profile via React Query, replacing the oldawait user.reload()pattern. [1] [2] [3] [4] [5]Documentation updates:
PLAYBOOK.mdandCLAUDE.mdto document the new user profile flow, the division of responsibility between Clerk and the app, and the rationale and rules for user and auth data ownership. Added instructions for backfilling theuserstable from Clerk. [1] [2] [3]Type and API consistency:
HuddleAnnouncement→Announcement,HuddleAward→Award) for consistency with new table naming conventions. [1] [2] [3] [4] [5]Database and migration guidance:
These changes centralize user profile data, simplify future auth migrations, and clarify the boundaries between Clerk and application-owned data.