Upload beautify
This commit is contained in:
@@ -2,82 +2,124 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\AvatarService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Services\AvatarService;
|
||||
|
||||
class AvatarsMigrate extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'avatars:migrate {--force}';
|
||||
protected $signature = 'avatars:migrate {--force : Reprocess avatars even when avatar_hash is already present} {--limit=0 : Limit number of users processed}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate legacy avatars to new WebP avatar storage';
|
||||
protected $description = 'Migrate legacy avatars into CDN-ready WebP variants and avatar_hash metadata';
|
||||
|
||||
protected $service;
|
||||
|
||||
public function __construct(AvatarService $service)
|
||||
public function __construct(private readonly AvatarService $service)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
public function handle(): int
|
||||
{
|
||||
$force = (bool) $this->option('force');
|
||||
$limit = max(0, (int) $this->option('limit'));
|
||||
|
||||
$this->info('Starting avatar migration...');
|
||||
|
||||
// Try to read legacy data from user_profiles.avatar_legacy or users.avatar_legacy or users.icon
|
||||
$rows = DB::table('user_profiles')->select('user_id', 'avatar_legacy')->whereNotNull('avatar_legacy')->get();
|
||||
$rows = DB::table('user_profiles as p')
|
||||
->leftJoin('users as u', 'u.id', '=', 'p.user_id')
|
||||
->select([
|
||||
'p.user_id',
|
||||
'p.avatar_hash',
|
||||
'p.avatar_legacy',
|
||||
'u.icon as user_icon',
|
||||
])
|
||||
->when(!$force, fn ($query) => $query->whereNull('p.avatar_hash'))
|
||||
->where(function ($query) {
|
||||
$query->whereNotNull('p.avatar_legacy')
|
||||
->orWhereNotNull('u.icon');
|
||||
})
|
||||
->orderBy('p.user_id')
|
||||
->when($limit > 0, fn ($query) => $query->limit($limit))
|
||||
->get();
|
||||
|
||||
if ($rows->isEmpty()) {
|
||||
// fallback to users table
|
||||
$rows = DB::table('users')->select('user_id', 'icon as avatar_legacy')->whereNotNull('icon')->get();
|
||||
$this->info('No avatars require migration.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$migrated = 0;
|
||||
$skipped = 0;
|
||||
$failed = 0;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$userId = $row->user_id;
|
||||
$legacy = $row->avatar_legacy ?? null;
|
||||
if (!$legacy) {
|
||||
$userId = (int) $row->user_id;
|
||||
$legacyName = $this->normalizeLegacyName($row->avatar_legacy ?: $row->user_icon);
|
||||
|
||||
if ($legacyName === null) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try common legacy paths
|
||||
$candidates = [
|
||||
public_path('user-picture/' . $legacy),
|
||||
public_path('avatar/' . $userId . '/' . $legacy),
|
||||
storage_path('app/public/user-picture/' . $legacy),
|
||||
storage_path('app/public/avatar/' . $userId . '/' . $legacy),
|
||||
];
|
||||
|
||||
$found = false;
|
||||
foreach ($candidates as $p) {
|
||||
if (file_exists($p) && is_readable($p)) {
|
||||
$this->info("Processing user {$userId} from {$p}");
|
||||
$hash = $this->service->storeFromLegacyFile($userId, $p);
|
||||
if ($hash) {
|
||||
$this->info(" -> migrated, hash={$hash}");
|
||||
$count++;
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$path = $this->locateLegacyAvatarPath($userId, $legacyName);
|
||||
if ($path === null) {
|
||||
$failed++;
|
||||
$this->warn("User {$userId}: legacy avatar not found ({$legacyName})");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$this->warn("Legacy file not found for user {$userId}, filename={$legacy}");
|
||||
try {
|
||||
$hash = $this->service->storeFromLegacyFile($userId, $path);
|
||||
if (!$hash) {
|
||||
$failed++;
|
||||
$this->warn("User {$userId}: unable to process legacy avatar ({$legacyName})");
|
||||
continue;
|
||||
}
|
||||
|
||||
$migrated++;
|
||||
$this->line("User {$userId}: migrated ({$hash})");
|
||||
} catch (\Throwable $e) {
|
||||
$failed++;
|
||||
$this->warn("User {$userId}: migration failed ({$e->getMessage()})");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("Migration complete. Processed: {$count}");
|
||||
return 0;
|
||||
$this->info("Avatar migration complete. Migrated={$migrated}, Skipped={$skipped}, Failed={$failed}");
|
||||
|
||||
return $failed > 0 ? self::FAILURE : self::SUCCESS;
|
||||
}
|
||||
|
||||
private function normalizeLegacyName(?string $value): ?string
|
||||
{
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$trimmed = trim($value);
|
||||
if ($trimmed === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return basename(urldecode($trimmed));
|
||||
}
|
||||
|
||||
private function locateLegacyAvatarPath(int $userId, string $legacyName): ?string
|
||||
{
|
||||
$candidates = [
|
||||
public_path('avatar/' . $legacyName),
|
||||
public_path('avatar/' . $userId . '/' . $legacyName),
|
||||
public_path('user-picture/' . $legacyName),
|
||||
storage_path('app/public/avatar/' . $legacyName),
|
||||
storage_path('app/public/avatar/' . $userId . '/' . $legacyName),
|
||||
storage_path('app/public/user-picture/' . $legacyName),
|
||||
base_path('oldSite/www/files/usericons/' . $legacyName),
|
||||
];
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
if (is_string($candidate) && is_file($candidate) && is_readable($candidate)) {
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user