make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap(); use Illuminate\Support\Facades\DB; $outPath = $argv[1] ?? __DIR__ . '/legacy-passwords-export.sql'; try { DB::connection('legacy')->getPdo(); } catch (\Throwable $e) { fwrite(STDERR, "Legacy DB connection not available: " . $e->getMessage() . PHP_EOL); exit(2); } $now = date('Y-m-d H:i:s'); $fh = fopen($outPath, 'wb'); if ($fh === false) { fwrite(STDERR, "Could not open output file: {$outPath}\n"); exit(3); } fwrite($fh, "-- Legacy password export\n"); fwrite($fh, "-- Generated: {$now}\n"); fwrite($fh, "-- Source: legacy DB (read-only)\n\n"); fwrite($fh, "SET NAMES utf8mb4;\n"); fwrite($fh, "USE `" . DB::getDatabaseName() . "`;\n"); fwrite($fh, "START TRANSACTION;\n\n"); $exported = 0; DB::connection('legacy') ->table('users') ->select(['user_id', 'password2', 'password']) ->orderBy('user_id') ->chunk(500, function ($rows) use (&$fh, &$exported, $now) { foreach ($rows as $r) { $id = (int) ($r->user_id ?? 0); $hash = trim((string) ($r->password2 ?: $r->password ?: '')); if ($id === 0 || $hash === '') { continue; } // detect common hash types $algo = 'unknown'; if (preg_match('/^\$2[aby]\$/', $hash)) { $algo = 'bcrypt'; } elseif (preg_match('/^\$argon2/', $hash)) { $algo = 'argon2'; } // escape for SQL single-quoted literal $escaped = str_replace(['\\', "'"], ['\\\\', "\\'"], $hash); fwrite($fh, "-- user_id={$id} legacy_algo={$algo}\n"); fwrite($fh, "SAVEPOINT sp_{$id};\n"); fwrite($fh, "UPDATE `users` SET `password` = '{$escaped}', `legacy_password_algo` = '" . $algo . "', `needs_password_reset` = 1, `updated_at` = '{$now}' WHERE `id` = {$id};\n\n"); $exported++; } }); fwrite($fh, "COMMIT;\n\n"); fwrite($fh, "-- Exported: {$exported} user(s)\n"); fclose($fh); fwrite(STDOUT, "Wrote {$exported} rows to: {$outPath}\n"); exit(0);