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 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-04-29 09:58:49 -07:00
parent 7e8c5afde5
commit 75dedd0a90
5 changed files with 9 additions and 8 deletions

View file

@ -25,7 +25,7 @@ export const auth = betterAuth({
}),
advanced: {
database: {
generateId: "uuid",
generateId: false,
},
},
user: {

View file

@ -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);
}

View file

@ -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<string | null>(null);
const [loading, setLoading] = useState(false);

View file

@ -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

View file

@ -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(),