diff --git a/artifacts/api-server/src/index.ts b/artifacts/api-server/src/index.ts index 3efdf09..86f89bb 100644 --- a/artifacts/api-server/src/index.ts +++ b/artifacts/api-server/src/index.ts @@ -83,30 +83,33 @@ async function ensureToolRelationsTable(): Promise { async function ensureUserColumns(): Promise { try { - const cols = await db.execute( - sql`SELECT column_name FROM information_schema.columns WHERE table_name = 'users'`, - ); - const existing = new Set((cols.rows as [{ column_name: string }]).map((r) => r.column_name)); - const toAdd: { name: string; def: string }[] = []; - if (!existing.has("tier")) toAdd.push({ name: "tier", def: "text NOT NULL DEFAULT 'free'" }); - if (!existing.has("auth_provider")) toAdd.push({ name: "auth_provider", def: "text NOT NULL DEFAULT 'local'" }); - if (!existing.has("auth_provider_id")) toAdd.push({ name: "auth_provider_id", def: "text" }); - if (!existing.has("display_name")) toAdd.push({ name: "display_name", def: "text" }); - if (!existing.has("password_hash")) { - toAdd.push({ name: "password_hash", def: "text" }); - } else { - const [ph] = await db.execute( - sql`SELECT is_nullable FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'password_hash'`, - ); - const nullable = (ph.rows as [{ is_nullable: string }])[0]?.is_nullable; - if (nullable === "NO") { - await db.execute(sql`ALTER TABLE "users" ALTER COLUMN "password_hash" DROP NOT NULL`); - } + const rows = (await db.execute( + sql`SELECT column_name, is_nullable FROM information_schema.columns WHERE table_name = 'users'`, + )).rows as { column_name: string; is_nullable: string }[]; + const existing = new Map(rows.map((r) => [r.column_name, r.is_nullable])); + + if (!existing.has("tier")) { + await db.execute(sql`ALTER TABLE "users" ADD COLUMN "tier" text NOT NULL DEFAULT 'free'`); + logger.info("Added column: tier"); } - for (const col of toAdd) { - const stmt = `ALTER TABLE "users" ADD COLUMN "${col.name}" ${col.def}`; - await db.execute(sql.raw(stmt)); - logger.info({ column: col.name }, "Added column to users table"); + if (!existing.has("auth_provider")) { + await db.execute(sql`ALTER TABLE "users" ADD COLUMN "auth_provider" text NOT NULL DEFAULT 'local'`); + logger.info("Added column: auth_provider"); + } + if (!existing.has("auth_provider_id")) { + await db.execute(sql`ALTER TABLE "users" ADD COLUMN "auth_provider_id" text`); + logger.info("Added column: auth_provider_id"); + } + if (!existing.has("display_name")) { + await db.execute(sql`ALTER TABLE "users" ADD COLUMN "display_name" text`); + logger.info("Added column: display_name"); + } + if (!existing.has("password_hash")) { + await db.execute(sql`ALTER TABLE "users" ADD COLUMN "password_hash" text`); + logger.info("Added column: password_hash"); + } else if (existing.get("password_hash") === "NO") { + await db.execute(sql`ALTER TABLE "users" ALTER COLUMN "password_hash" DROP NOT NULL`); + logger.info("Made password_hash nullable"); } } catch (err) { logger.error({ err }, "Failed to ensure user columns"); @@ -166,6 +169,7 @@ async function start(): Promise { await ensureToolRelationsTable(); await ensureToolCostsTable(); await ensureVoterTokenColumn(); + await ensureUserColumns(); await seedAdminUser(); await ensureAdminTier();