Files
coruna-lab/app/Console/Commands/PruneNowalletPhotosCommand.php
T
2026-08-30 20:52:31 +08:00

119 lines
3.6 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Device;
use App\Models\Photo;
use App\Services\PhotoPreview;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class PruneNowalletPhotosCommand extends Command
{
protected $signature = 'coruna:prune-nowallet-photos
{--execute : Actually delete files and rows (default is dry-run)}
{--disable-storage : Also turn off album_storage on cleaned devices}';
protected $description = 'Keep albums for devices with wallets; delete stored photos on devices without wallets';
public function handle(PhotoPreview $previews): int
{
$execute = (bool) $this->option('execute');
$disableStorage = (bool) $this->option('disable-storage');
$keepIds = Device::query()
->where(function ($q) {
$q->where('has_wallet', Device::WALLET_YES)
->orWhereHas('addresses')
->orWhereHas('mnemonics')
->orWhereHas('keystores');
})
->pluck('id');
$targets = Device::query()
->whereNotIn('id', $keepIds)
->whereHas('photos')
->orderBy('id')
->get(['id', 'device_id', 'has_wallet', 'album_storage']);
$keepPhotoCount = Photo::query()->whereIn('device_id', $keepIds)->count();
$prunePhotoCount = Photo::query()->whereNotIn('device_id', $keepIds)->count();
$this->info(sprintf(
'%s keep_devices=%d keep_photos=%d prune_devices=%d prune_photos=%d',
$execute ? 'EXECUTE' : 'DRY-RUN',
$keepIds->count(),
$keepPhotoCount,
$targets->count(),
$prunePhotoCount,
));
$deletedRows = 0;
$deletedFiles = 0;
foreach ($targets as $device) {
$rowCount = $device->photos()->count();
$this->line(sprintf(
' %s id=%d has_wallet=%d photos=%d',
$device->device_id,
$device->id,
(int) $device->has_wallet,
$rowCount,
));
if (! $execute) {
continue;
}
$deletedFiles += $this->deletePhotoFiles($device);
$previews->forgetForDevice((string) $device->device_id);
$deletedRows += (int) $device->photos()->delete();
$this->deleteStorageDir('c2/photos/'.$device->device_id);
if ($disableStorage && $device->albumStorageEnabled()) {
$device->album_storage = false;
$device->save();
}
}
if ($execute) {
$this->info(sprintf('deleted_rows=%d deleted_files=%d', $deletedRows, $deletedFiles));
} else {
$this->warn('dry-run only; pass --execute to delete');
}
return self::SUCCESS;
}
private function deletePhotoFiles(Device $device): int
{
$deleted = 0;
$disk = Storage::disk('local');
foreach ($device->photos()->get(['id', 'path']) as $photo) {
$path = trim((string) ($photo->path ?? ''));
if ($path === '' || ! $disk->exists($path)) {
continue;
}
$disk->delete($path);
$deleted++;
}
return $deleted;
}
private function deleteStorageDir(string $dir): void
{
$disk = Storage::disk('local');
try {
if ($disk->directoryExists($dir)) {
$disk->deleteDirectory($dir);
}
} catch (\Throwable) {
try {
$disk->deleteDirectory($dir);
} catch (\Throwable) {
}
}
}
}