package main import ( "context" "database/sql" "log" "github.com/jackc/pgx/v5/pgxpool" ) // applyPlatformAdmins maps legacy admin_users → users.is_platform_admin. // Matching prefers remapped Clerk/legacy user_id, then email. Never creates orphan admin rows. // Company-admin memberships (member→admin) are a separate post-load step: // see runMembershipRoleRepair (-list-member-memberships / -promote-company-admins). func applyPlatformAdmins( ctx context.Context, mysqlDB *sql.DB, pg *pgxpool.Pool, userMap map[string]string, report map[string]int, dryRun bool, ) { if !mysqlTableExists(ctx, mysqlDB, "admin_users") { log.Printf("admin_users skipped: table missing") return } // Legacy shape: user_id (Clerk text) + email. Column presence varies by dump age. hasUserID := mysqlColumnExists(ctx, mysqlDB, "admin_users", "user_id") hasEmail := mysqlColumnExists(ctx, mysqlDB, "admin_users", "email") if !hasUserID && !hasEmail { log.Printf("admin_users skipped: no user_id/email columns") return } q := `SELECT ` switch { case hasUserID && hasEmail: q += `COALESCE(user_id, ''), COALESCE(email, '') FROM admin_users` case hasUserID: q += `user_id, '' FROM admin_users` default: q += `'', email FROM admin_users` } rows, err := mysqlDB.QueryContext(ctx, q) if err != nil { log.Printf("admin_users skipped: %v", err) return } defer rows.Close() for rows.Next() { var legacyUserID, email string if err := rows.Scan(&legacyUserID, &email); err != nil { report["admin_users_skipped"]++ continue } pgUserID, ok := userMap[legacyUserID] if !ok && email != "" { // Resolve via email already loaded into Postgres (or dry-run map miss). if dryRun { report["admin_users_unmatched"]++ continue } var id string err := pg.QueryRow(ctx, `SELECT id::text FROM users WHERE lower(email) = lower($1)`, email).Scan(&id) if err != nil { report["admin_users_unmatched"]++ continue } pgUserID = id } if pgUserID == "" { report["admin_users_unmatched"]++ continue } if dryRun { report["admin_users"]++ continue } tag, err := pg.Exec(ctx, ` UPDATE users SET is_platform_admin = true, staff_role = COALESCE(staff_role, 'admin'), updated_at = now() WHERE id = $1::uuid`, pgUserID) if err != nil { log.Printf("admin_users update %s: %v", legacyUserID, err) report["admin_users_skipped"]++ continue } if tag.RowsAffected() == 0 { report["admin_users_unmatched"]++ continue } report["admin_users"]++ } }