96 lines
2.9 KiB
PHP
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,
|
|
]);
|
|
}
|
|
}
|
|
}
|