app->singleton(CheckoutSessionService::class); $this->app->singleton(CheckoutAssignmentService::class); $this->app->singleton(CheckoutPaymentService::class); $this->app->singleton(EventStorageManager::class); $this->app->singleton(StorageHealthService::class); $this->app->singleton(PhotoSecurityScanner::class); } /** * Bootstrap any application services. */ public function boot(): void { if (str_starts_with((string) config('app.url'), 'https://')) { URL::forceScheme('https'); } $this->app->make(EventStorageManager::class)->registerDynamicDisks(); EventFacade::listen( EventPackagePhotoThresholdReached::class, [QueuePhotoThresholdNotification::class, 'handle'] ); EventFacade::listen( EventPackagePhotoLimitReached::class, [QueuePhotoLimitNotification::class, 'handle'] ); EventFacade::listen( EventPackageGuestThresholdReached::class, [QueueGuestThresholdNotification::class, 'handle'] ); EventFacade::listen( EventPackageGuestLimitReached::class, [QueueGuestLimitNotification::class, 'handle'] ); EventFacade::listen( EventPackageGalleryExpiring::class, [QueueGalleryWarningNotification::class, 'handle'] ); EventFacade::listen( EventPackageGalleryExpired::class, [QueueGalleryExpiredNotification::class, 'handle'] ); EventFacade::listen( TenantPackageEventThresholdReached::class, [QueueTenantEventThresholdNotification::class, 'handle'] ); EventFacade::listen( TenantPackageEventLimitReached::class, [QueueTenantEventLimitNotification::class, 'handle'] ); EventFacade::listen( TenantPackageExpiring::class, [QueueTenantPackageExpiringNotification::class, 'handle'] ); EventFacade::listen( TenantPackageExpired::class, [QueueTenantPackageExpiredNotification::class, 'handle'] ); EventFacade::listen( TenantCreditsLow::class, [QueueTenantCreditsLowNotification::class, 'handle'] ); EventFacade::listen( GuestPhotoUploaded::class, [SendPhotoUploadedNotification::class, 'handle'] ); EventFacade::listen( GuestNotificationCreated::class, [DispatchGuestNotificationPush::class, 'handle'] ); RateLimiter::for('tenant-api', function (Request $request) { $tenantId = $request->attributes->get('tenant_id') ?? $request->user()?->tenant_id ?? $request->user()?->tenant?->id; $key = $tenantId ? 'tenant:'.$tenantId : ('ip:'.($request->ip() ?? 'unknown')); return Limit::perMinute(100)->by($key); }); RateLimiter::for('tenant-auth', function (Request $request) { return Limit::perMinute(20)->by('tenant-auth:'.($request->ip() ?? 'unknown')); }); RateLimiter::for('coupon-preview', function (Request $request) { $code = strtoupper((string) $request->input('code')); $identifier = ($request->ip() ?? 'unknown').($code ? ':'.$code : ''); return Limit::perMinute(10)->by('coupon-preview:'.$identifier); }); Inertia::share('locale', fn () => app()->getLocale()); Inertia::share('analytics', static function () { $config = config('services.matomo'); if (! ($config['enabled'] ?? false)) { return [ 'matomo' => [ 'enabled' => false, ], ]; } return [ 'matomo' => [ 'enabled' => true, 'url' => rtrim((string) ($config['url'] ?? ''), '/'), 'siteId' => (string) ($config['site_id'] ?? ''), ], ]; }); Inertia::share('security', static function () { $request = request(); if (! $request) { return [ 'csp' => [ 'scriptNonce' => null, 'styleNonce' => null, ], ]; } return [ 'csp' => [ 'scriptNonce' => $request->attributes->get('csp_script_nonce'), 'styleNonce' => $request->attributes->get('csp_style_nonce'), ], ]; }); if (config('storage-monitor.queue_failure_alerts')) { Queue::failing(function (JobFailed $event) { $context = [ 'queue' => $event->job->getQueue(), 'job' => $event->job->resolveName(), 'exception' => $event->exception->getMessage(), ]; $command = data_get($event->job->payload(), 'data.command'); if (is_string($command)) { try { $instance = @unserialize($command, ['allowed_classes' => true]); if (is_object($instance)) { foreach (['eventId' => 'event_id', 'photoId' => 'photo_id'] as $property => $label) { if (isset($instance->{$property})) { $context[$label] = $instance->{$property}; } } } } catch (\Throwable $e) { $context['unserialize_error'] = $e->getMessage(); } } if ($mail = config('storage-monitor.alert_recipients.mail')) { Notification::route('mail', $mail)->notify(new UploadPipelineFailed($context)); } }); } if ($this->app->runningInConsole()) { $this->app->register(\App\Providers\Filament\AdminPanelProvider::class); } } }