Rename to admin:MigrateLocalS3MediaURL and drop --avatars

Rename the command (and test) to admin:MigrateLocalS3MediaURL to reflect its
scope: rewriting stale S3/cloud media URLs only. Remove avatar handling and
the --avatars option; the command now focuses solely on status media
(cdn_url, thumbnail_url, optimized_url).
pull/6929/head
Your Name 4 weeks ago
parent 04536a6e32
commit da9e73dd22

@ -2,27 +2,24 @@
namespace App\Console\Commands;
use App\Models\Avatar;
use App\Models\Media;
use App\Models\Status;
use App\Services\AccountService;
use App\Services\MediaService;
use App\Services\StatusService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class MigrateLocalMediaURL extends Command
class MigrateLocalS3MediaURL extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'admin:MigrateLocalMediaURL
protected $signature = 'admin:MigrateLocalS3MediaURL
{id? : A status id (or post URL) to fix; omit with --all}
{--all : Scan every local media row and fix any with a stale host}
{--avatars : Also rebuild stale avatar cdn_urls (implied by --all)}
{--oldDomain= : Only rewrite URLs whose host matches this old backend (default: rewrite all stale hosts)}
{--newDomain= : Target host to rewrite to (default: the configured cloud disk host from .env)}
{--dry-run : Report what would change without writing}
@ -33,7 +30,7 @@ class MigrateLocalMediaURL extends Command
*
* @var string
*/
protected $description = 'Rebuild stale local media URLs (cdn_url, thumbnail_url, optimized_url, avatars) from their storage paths using the configured cloud disk. Replaces media:cloud-url-rewrite.';
protected $description = 'Rewrite stale local media cloud URLs (cdn_url, thumbnail_url, optimized_url) from their storage paths to the configured S3/cloud host. Replaces media:cloud-url-rewrite.';
/**
* The target host to rewrite URLs to.
@ -91,10 +88,9 @@ class MigrateLocalMediaURL extends Command
$id = $this->argument('id');
$all = $this->option('all');
$avatarsOnly = $this->option('avatars') && ! $all && ! $id;
if (! $id && ! $all && ! $avatarsOnly) {
$this->error('Provide a status id/URL, or pass --all (optionally --avatars).');
if (! $id && ! $all) {
$this->error('Provide a status id/URL, or pass --all.');
return 1;
}
@ -125,12 +121,6 @@ class MigrateLocalMediaURL extends Command
return $this->handleSingle($id);
}
if ($avatarsOnly) {
$this->migrateAvatars();
return 0;
}
return $this->handleAll();
}
@ -201,14 +191,6 @@ class MigrateLocalMediaURL extends Command
protected function handleAll(): int
{
if (! $this->option('dry-run') && ! $this->option('force')) {
if (! $this->confirm('Rebuild stale URLs for all local media rows?', true)) {
$this->comment('Aborted.');
return 0;
}
}
$fixed = 0;
$scanned = 0;
$affectedStatusIds = [];
@ -238,65 +220,15 @@ class MigrateLocalMediaURL extends Command
$this->info('Scanned '.$scanned.' local media rows; '.($this->option('dry-run') ? 'would fix ' : 'fixed ').$fixed.'.');
if ($fixed > 0 && ! $this->option('dry-run')) {
$this->comment('Caches busted for '.count($affectedStatusIds).' affected status(es).');
}
// --all always includes avatars (parity with the old command's
// "Migrate All"); --avatars can also be passed explicitly.
$this->migrateAvatars();
if (! $this->option('dry-run')) {
$this->comment('Tip: run `php artisan cache:clear` if any stale URLs remain cached elsewhere.');
}
return 0;
}
/**
* Rebuild stale avatar cdn_urls from their media_path.
*/
protected function migrateAvatars(): void
{
$this->newLine();
$this->info('Checking avatars...');
$fixed = 0;
$scanned = 0;
Avatar::whereNotNull('cdn_url')->lazyById(1000, 'id')->each(function ($avatar) use (&$fixed, &$scanned) {
$scanned++;
if (! $avatar->cdn_url || ! $avatar->media_path) {
return;
}
if (Str::startsWith((string) $avatar->media_path, 'http')) {
return;
}
$host = parse_url($avatar->cdn_url, PHP_URL_HOST);
if (! $this->shouldRewrite($host)) {
return;
}
$rebuilt = $this->targetUrl($avatar->media_path);
if (! $rebuilt) {
return;
}
$this->line(' avatar '.$avatar->id.' (profile '.$avatar->profile_id.'): '.$host.' -> '.$this->newHost);
if (! $this->option('dry-run')) {
$avatar->cdn_url = $rebuilt;
$avatar->save();
AccountService::del($avatar->profile_id);
}
$fixed++;
});
$this->info('Scanned '.$scanned.' avatars; '.($this->option('dry-run') ? 'would fix ' : 'fixed ').$fixed.'.');
}
/**
* Rebuild any stale URL field on a single media row from its storage path.
* Only writes when a field's host differs from the cloud host.
* Only writes when a field's host differs from the target host.
*
* @return bool whether the row was (or would be) changed
*/

