queue = config('security.queue.name', 'default'); } public function handle(PhotoSecurityScanner $scanner): void { $photo = Photo::with('mediaAsset')->find($this->photoId); if (! $photo) { Log::warning('[PhotoSecurity] Skipping missing photo', ['photo_id' => $this->photoId]); return; } $asset = $photo->mediaAsset ?? EventMediaAsset::where('photo_id', $photo->id)->where('variant', 'original')->first(); if (! $asset) { Log::warning('[PhotoSecurity] No media asset available for scan', [ 'photo_id' => $photo->id, ]); $photo->forceFill([ 'security_scan_status' => 'error', 'security_scan_message' => 'Media asset not available for scanning.', 'security_scanned_at' => now(), ])->save(); return; } $disk = $asset->disk ?? config('filesystems.default', 'public'); $path = $asset->path ?? $photo->file_path; $scanResult = $scanner->scan($disk, $path); $status = $scanResult['status'] ?? 'error'; $message = $scanResult['message'] ?? null; $metadata = [ 'scan' => $scanResult, ]; if ($status === 'clean' && config('security.exif.strip', true)) { $stripResult = $scanner->stripExif($disk, $path); $metadata['exif'] = $stripResult; } $existingMeta = $photo->security_meta ?? []; $update = [ 'security_scan_status' => $status, 'security_scan_message' => $message, 'security_scanned_at' => now(), 'security_meta' => array_merge(is_array($existingMeta) ? $existingMeta : [], $metadata), ]; if (in_array($status, ['clean', 'skipped'], true) && $photo->status === 'pending') { $update['status'] = 'approved'; } if ($status === 'infected') { $update['status'] = 'rejected'; } $photo->forceFill($update)->save(); if ($status === 'infected') { Log::alert('[PhotoSecurity] Infected photo detected', [ 'photo_id' => $photo->id, 'event_id' => $photo->event_id, 'message' => $message, ]); } } }