Add guest policy settings
This commit is contained in:
@@ -12,6 +12,7 @@ use App\Models\Event;
|
||||
use App\Models\EventJoinToken;
|
||||
use App\Models\EventMediaAsset;
|
||||
use App\Models\GuestNotification;
|
||||
use App\Models\GuestPolicySetting;
|
||||
use App\Models\Photo;
|
||||
use App\Models\PhotoShareLink;
|
||||
use App\Services\Analytics\JoinTokenAnalyticsRecorder;
|
||||
@@ -47,6 +48,8 @@ class EventPublicController extends BaseController
|
||||
|
||||
private const BRANDING_SIGNED_TTL_SECONDS = 3600;
|
||||
|
||||
private ?GuestPolicySetting $guestPolicy = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly EventJoinTokenService $joinTokenService,
|
||||
private readonly EventStorageManager $eventStorageManager,
|
||||
@@ -283,8 +286,9 @@ class EventPublicController extends BaseController
|
||||
?string $rawToken = null,
|
||||
?EventJoinToken $joinToken = null
|
||||
): JsonResponse {
|
||||
$failureLimit = max(1, (int) config('join_tokens.failure_limit', 10));
|
||||
$failureDecay = max(1, (int) config('join_tokens.failure_decay_minutes', 5));
|
||||
$policy = $this->guestPolicy();
|
||||
$failureLimit = max(1, (int) ($policy->join_token_failure_limit ?? config('join_tokens.failure_limit', 10)));
|
||||
$failureDecay = max(1, (int) ($policy->join_token_failure_decay_minutes ?? config('join_tokens.failure_decay_minutes', 5)));
|
||||
|
||||
if (RateLimiter::tooManyAttempts($rateLimiterKey, $failureLimit)) {
|
||||
Log::warning('Join token rate limit exceeded', array_merge([
|
||||
@@ -369,12 +373,13 @@ class EventPublicController extends BaseController
|
||||
|
||||
private function enforceAccessThrottle(EventJoinToken $joinToken, Request $request, string $rawToken): ?JsonResponse
|
||||
{
|
||||
$limit = (int) config('join_tokens.access_limit', 0);
|
||||
$policy = $this->guestPolicy();
|
||||
$limit = (int) ($policy->join_token_access_limit ?? config('join_tokens.access_limit', 0));
|
||||
if ($limit <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decay = max(1, (int) config('join_tokens.access_decay_minutes', 1));
|
||||
$decay = max(1, (int) ($policy->join_token_access_decay_minutes ?? config('join_tokens.access_decay_minutes', 1)));
|
||||
$key = sprintf('event:token:access:%s:%s', $joinToken->getKey(), $request->ip());
|
||||
|
||||
if (RateLimiter::tooManyAttempts($key, $limit)) {
|
||||
@@ -409,12 +414,13 @@ class EventPublicController extends BaseController
|
||||
|
||||
private function enforceDownloadThrottle(EventJoinToken $joinToken, Request $request, string $rawToken): ?JsonResponse
|
||||
{
|
||||
$limit = (int) config('join_tokens.download_limit', 0);
|
||||
$policy = $this->guestPolicy();
|
||||
$limit = (int) ($policy->join_token_download_limit ?? config('join_tokens.download_limit', 0));
|
||||
if ($limit <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decay = max(1, (int) config('join_tokens.download_decay_minutes', 1));
|
||||
$decay = max(1, (int) ($policy->join_token_download_decay_minutes ?? config('join_tokens.download_decay_minutes', 1)));
|
||||
$key = sprintf('event:token:download:%s:%s', $joinToken->getKey(), $request->ip());
|
||||
|
||||
if (RateLimiter::tooManyAttempts($key, $limit)) {
|
||||
@@ -1431,6 +1437,7 @@ class EventPublicController extends BaseController
|
||||
$branding = $this->buildGalleryBranding($event);
|
||||
$expiresAt = optional($event->eventPackage)->gallery_expires_at;
|
||||
$settings = is_array($event->settings) ? $event->settings : [];
|
||||
$policy = $this->guestPolicy();
|
||||
|
||||
return response()->json([
|
||||
'event' => [
|
||||
@@ -1439,8 +1446,8 @@ class EventPublicController extends BaseController
|
||||
'slug' => $event->slug,
|
||||
'description' => $this->translateLocalized($event->description, $locale, ''),
|
||||
'gallery_expires_at' => $expiresAt?->toIso8601String(),
|
||||
'guest_downloads_enabled' => (bool) ($settings['guest_downloads_enabled'] ?? true),
|
||||
'guest_sharing_enabled' => (bool) ($settings['guest_sharing_enabled'] ?? true),
|
||||
'guest_downloads_enabled' => (bool) ($settings['guest_downloads_enabled'] ?? $policy->guest_downloads_enabled),
|
||||
'guest_sharing_enabled' => (bool) ($settings['guest_sharing_enabled'] ?? $policy->guest_sharing_enabled),
|
||||
],
|
||||
'branding' => $branding,
|
||||
]);
|
||||
@@ -1578,7 +1585,8 @@ class EventPublicController extends BaseController
|
||||
}
|
||||
|
||||
$deviceId = trim((string) ($request->header('X-Device-Id') ?? $request->input('device_id', '')));
|
||||
$ttlHours = max(1, (int) config('share-links.ttl_hours', 48));
|
||||
$policy = $this->guestPolicy();
|
||||
$ttlHours = max(1, (int) ($policy->share_link_ttl_hours ?? config('share-links.ttl_hours', 48)));
|
||||
|
||||
$existing = PhotoShareLink::query()
|
||||
->where('photo_id', $photo->id)
|
||||
@@ -1892,6 +1900,7 @@ class EventPublicController extends BaseController
|
||||
$settings = $this->normalizeSettings($event->settings ?? []);
|
||||
$engagementMode = $settings['engagement_mode'] ?? 'tasks';
|
||||
$event->loadMissing('photoboothSetting');
|
||||
$policy = $this->guestPolicy();
|
||||
|
||||
if ($joinToken) {
|
||||
$this->joinTokenService->incrementUsage($joinToken);
|
||||
@@ -1911,7 +1920,7 @@ class EventPublicController extends BaseController
|
||||
'demo_read_only' => $demoReadOnly,
|
||||
'photobooth_enabled' => (bool) ($event->photoboothSetting?->enabled),
|
||||
'branding' => $branding,
|
||||
'guest_upload_visibility' => Arr::get($event->settings ?? [], 'guest_upload_visibility', 'review'),
|
||||
'guest_upload_visibility' => Arr::get($event->settings ?? [], 'guest_upload_visibility', $policy->guest_upload_visibility),
|
||||
'engagement_mode' => $engagementMode,
|
||||
])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
@@ -2427,10 +2436,13 @@ class EventPublicController extends BaseController
|
||||
];
|
||||
}
|
||||
|
||||
private function notifyDeviceUploadLimit(Event $event, string $guestIdentifier, string $token): void
|
||||
private function notifyDeviceUploadLimit(Event $event, string $guestIdentifier, string $token, int $limit): void
|
||||
{
|
||||
$title = 'Upload-Limit erreicht';
|
||||
$body = 'Du hast bereits 50 Fotos hochgeladen. Wir speichern deine Warteschlange – bitte lösche zuerst alte Uploads.';
|
||||
$body = sprintf(
|
||||
'Du hast bereits %d Fotos hochgeladen. Wir speichern deine Warteschlange – bitte lösche zuerst alte Uploads.',
|
||||
$limit
|
||||
);
|
||||
|
||||
$this->guestNotificationService->createNotification(
|
||||
$event,
|
||||
@@ -2473,6 +2485,11 @@ class EventPublicController extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
private function guestPolicy(): GuestPolicySetting
|
||||
{
|
||||
return $this->guestPolicy ??= GuestPolicySetting::current();
|
||||
}
|
||||
|
||||
private function eventHasActiveNotification(Event $event, GuestNotificationType $type, string $title): bool
|
||||
{
|
||||
return GuestNotification::query()
|
||||
@@ -2895,7 +2912,8 @@ class EventPublicController extends BaseController
|
||||
'eventPackages.package',
|
||||
'storageAssignments.storageTarget',
|
||||
])->findOrFail($eventId);
|
||||
$uploadVisibility = Arr::get($eventModel->settings ?? [], 'guest_upload_visibility', 'review');
|
||||
$policy = $this->guestPolicy();
|
||||
$uploadVisibility = Arr::get($eventModel->settings ?? [], 'guest_upload_visibility', $policy->guest_upload_visibility);
|
||||
$autoApproveUploads = $uploadVisibility === 'immediate';
|
||||
|
||||
$tenantModel = $eventModel->tenant;
|
||||
@@ -2930,10 +2948,10 @@ class EventPublicController extends BaseController
|
||||
|
||||
$deviceId = $this->resolveDeviceIdentifier($request);
|
||||
|
||||
// Per-device cap per event (MVP: 50)
|
||||
$deviceLimit = max(0, (int) ($policy->per_device_upload_limit ?? 50));
|
||||
$deviceCount = DB::table('photos')->where('event_id', $eventId)->where('guest_name', $deviceId)->count();
|
||||
if ($deviceCount >= 50) {
|
||||
$this->notifyDeviceUploadLimit($eventModel, $deviceId, $token);
|
||||
if ($deviceLimit > 0 && $deviceCount >= $deviceLimit) {
|
||||
$this->notifyDeviceUploadLimit($eventModel, $deviceId, $token, $deviceLimit);
|
||||
|
||||
$this->recordTokenEvent(
|
||||
$joinToken,
|
||||
@@ -2957,7 +2975,7 @@ class EventPublicController extends BaseController
|
||||
'scope' => 'photos',
|
||||
'event_id' => $eventId,
|
||||
'device_id' => $deviceId,
|
||||
'limit' => 50,
|
||||
'limit' => $deviceLimit,
|
||||
'used' => $deviceCount,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Http\Requests\Tenant\BroadcastGuestNotificationRequest;
|
||||
use App\Http\Resources\Tenant\GuestNotificationResource;
|
||||
use App\Models\Event;
|
||||
use App\Models\GuestNotification;
|
||||
use App\Models\GuestPolicySetting;
|
||||
use App\Services\GuestNotificationService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -55,6 +56,11 @@ class EventGuestNotificationController extends Controller
|
||||
$expiresAt = null;
|
||||
if (! empty($data['expires_in_minutes'])) {
|
||||
$expiresAt = now()->addMinutes((int) $data['expires_in_minutes']);
|
||||
} else {
|
||||
$policyTtl = GuestPolicySetting::current()->guest_notification_ttl_hours;
|
||||
if ($policyTtl !== null && $policyTtl > 0) {
|
||||
$expiresAt = now()->addHours((int) $policyTtl);
|
||||
}
|
||||
}
|
||||
|
||||
$payload = null;
|
||||
|
||||
Reference in New Issue
Block a user