Limit-Status im Upload-Flow anzeigen (Warnbanner + Sperrzustände).
Upload-Fehlercodes auswerten und freundliche Dialoge zeigen.
This commit is contained in:
@@ -22,6 +22,7 @@ use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PhotoController extends Controller
|
||||
{
|
||||
@@ -46,27 +47,16 @@ class PhotoController extends Controller
|
||||
->where('tenant_id', $tenantId)
|
||||
->firstOrFail();
|
||||
|
||||
$event->loadMissing(['tenant', 'eventPackage.package', 'eventPackages.package']);
|
||||
$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;
|
||||
$limitSummary = $eventPackage
|
||||
? $this->packageLimitEvaluator->summarizeEventPackage($eventPackage)
|
||||
: null;
|
||||
|
||||
$query = Photo::where('event_id', $event->id)
|
||||
->with('event')->withCount('likes')
|
||||
@@ -84,7 +74,9 @@ class PhotoController extends Controller
|
||||
$perPage = $request->get('per_page', 20);
|
||||
$photos = $query->paginate($perPage);
|
||||
|
||||
return PhotoResource::collection($photos);
|
||||
return PhotoResource::collection($photos)->additional([
|
||||
'limits' => $limitSummary,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,6 +89,29 @@ class PhotoController extends Controller
|
||||
->where('tenant_id', $tenantId)
|
||||
->firstOrFail();
|
||||
|
||||
$event->loadMissing(['tenant', 'eventPackage.package', 'eventPackages.package']);
|
||||
$tenant = $event->tenant;
|
||||
|
||||
$eventPackage = $tenant
|
||||
? $this->packageLimitEvaluator->resolveEventPackageForPhotoUpload($tenant, $event->id, $event)
|
||||
: null;
|
||||
|
||||
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']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$previousUsedPhotos = $eventPackage?->used_photos ?? 0;
|
||||
|
||||
$validated = $request->validated();
|
||||
$file = $request->file('photo');
|
||||
|
||||
@@ -197,12 +212,17 @@ class PhotoController extends Controller
|
||||
$this->packageUsageTracker->recordPhotoUsage($eventPackage, $previousUsedPhotos, 1);
|
||||
}
|
||||
|
||||
$limitSummary = $eventPackage
|
||||
? $this->packageLimitEvaluator->summarizeEventPackage($eventPackage)
|
||||
: null;
|
||||
|
||||
$photo->load('event')->loadCount('likes');
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Photo uploaded successfully. Awaiting moderation.',
|
||||
'data' => new PhotoResource($photo),
|
||||
'moderation_notice' => 'Your photo has been uploaded and will be reviewed shortly.',
|
||||
'limits' => $limitSummary,
|
||||
], 201);
|
||||
}
|
||||
|
||||
@@ -217,7 +237,13 @@ class PhotoController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
if ($photo->event_id !== $event->id) {
|
||||
return response()->json(['error' => 'Photo not found'], 404);
|
||||
return ApiError::response(
|
||||
'photo_not_found',
|
||||
'Foto nicht gefunden',
|
||||
'Das Foto gehört nicht zu diesem Event.',
|
||||
404,
|
||||
['photo_id' => $photo->id]
|
||||
);
|
||||
}
|
||||
|
||||
$photo->load('event')->loadCount('likes');
|
||||
@@ -239,7 +265,13 @@ class PhotoController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
if ($photo->event_id !== $event->id) {
|
||||
return response()->json(['error' => 'Photo not found'], 404);
|
||||
return ApiError::response(
|
||||
'photo_not_found',
|
||||
'Foto nicht gefunden',
|
||||
'Das Foto gehört nicht zu diesem Event.',
|
||||
404,
|
||||
['photo_id' => $photo->id]
|
||||
);
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
@@ -251,7 +283,13 @@ class PhotoController extends Controller
|
||||
|
||||
// Only tenant admins can moderate
|
||||
if (isset($validated['status']) && ! $this->tokenHasScope($request, 'tenant:write')) {
|
||||
return response()->json(['error' => 'Insufficient scopes'], 403);
|
||||
return ApiError::response(
|
||||
'insufficient_scope',
|
||||
'Insufficient Scopes',
|
||||
'You are not allowed to moderate photos for this event.',
|
||||
Response::HTTP_FORBIDDEN,
|
||||
['required_scope' => 'tenant:write']
|
||||
);
|
||||
}
|
||||
|
||||
$photo->update($validated);
|
||||
@@ -279,7 +317,13 @@ class PhotoController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
if ($photo->event_id !== $event->id) {
|
||||
return response()->json(['error' => 'Photo not found'], 404);
|
||||
return ApiError::response(
|
||||
'photo_not_found',
|
||||
'Foto nicht gefunden',
|
||||
'Das Foto gehört nicht zu diesem Event.',
|
||||
404,
|
||||
['photo_id' => $photo->id]
|
||||
);
|
||||
}
|
||||
|
||||
$assets = EventMediaAsset::where('photo_id', $photo->id)->get();
|
||||
@@ -303,6 +347,9 @@ class PhotoController extends Controller
|
||||
Storage::disk($fallbackDisk)->delete([$photo->path, $photo->thumbnail_path]);
|
||||
}
|
||||
|
||||
$eventPackage = $event->eventPackage;
|
||||
$usageTracker = app(\App\Services\Packages\PackageUsageTracker::class);
|
||||
|
||||
// Delete record and likes
|
||||
DB::transaction(function () use ($photo, $assets) {
|
||||
$photo->likes()->delete();
|
||||
@@ -312,6 +359,15 @@ class PhotoController extends Controller
|
||||
$photo->delete();
|
||||
});
|
||||
|
||||
if ($eventPackage && $eventPackage->package) {
|
||||
$previousUsed = (int) $eventPackage->used_photos;
|
||||
if ($previousUsed > 0) {
|
||||
$eventPackage->decrement('used_photos');
|
||||
$eventPackage->refresh();
|
||||
$usageTracker->recordPhotoUsage($eventPackage, $previousUsed, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Photo deleted successfully',
|
||||
]);
|
||||
@@ -328,7 +384,13 @@ class PhotoController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
if ($photo->event_id !== $event->id) {
|
||||
return response()->json(['error' => 'Photo not found'], 404);
|
||||
return ApiError::response(
|
||||
'photo_not_found',
|
||||
'Photo not found',
|
||||
'The specified photo could not be located for this event.',
|
||||
Response::HTTP_NOT_FOUND,
|
||||
['photo_id' => $photo->id, 'event_id' => $event->id]
|
||||
);
|
||||
}
|
||||
|
||||
$photo->update(['is_featured' => true]);
|
||||
@@ -345,7 +407,13 @@ class PhotoController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
if ($photo->event_id !== $event->id) {
|
||||
return response()->json(['error' => 'Photo not found'], 404);
|
||||
return ApiError::response(
|
||||
'photo_not_found',
|
||||
'Photo not found',
|
||||
'The specified photo could not be located for this event.',
|
||||
Response::HTTP_NOT_FOUND,
|
||||
['photo_id' => $photo->id, 'event_id' => $event->id]
|
||||
);
|
||||
}
|
||||
|
||||
$photo->update(['is_featured' => false]);
|
||||
@@ -569,7 +637,13 @@ class PhotoController extends Controller
|
||||
]);
|
||||
|
||||
if ($request->event_id !== $event->id) {
|
||||
return response()->json(['error' => 'Invalid event ID'], 400);
|
||||
return ApiError::response(
|
||||
'event_mismatch',
|
||||
'Invalid Event',
|
||||
'The provided event does not match the authenticated tenant event.',
|
||||
Response::HTTP_BAD_REQUEST,
|
||||
['payload_event_id' => $request->event_id, 'expected_event_id' => $event->id]
|
||||
);
|
||||
}
|
||||
|
||||
$event->load('storageAssignments.storageTarget');
|
||||
|
||||
Reference in New Issue
Block a user