From 75dedd0a90321ce224fbcb3fddc64b5c333b13cb Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Wed, 29 Apr 2026 09:58:49 -0700 Subject: [PATCH] Fix BetterAuth ID generation, clean up review issues - Set generateId: false so BetterAuth omits id from inserts; add $defaultFn(crypto.randomUUID) to accounts/sessions/verifications so Drizzle fills it in (fixes null id constraint violation on signup) - Move renameTeam to static import; rename before removeTeamOwner so a failed rename leaves owner intact rather than leaving a stale prefix - Clarify stripOwnerFromTeamName toTitleCase assumption in comment - Annotate reset-password token fallback to explain loader guarantee Co-Authored-By: Claude Sonnet 4.6 --- app/lib/auth.server.ts | 2 +- app/routes/leagues/$leagueId.settings.tsx | 5 ++--- app/routes/reset-password.tsx | 2 +- app/utils/team-names.ts | 2 ++ database/schema.ts | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/lib/auth.server.ts b/app/lib/auth.server.ts index 0dca6c5..544db57 100644 --- a/app/lib/auth.server.ts +++ b/app/lib/auth.server.ts @@ -25,7 +25,7 @@ export const auth = betterAuth({ }), advanced: { database: { - generateId: "uuid", + generateId: false, }, }, user: { diff --git a/app/routes/leagues/$leagueId.settings.tsx b/app/routes/leagues/$leagueId.settings.tsx index c32b15f..ff012d9 100644 --- a/app/routes/leagues/$leagueId.settings.tsx +++ b/app/routes/leagues/$leagueId.settings.tsx @@ -15,7 +15,7 @@ import { removeCommissionerByLeagueAndUser, } from "~/models/commissioner"; import { findCurrentSeasonWithSports, updateSeason, type NewSeason } from "~/models/season"; -import { findTeamsBySeasonId, deleteTeam, removeTeamOwner, claimTeam, findTeamById } from "~/models/team"; +import { findTeamsBySeasonId, deleteTeam, removeTeamOwner, claimTeam, findTeamById, renameTeam } from "~/models/team"; import { findAllUsers, findUsersByIds, findUserById, getUserDisplayName, isUserAdmin } from "~/models/user"; import { prependOwnerToTeamName, stripOwnerFromTeamName, generateUniqueTeamNames } from "~/utils/team-names"; import { unlinkSportFromSeason, linkMultipleSportsToSeason } from "~/models/season-sport"; @@ -407,11 +407,10 @@ export async function action(args: Route.ActionArgs) { const username = owner?.username ?? owner?.displayName; if (username) { const strippedName = stripOwnerFromTeamName(team.name, username); - await removeTeamOwner(teamId); if (strippedName !== team.name) { - const { renameTeam } = await import("~/models/team"); await renameTeam(teamId, strippedName); } + await removeTeamOwner(teamId); } else { await removeTeamOwner(teamId); } diff --git a/app/routes/reset-password.tsx b/app/routes/reset-password.tsx index 6f5f1ea..37b0ba7 100644 --- a/app/routes/reset-password.tsx +++ b/app/routes/reset-password.tsx @@ -22,7 +22,7 @@ export async function loader(args: Route.LoaderArgs) { export default function ResetPasswordPage() { const [searchParams] = useSearchParams(); - const token = searchParams.get("token") ?? ""; + const token = searchParams.get("token") ?? ""; // loader redirects to /forgot-password when absent const [error, setError] = useState(null); const [loading, setLoading] = useState(false); diff --git a/app/utils/team-names.ts b/app/utils/team-names.ts index b379639..a41df36 100644 --- a/app/utils/team-names.ts +++ b/app/utils/team-names.ts @@ -93,6 +93,8 @@ export function prependOwnerToTeamName( return `${titleName}${possessiveSuffix(titleName)} ${baseName}`; } +// Relies on the name having been set via prependOwnerToTeamName (same toTitleCase logic). +// If username casing differs from when the name was set, the strip is a safe no-op. export function stripOwnerFromTeamName( teamName: string, username: string diff --git a/database/schema.ts b/database/schema.ts index 02a5e21..8466407 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -19,7 +19,7 @@ export const users = pgTable("users", { // BetterAuth session/account/verification tables export const sessions = pgTable("sessions", { - id: text("id").primaryKey(), + id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()), expiresAt: timestamp("expires_at").notNull(), token: text("token").notNull().unique(), userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), @@ -30,7 +30,7 @@ export const sessions = pgTable("sessions", { }); export const accounts = pgTable("accounts", { - id: text("id").primaryKey(), + id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()), accountId: text("account_id").notNull(), providerId: text("provider_id").notNull(), userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), @@ -47,7 +47,7 @@ export const accounts = pgTable("accounts", { }); export const verifications = pgTable("verifications", { - id: text("id").primaryKey(), + id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: timestamp("expires_at").notNull(),