name('api.v1.')->group(function () { Route::post('/webhooks/revenuecat', [RevenueCatWebhookController::class, 'handle']) ->middleware('throttle:60,1') ->name('webhooks.revenuecat'); Route::middleware('throttle:oauth')->group(function () { Route::get('/oauth/authorize', [OAuthController::class, 'authorize'])->name('oauth.authorize'); Route::post('/oauth/token', [OAuthController::class, 'token'])->name('oauth.token'); }); Route::middleware('throttle:100,1')->group(function () { Route::get('/events/{token}', [EventPublicController::class, 'event'])->name('events.show'); Route::get('/events/{token}/stats', [EventPublicController::class, 'stats'])->name('events.stats'); Route::get('/events/{token}/package', [EventPublicController::class, 'package'])->name('events.package'); Route::get('/events/{token}/achievements', [EventPublicController::class, 'achievements'])->name('events.achievements'); Route::get('/events/{token}/emotions', [EventPublicController::class, 'emotions'])->name('events.emotions'); Route::get('/events/{token}/tasks', [EventPublicController::class, 'tasks'])->name('events.tasks'); Route::get('/events/{token}/photos', [EventPublicController::class, 'photos'])->name('events.photos'); Route::get('/photos/{id}', [EventPublicController::class, 'photo'])->name('photos.show'); Route::post('/photos/{id}/like', [EventPublicController::class, 'like'])->name('photos.like'); Route::post('/events/{token}/upload', [EventPublicController::class, 'upload'])->name('events.upload'); Route::get('/gallery/{token}', [EventPublicController::class, 'gallery'])->name('gallery.show'); Route::get('/gallery/{token}/photos', [EventPublicController::class, 'galleryPhotos'])->name('gallery.photos'); Route::get('/gallery/{token}/photos/{photo}/download', [EventPublicController::class, 'galleryPhotoDownload']) ->whereNumber('photo') ->middleware('signed') ->name('gallery.photos.download'); Route::get('/gallery/{token}/photos/{photo}/{variant}', [EventPublicController::class, 'galleryPhotoAsset']) ->whereNumber('photo') ->where('variant', 'thumbnail|full') ->middleware('signed') ->name('gallery.photos.asset'); }); Route::middleware(['tenant.token', 'tenant.isolation', 'throttle:tenant-api'])->prefix('tenant')->group(function () { Route::get('me', [OAuthController::class, 'me'])->name('tenant.me'); Route::get('dashboard', DashboardController::class)->name('tenant.dashboard'); Route::get('event-types', EventTypeController::class)->name('tenant.event-types.index'); Route::apiResource('events', EventController::class) ->only(['index', 'show', 'destroy']) ->parameters(['events' => 'event:slug']); Route::middleware(['package.check', 'credit.check'])->group(function () { Route::post('events', [EventController::class, 'store'])->name('tenant.events.store'); Route::match(['put', 'patch'], 'events/{event:slug}', [EventController::class, 'update'])->name('tenant.events.update'); }); Route::prefix('events/{event:slug}')->scopeBindings()->group(function () { Route::get('stats', [EventController::class, 'stats'])->name('tenant.events.stats'); Route::post('toggle', [EventController::class, 'toggle'])->name('tenant.events.toggle'); Route::post('invites', [EventController::class, 'createInvite'])->name('tenant.events.invites'); Route::get('toolkit', [EventController::class, 'toolkit'])->name('tenant.events.toolkit'); Route::prefix('join-tokens')->group(function () { Route::get('/', [EventJoinTokenController::class, 'index'])->name('tenant.events.join-tokens.index'); Route::post('/', [EventJoinTokenController::class, 'store'])->name('tenant.events.join-tokens.store'); Route::get('{joinToken}/layouts', [EventJoinTokenLayoutController::class, 'index']) ->whereNumber('joinToken') ->name('tenant.events.join-tokens.layouts.index'); Route::get('{joinToken}/layouts/{layout}.{format}', [EventJoinTokenLayoutController::class, 'download']) ->whereNumber('joinToken') ->where('format', 'pdf|png') ->name('tenant.events.join-tokens.layouts.download'); Route::patch('{joinToken}', [EventJoinTokenController::class, 'update']) ->whereNumber('joinToken') ->name('tenant.events.join-tokens.update'); Route::delete('{joinToken}', [EventJoinTokenController::class, 'destroy']) ->whereNumber('joinToken') ->name('tenant.events.join-tokens.destroy'); }); Route::get('photos', [PhotoController::class, 'index'])->name('tenant.events.photos.index'); Route::post('photos', [PhotoController::class, 'store'])->name('tenant.events.photos.store'); Route::get('photos/{photo}', [PhotoController::class, 'show'])->name('tenant.events.photos.show'); Route::patch('photos/{photo}', [PhotoController::class, 'update'])->name('tenant.events.photos.update'); Route::delete('photos/{photo}', [PhotoController::class, 'destroy'])->name('tenant.events.photos.destroy'); Route::post('photos/{photo}/feature', [PhotoController::class, 'feature'])->name('tenant.events.photos.feature'); Route::post('photos/{photo}/unfeature', [PhotoController::class, 'unfeature'])->name('tenant.events.photos.unfeature'); Route::post('photos/bulk-approve', [PhotoController::class, 'bulkApprove'])->name('tenant.events.photos.bulk-approve'); Route::post('photos/bulk-reject', [PhotoController::class, 'bulkReject'])->name('tenant.events.photos.bulk-reject'); Route::get('photos/moderation', [PhotoController::class, 'forModeration'])->name('tenant.events.photos.for-moderation'); Route::get('photos/stats', [PhotoController::class, 'stats'])->name('tenant.events.photos.stats'); }); Route::post('events/bulk-status', [EventController::class, 'bulkUpdateStatus'])->name('tenant.events.bulk-status'); Route::get('events/search', [EventController::class, 'search'])->name('tenant.events.search'); Route::apiResource('tasks', TaskController::class); Route::post('tasks/{task}/assign-event/{event}', [TaskController::class, 'assignToEvent']) ->name('tenant.tasks.assign-to-event'); Route::post('tasks/bulk-assign-event/{event}', [TaskController::class, 'bulkAssignToEvent']) ->name('tenant.tasks.bulk-assign-to-event'); Route::get('tasks/event/{event}', [TaskController::class, 'forEvent']) ->name('tenant.tasks.for-event'); Route::get('tasks/collection/{collection}', [TaskController::class, 'fromCollection']) ->name('tenant.tasks.from-collection'); Route::get('task-collections', [TaskCollectionController::class, 'index']) ->name('tenant.task-collections.index'); Route::get('task-collections/{collection}', [TaskCollectionController::class, 'show']) ->name('tenant.task-collections.show'); Route::post('task-collections/{collection}/activate', [TaskCollectionController::class, 'activate']) ->name('tenant.task-collections.activate'); Route::get('emotions', [EmotionController::class, 'index'])->name('tenant.emotions.index'); Route::post('emotions', [EmotionController::class, 'store'])->name('tenant.emotions.store'); Route::patch('emotions/{emotion}', [EmotionController::class, 'update'])->name('tenant.emotions.update'); Route::prefix('settings')->group(function () { Route::get('/', [SettingsController::class, 'index']) ->name('tenant.settings.index'); Route::post('/', [SettingsController::class, 'update']) ->name('tenant.settings.update'); Route::post('/reset', [SettingsController::class, 'reset']) ->name('tenant.settings.reset'); Route::post('/validate-domain', [SettingsController::class, 'validateDomain']) ->name('tenant.settings.validate-domain'); Route::get('/notifications', [SettingsController::class, 'notificationPreferences']) ->name('tenant.settings.notifications.index'); Route::post('/notifications', [SettingsController::class, 'updateNotificationPreferences']) ->name('tenant.settings.notifications.update'); }); Route::prefix('credits')->group(function () { Route::get('balance', [CreditController::class, 'balance'])->name('tenant.credits.balance'); Route::get('ledger', [CreditController::class, 'ledger'])->name('tenant.credits.ledger'); Route::get('history', [CreditController::class, 'history'])->name('tenant.credits.history'); Route::post('purchase', [CreditController::class, 'purchase'])->name('tenant.credits.purchase'); Route::post('sync', [CreditController::class, 'sync'])->name('tenant.credits.sync'); }); Route::prefix('packages')->group(function () { Route::get('/', [PackageController::class, 'index'])->name('packages.index'); Route::post('/purchase', [PackageController::class, 'purchase'])->name('packages.purchase'); Route::post('/payment-intent', [PackageController::class, 'createPaymentIntent'])->name('packages.payment-intent'); Route::post('/complete', [PackageController::class, 'completePurchase'])->name('packages.complete'); Route::post('/free', [PackageController::class, 'assignFree'])->name('packages.free'); Route::post('/paddle-checkout', [PackageController::class, 'createPaddleCheckout'])->name('packages.paddle-checkout'); }); Route::prefix('tenant/packages')->group(function () { Route::get('/', [TenantPackageController::class, 'index'])->name('tenant.packages.index'); }); Route::get('tenant/billing/transactions', [TenantBillingController::class, 'transactions']) ->name('tenant.billing.transactions'); Route::post('feedback', [TenantFeedbackController::class, 'store']) ->name('tenant.feedback.store'); }); });