Fix race window in user creation and restore FlagSvg safety fallback

auth.server.ts: switch generateId to application-generated UUIDs so the
ID is known before the insert, then move flagConfig assignment from
create.after (a separate UPDATE) to create.before so it lands in the
initial INSERT with no race window.

FlagSvg.tsx: validate the config with parseFlagConfig before rendering;
corrupt or missing data falls back to a neutral gray triband flag rather
than silently rendering blank SVG shapes.

https://claude.ai/code/session_01T7iTb9YZLuWJFtKV753tdB
This commit is contained in:
Claude 2026-05-07 17:01:11 +00:00
parent c50386ed4c
commit 147ebc859d
No known key found for this signature in database
2 changed files with 25 additions and 20 deletions

View file

@ -1,5 +1,5 @@
import { cn } from "~/lib/utils";
import type { FlagConfig } from "~/lib/flag-types";
import { parseFlagConfig, type FlagConfig } from "~/lib/flag-types";
interface FlagSvgProps {
config: FlagConfig;
@ -8,8 +8,11 @@ interface FlagSvgProps {
title?: string;
}
const FALLBACK_FLAG: FlagConfig = { pattern: "triband-h", colors: ["#9ca3af", "#6b7280", "#4b5563"] };
export function FlagSvg({ config, size = 48, className, title }: FlagSvgProps) {
const [c0 = "", c1 = "", c2 = ""] = config.colors;
const safeConfig = parseFlagConfig(config) ?? FALLBACK_FLAG;
const [c0, c1, c2] = safeConfig.colors;
return (
<svg
@ -21,28 +24,28 @@ export function FlagSvg({ config, size = 48, className, title }: FlagSvgProps) {
className={cn("block shrink-0 overflow-hidden", className)}
>
{title ? <title>{title}</title> : null}
{config.pattern === "triband-h" && (
{safeConfig.pattern === "triband-h" && (
<>
<rect width="100" height="34" fill={c0} />
<rect y="33" width="100" height="34" fill={c1} />
<rect y="66" width="100" height="34" fill={c2} />
</>
)}
{config.pattern === "triband-v" && (
{safeConfig.pattern === "triband-v" && (
<>
<rect width="34" height="100" fill={c0} />
<rect x="33" width="34" height="100" fill={c1} />
<rect x="66" width="34" height="100" fill={c2} />
</>
)}
{config.pattern === "diagonal" && (
{safeConfig.pattern === "diagonal" && (
<>
<rect width="100" height="100" fill={c0} />
<polygon points="0,0 100,0 0,100" fill={c1} />
<polygon points="100,100 100,28 28,100" fill={c2} />
</>
)}
{config.pattern === "nordic-cross" && (
{safeConfig.pattern === "nordic-cross" && (
<>
<rect width="100" height="100" fill={c0} />
<rect x="30" width="18" height="100" fill={c1} />
@ -51,7 +54,7 @@ export function FlagSvg({ config, size = 48, className, title }: FlagSvgProps) {
<rect y="46" width="100" height="6" fill={c2} />
</>
)}
{config.pattern === "cross" && (
{safeConfig.pattern === "cross" && (
<>
<rect width="100" height="100" fill={c0} />
<rect x="40" width="20" height="100" fill={c1} />
@ -60,28 +63,28 @@ export function FlagSvg({ config, size = 48, className, title }: FlagSvgProps) {
<rect y="46" width="100" height="8" fill={c2} />
</>
)}
{config.pattern === "canton" && (
{safeConfig.pattern === "canton" && (
<>
<rect width="100" height="100" fill={c0} />
<rect width="50" height="50" fill={c1} />
<rect x="12" y="12" width="26" height="26" fill={c2} />
</>
)}
{config.pattern === "triangle" && (
{safeConfig.pattern === "triangle" && (
<>
<rect width="100" height="100" fill={c0} />
<polygon points="0,0 64,50 0,100" fill={c1} />
<polygon points="0,22 36,50 0,78" fill={c2} />
</>
)}
{config.pattern === "chevron" && (
{safeConfig.pattern === "chevron" && (
<>
<rect width="100" height="100" fill={c0} />
<polygon points="0,0 56,50 0,100 28,100 84,50 28,0" fill={c1} />
<polygon points="0,22 32,50 0,78 0,62 14,50 0,38" fill={c2} />
</>
)}
{config.pattern === "quartered" && (
{safeConfig.pattern === "quartered" && (
<>
<rect width="50" height="50" fill={c0} />
<rect x="50" width="50" height="50" fill={c1} />
@ -89,21 +92,21 @@ export function FlagSvg({ config, size = 48, className, title }: FlagSvgProps) {
<rect x="50" y="50" width="50" height="50" fill={c2} />
</>
)}
{config.pattern === "bordered" && (
{safeConfig.pattern === "bordered" && (
<>
<rect width="100" height="100" fill={c0} />
<rect x="12" y="12" width="76" height="76" fill={c1} />
<rect x="24" y="24" width="52" height="52" fill={c2} />
</>
)}
{config.pattern === "circle" && (
{safeConfig.pattern === "circle" && (
<>
<rect width="100" height="100" fill={c0} />
<circle cx="50" cy="50" r="30" fill={c1} />
<circle cx="50" cy="50" r="15" fill={c2} />
</>
)}
{config.pattern === "star" && (
{safeConfig.pattern === "star" && (
<>
<rect width="100" height="100" fill={c0} />
<polygon

View file

@ -32,11 +32,13 @@ export const auth = betterAuth({
databaseHooks: {
user: {
create: {
after: async (user) => {
await db
.update(schema.users)
.set({ flagConfig: generateFlagConfig(user.id) })
.where(eq(schema.users.id, user.id));
before: async (data) => {
return {
data: {
...data,
flagConfig: generateFlagConfig(data.id),
} as typeof data,
};
},
},
update: {
@ -63,7 +65,7 @@ export const auth = betterAuth({
},
advanced: {
database: {
generateId: false,
generateId: () => crypto.randomUUID(),
},
},
user: {