Files
fotospiel-app/app/Jobs/ProcessPhotoSecurityScan.php
2025-12-09 20:29:32 +01:00

96 lines
2.9 KiB
PHP

<?php
namespace App\Jobs;
use App\Models\EventMediaAsset;
use App\Models\Photo;
use App\Services\Security\PhotoSecurityScanner;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class ProcessPhotoSecurityScan implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public function __construct(public int $photoId)
{
$this->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,
]);
}
}
}