@ -11,7 +11,7 @@ uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| admin:MigrateLocalMediaURL
| admin:MigrateLocalS3MediaURL
|--------------------------------------------------------------------------
|
| Rebuilds stale local media URLs (cdn_url, thumbnail_url, optimized_url)
@ -67,7 +67,7 @@ function makeStatusWithStaleMedia(string $staleHost = 'https://s3.old.example'):
it('rebuilds stale thumbnail_url and optimized_url but leaves correct cdn_url', function () {
$media = makeStatusWithStaleMedia();
$this->artisan('admin:MigrateLocalMediaURL', ['id' => (string) $media->status_id, '--force' => true])
$this->artisan('admin:MigrateLocalS3MediaURL', ['id' => (string) $media->status_id, '--force' => true])
->assertExitCode(0);
$media->refresh();
@ -82,7 +82,7 @@ it('does not change anything in dry-run mode', function () {
$media = makeStatusWithStaleMedia();
$originalThumb = $media->thumbnail_url;
$this->artisan('admin:MigrateLocalMediaURL', ['id' => (string) $media->status_id, '--dry-run' => true])
$this->artisan('admin:MigrateLocalS3MediaURL', ['id' => (string) $media->status_id, '--dry-run' => true])
->assertExitCode(0);
expect($media->fresh()->thumbnail_url)->toBe($originalThumb);
@ -108,7 +108,7 @@ it('leaves already-correct media untouched', function () {
$updatedAt = $media->fresh()->updated_at;
$this->artisan('admin:MigrateLocalMediaURL', ['id' => (string) $status->id, '--force' => true])
$this->artisan('admin:MigrateLocalS3MediaURL', ['id' => (string) $status->id, '--force' => true])
->assertExitCode(0);
expect($media->fresh()->updated_at->eq($updatedAt))->toBeTrue();
@ -131,7 +131,7 @@ it('never rewrites remote media', function () {
'order' => 0,
]);
$this->artisan('admin:MigrateLocalMediaURL', ['id' => (string) $status->id, '--force' => true])
$this->artisan('admin:MigrateLocalS3MediaURL', ['id' => (string) $status->id, '--force' => true])
->assertExitCode(0);
// Unchanged: remote media is skipped.
@ -139,7 +139,7 @@ it('never rewrites remote media', function () {
});
it('requires an id or --all', function () {
$this->artisan('admin:MigrateLocalMediaURL')
$this->artisan('admin:MigrateLocalS3MediaURL')
->assertExitCode(1);
});
@ -147,7 +147,7 @@ it('refuses to run on a local-storage instance', function () {
// Simulate local storage: cloud disabled.
ConfigCacheService::put('pixelfed.cloud_storage', false);
$this->artisan('admin:MigrateLocalMediaURL', ['--all' => true, '--force' => true])
$this->artisan('admin:MigrateLocalS3MediaURL', ['--all' => true, '--force' => true])
->expectsOutputToContain('Cloud storage is not enabled')
->assertExitCode(1);
});
@ -158,7 +158,7 @@ it('with --oldDomain only rewrites URLs on that host', function () {
$media->optimized_url = 'https://other.example/'.$media->media_path;
$media->save();
$this->artisan('admin:MigrateLocalMediaURL', [
$this->artisan('admin:MigrateLocalS3MediaURL', [
'id' => (string) $media->status_id,
'--oldDomain' => 's3.old.example',
'--force' => true,
@ -174,7 +174,7 @@ it('with --oldDomain only rewrites URLs on that host', function () {
it('with --newDomain override rewrites to the given host', function () {
$media = makeStatusWithStaleMedia('https://s3.old.example');
$this->artisan('admin:MigrateLocalMediaURL', [
$this->artisan('admin:MigrateLocalS3MediaURL', [
'id' => (string) $media->status_id,
'--newDomain' => 'media.example',
'--force' => true,
Loading…
Cancel
Save