Implement package limit notification system
This commit is contained in:
@@ -5,37 +5,69 @@ namespace App\Http\Controllers\Api\Tenant;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Tenant\PhotoStoreRequest;
|
||||
use App\Http\Resources\Tenant\PhotoResource;
|
||||
use App\Models\Event;
|
||||
use App\Models\Photo;
|
||||
use App\Support\ImageHelper;
|
||||
use App\Services\Storage\EventStorageManager;
|
||||
use App\Jobs\ProcessPhotoSecurityScan;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Event;
|
||||
use App\Models\EventMediaAsset;
|
||||
use App\Models\Photo;
|
||||
use App\Services\Packages\PackageLimitEvaluator;
|
||||
use App\Services\Packages\PackageUsageTracker;
|
||||
use App\Services\Storage\EventStorageManager;
|
||||
use App\Support\ApiError;
|
||||
use App\Support\ImageHelper;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Models\EventMediaAsset;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class PhotoController extends Controller
|
||||
{
|
||||
public function __construct(private readonly EventStorageManager $eventStorageManager)
|
||||
{
|
||||
}
|
||||
public function __construct(
|
||||
private readonly EventStorageManager $eventStorageManager,
|
||||
private readonly PackageLimitEvaluator $packageLimitEvaluator,
|
||||
private readonly PackageUsageTracker $packageUsageTracker,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Display a listing of the event's photos.
|
||||
*/
|
||||
public function index(Request $request, string $eventSlug): AnonymousResourceCollection
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
$event = Event::where('slug', $eventSlug)
|
||||
$event = Event::with([
|
||||
'tenant',
|
||||
'eventPackage.package',
|
||||
'eventPackages.package',
|
||||
'storageAssignments.storageTarget',
|
||||
])->where('slug', $eventSlug)
|
||||
->where('tenant_id', $tenantId)
|
||||
->firstOrFail();
|
||||
|
||||
$tenant = $event->tenant;
|
||||
|
||||
if ($tenant) {
|
||||
$violation = $this->packageLimitEvaluator->assessPhotoUpload($tenant, $event->id, $event);
|
||||
|
||||
if ($violation !== null) {
|
||||
return ApiError::response(
|
||||
$violation['code'],
|
||||
$violation['title'],
|
||||
$violation['message'],
|
||||
$violation['status'],
|
||||
$violation['meta']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$eventPackage = $tenant
|
||||
? $this->packageLimitEvaluator->resolveEventPackageForPhotoUpload($tenant, $event->id, $event)
|
||||
: null;
|
||||
|
||||
$previousUsedPhotos = $eventPackage?->used_photos ?? 0;
|
||||
|
||||
$query = Photo::where('event_id', $event->id)
|
||||
->with('event')->withCount('likes')
|
||||
->orderBy('created_at', 'desc');
|
||||
@@ -68,7 +100,7 @@ class PhotoController extends Controller
|
||||
$validated = $request->validated();
|
||||
$file = $request->file('photo');
|
||||
|
||||
if (!$file) {
|
||||
if (! $file) {
|
||||
throw ValidationException::withMessages([
|
||||
'photo' => 'No photo file uploaded.',
|
||||
]);
|
||||
@@ -76,7 +108,7 @@ class PhotoController extends Controller
|
||||
|
||||
// Validate file type and size
|
||||
$allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
|
||||
if (!in_array($file->getMimeType(), $allowedTypes)) {
|
||||
if (! in_array($file->getMimeType(), $allowedTypes)) {
|
||||
throw ValidationException::withMessages([
|
||||
'photo' => 'Only JPEG, PNG, and WebP images are allowed.',
|
||||
]);
|
||||
@@ -89,12 +121,11 @@ class PhotoController extends Controller
|
||||
}
|
||||
|
||||
// Determine storage target
|
||||
$event->load('storageAssignments.storageTarget');
|
||||
$disk = $this->eventStorageManager->getHotDiskForEvent($event);
|
||||
|
||||
// Generate unique filename
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = Str::uuid() . '.' . $extension;
|
||||
$filename = Str::uuid().'.'.$extension;
|
||||
$path = "events/{$eventSlug}/photos/{$filename}";
|
||||
|
||||
// Store original file
|
||||
@@ -160,6 +191,12 @@ class PhotoController extends Controller
|
||||
|
||||
ProcessPhotoSecurityScan::dispatch($photo->id);
|
||||
|
||||
if ($eventPackage) {
|
||||
$eventPackage->increment('used_photos');
|
||||
$eventPackage->refresh();
|
||||
$this->packageUsageTracker->recordPhotoUsage($eventPackage, $previousUsedPhotos, 1);
|
||||
}
|
||||
|
||||
$photo->load('event')->loadCount('likes');
|
||||
|
||||
return response()->json([
|
||||
@@ -283,7 +320,6 @@ class PhotoController extends Controller
|
||||
/**
|
||||
* Bulk approve photos (admin only)
|
||||
*/
|
||||
|
||||
public function feature(Request $request, string $eventSlug, Photo $photo): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
@@ -317,6 +353,7 @@ class PhotoController extends Controller
|
||||
|
||||
return response()->json(['message' => 'Photo removed from featured', 'data' => new PhotoResource($photo)]);
|
||||
}
|
||||
|
||||
public function bulkApprove(Request $request, string $eventSlug): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
@@ -436,7 +473,7 @@ class PhotoController extends Controller
|
||||
$totalPhotos = Photo::where('event_id', $event->id)->count();
|
||||
$pendingPhotos = Photo::where('event_id', $event->id)->where('status', 'pending')->count();
|
||||
$approvedPhotos = Photo::where('event_id', $event->id)->where('status', 'approved')->count();
|
||||
$totalLikes = DB::table('photo_likes')->whereIn('photo_id',
|
||||
$totalLikes = DB::table('photo_likes')->whereIn('photo_id',
|
||||
Photo::where('event_id', $event->id)->pluck('id')
|
||||
)->count();
|
||||
$totalStorage = Photo::where('event_id', $event->id)->sum('size');
|
||||
@@ -492,13 +529,13 @@ class PhotoController extends Controller
|
||||
|
||||
// Generate unique filename
|
||||
$extension = pathinfo($request->filename, PATHINFO_EXTENSION);
|
||||
$filename = Str::uuid() . '.' . $extension;
|
||||
$filename = Str::uuid().'.'.$extension;
|
||||
$path = "events/{$eventSlug}/pending/{$filename}";
|
||||
|
||||
// For local storage, return direct upload endpoint
|
||||
// For S3, use Storage::disk('s3')->temporaryUrl() or presigned URL
|
||||
$uploadUrl = url("/api/v1/tenant/events/{$eventSlug}/upload-direct");
|
||||
|
||||
|
||||
$fields = [
|
||||
'event_id' => $event->id,
|
||||
'filename' => $filename,
|
||||
@@ -605,9 +642,3 @@ class PhotoController extends Controller
|
||||
], 201);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user