vmangos
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\VMangosSrpService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use RuntimeException;
|
||||
|
||||
class ImportWebAccountsToVMangos extends Command
|
||||
{
|
||||
protected $signature = 'vmangos:import-web-accounts {--dry-run : Show counts without writing} {--include-bots : Import RNDBOT accounts too}';
|
||||
|
||||
protected $description = 'Create vMaNGOS accounts for existing web users, preserving their web passwords and using random game passwords';
|
||||
|
||||
public function handle(VMangosSrpService $srp): int
|
||||
{
|
||||
$source = DB::connection(config('moonwell.legacy_auth_connection'));
|
||||
$target = DB::connection(config('moonwell.auth_connection'));
|
||||
$query = $source->table('account')->selectRaw('id, username, email, locked, joindate, HEX(salt) AS salt_hex, HEX(verifier) AS verifier_hex')->orderBy('id');
|
||||
|
||||
if (! $this->option('include-bots')) {
|
||||
$query->where('username', 'not like', config('moonwell.portal.bot_account_prefix').'%');
|
||||
}
|
||||
|
||||
$created = 0;
|
||||
$linked = 0;
|
||||
$skipped = 0;
|
||||
foreach ($query->cursor() as $legacy) {
|
||||
$username = $srp->normalizeUsername($legacy->username);
|
||||
if (preg_match('/^[A-Z0-9]{3,16}$/D', $username) !== 1) {
|
||||
throw new RuntimeException("Unsupported legacy account name for ID {$legacy->id}.");
|
||||
}
|
||||
|
||||
$existing = $target->table('account')->where('username', $username)->first(['id']);
|
||||
$credential = $existing ? $target->table('site_credentials')->where('account_id', $existing->id)->first() : null;
|
||||
if ($credential) {
|
||||
if ((int) $credential->legacy_account_id !== (int) $legacy->id) {
|
||||
throw new RuntimeException("Account name conflict for legacy ID {$legacy->id}.");
|
||||
}
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->option('dry-run')) {
|
||||
$existing ? $linked++ : $created++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$target->transaction(function () use ($target, $source, $srp, $legacy, $username, $existing): void {
|
||||
// Only the SRP verifier is stored; the random plaintext is discarded.
|
||||
[$gameSalt, $gameVerifier] = $srp->makeRegistrationData($username, bin2hex(random_bytes(16)));
|
||||
if ($existing) {
|
||||
$accountId = (int) $existing->id;
|
||||
$target->table('account')->where('id', $accountId)->update([
|
||||
's' => $gameSalt,
|
||||
'v' => $gameVerifier,
|
||||
'sessionkey' => null,
|
||||
'email' => $legacy->email,
|
||||
'locked' => (int) $legacy->locked,
|
||||
]);
|
||||
} else {
|
||||
$accountId = (int) $target->table('account')->insertGetId([
|
||||
'username' => $username,
|
||||
's' => $gameSalt,
|
||||
'v' => $gameVerifier,
|
||||
'email' => $legacy->email,
|
||||
'locked' => (int) $legacy->locked,
|
||||
'expansion' => 0,
|
||||
'joindate' => $legacy->joindate,
|
||||
]);
|
||||
}
|
||||
|
||||
$target->table('site_credentials')->insert([
|
||||
'account_id' => $accountId,
|
||||
'legacy_account_id' => (int) $legacy->id,
|
||||
'salt_hex' => $legacy->salt_hex,
|
||||
'verifier_hex' => $legacy->verifier_hex,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
foreach ($target->table('realmlist')->pluck('id') as $realmId) {
|
||||
$target->table('realmcharacters')->insertOrIgnore(['realmid' => (int) $realmId, 'acctid' => $accountId, 'numchars' => 0]);
|
||||
}
|
||||
|
||||
$gmLevel = (int) $source->table('account_access')->where('id', $legacy->id)->max('gmlevel');
|
||||
if ($gmLevel > 0) {
|
||||
$target->table('account_access')->updateOrInsert(
|
||||
['id' => $accountId, 'RealmID' => -1],
|
||||
['gmlevel' => $gmLevel],
|
||||
);
|
||||
}
|
||||
}, 3);
|
||||
|
||||
$existing ? $linked++ : $created++;
|
||||
}
|
||||
|
||||
if (! $this->option('dry-run') && ($created + $linked) > 0) {
|
||||
// Old bearer tokens and sessions contain AzerothCore IDs, which may
|
||||
// now identify a different vMaNGOS account. Force fresh login.
|
||||
DB::table('oauth_access_tokens')->where('revoked', false)->update(['revoked' => true]);
|
||||
DB::table('oauth_refresh_tokens')->where('revoked', false)->update(['revoked' => true]);
|
||||
if (config('session.driver') === 'database') {
|
||||
DB::table(config('session.table', 'sessions'))->delete();
|
||||
} elseif (config('session.driver') === 'file') {
|
||||
foreach (File::files(config('session.files')) as $sessionFile) {
|
||||
File::delete($sessionFile->getPathname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("Created: {$created}; linked: {$linked}; already imported: {$skipped}.");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user