Fix migrate.mjs: run FK conversion unconditionally, not gated on CLERK_SECRET_KEY

The FK conversion (teams.owner_id, commissioners.user_id → UUIDs) and
email_verified update were inside runClerkMigration() which only ran when
CLERK_SECRET_KEY was set. If the key wasn't in the environment at deploy
time, all Clerk IDs were left unconverted, causing UUID parse errors at
runtime.

Split into two functions:
- convertForeignKeys(): always runs, idempotent WHERE clauses
- importClerkOAuthAccounts(): only runs when CLERK_SECRET_KEY is set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-04-24 22:41:41 -07:00
parent 65478e1ef2
commit cd8074ef10

View file

@ -30,20 +30,29 @@ try {
}
// ── Clerk → BetterAuth one-time data migration ────────────────────────────────
// Runs on every deploy but is fully idempotent. Skips automatically once
// CLERK_SECRET_KEY is removed from the environment.
// FK conversion and email_verified always run (idempotent WHERE clauses).
// OAuth account import only runs when CLERK_SECRET_KEY is set.
console.log("Running FK conversion...");
try {
await convertForeignKeys(client);
} catch (err) {
console.error("FK conversion failed:", err);
await client.end().catch(() => {});
process.exit(1);
}
const CLERK_SECRET_KEY = process.env.CLERK_SECRET_KEY;
if (!CLERK_SECRET_KEY) {
console.log("CLERK_SECRET_KEY not set — skipping Clerk account migration");
console.log("CLERK_SECRET_KEY not set — skipping Clerk OAuth account import");
} else {
console.log("Running Clerk account migration...");
console.log("Running Clerk OAuth account import...");
try {
await runClerkMigration(client, CLERK_SECRET_KEY);
console.log("Clerk account migration complete");
await importClerkOAuthAccounts(client, CLERK_SECRET_KEY);
console.log("Clerk OAuth account import complete");
} catch (err) {
console.error("Clerk account migration failed:", err);
console.error("Clerk OAuth account import failed:", err);
await client.end().catch(() => {});
process.exit(1);
}
@ -54,8 +63,8 @@ process.exit(0);
// ─────────────────────────────────────────────────────────────────────────────
async function runClerkMigration(sql, secretKey) {
// 1. Convert teams.owner_id from Clerk IDs to users.id UUIDs
async function convertForeignKeys(sql) {
// Convert teams.owner_id from Clerk IDs to users.id UUIDs.
// Safe to run repeatedly — WHERE clause only matches unconverted rows.
const teamsResult = await sql`
UPDATE teams t
@ -66,7 +75,7 @@ async function runClerkMigration(sql, secretKey) {
`;
console.log(` teams.owner_id: ${teamsResult.count} rows converted`);
// 2. Convert commissioners.user_id from Clerk IDs to users.id UUIDs
// Convert commissioners.user_id from Clerk IDs to users.id UUIDs
const commissResult = await sql`
UPDATE commissioners c
SET user_id = u.id::text
@ -76,15 +85,17 @@ async function runClerkMigration(sql, secretKey) {
`;
console.log(` commissioners.user_id: ${commissResult.count} rows converted`);
// 3. Mark all Clerk users as email-verified (they already verified via Clerk)
// Mark all Clerk users as email-verified (they already verified via Clerk)
await sql`
UPDATE users
SET email_verified = true
WHERE clerk_id IS NOT NULL
AND email_verified = false
`;
}
// 4. Migrate OAuth accounts from Clerk into BetterAuth's accounts table.
async function importClerkOAuthAccounts(sql, secretKey) {
// Migrate OAuth accounts from Clerk into BetterAuth's accounts table.
// Skip entirely if any OAuth accounts already exist to avoid duplicates.
// We check only OAuth providers (not 'credential') so running migrate-clerk-passwords.mjs
// first doesn't prevent OAuth accounts from being created.