Harden credit flows and add RevenueCat webhook

This commit is contained in:
2025-09-25 14:05:58 +02:00
parent 9248d7a3f5
commit 215d19f07e
18 changed files with 804 additions and 190 deletions

View File

@@ -1,18 +1,24 @@
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\EventPublicController;
use App\Http\Controllers\Api\Tenant\EventController;
use App\Http\Controllers\Api\Tenant\SettingsController;
use App\Http\Controllers\Api\Tenant\TaskController;
use App\Http\Controllers\OAuthController;
use App\Http\Controllers\RevenueCatWebhookController;
use App\Http\Controllers\Tenant\CreditController;
use Illuminate\Support\Facades\Route;
// API routes without CSRF protection for guest PWA (stateless)
Route::prefix('v1')->name('api.v1.')->group(function () {
// OAuth routes (public)
Route::get('/oauth/authorize', [OAuthController::class, 'authorize'])->name('oauth.authorize');
Route::post('/oauth/token', [OAuthController::class, 'token'])->name('oauth.token');
// Guest PWA routes (public, throttled, no tenant middleware)
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/{slug}', [EventPublicController::class, 'event'])->name('events.show');
Route::get('/events/{slug}/stats', [EventPublicController::class, 'stats'])->name('events.stats');
@@ -25,39 +31,39 @@ Route::prefix('v1')->name('api.v1.')->group(function () {
Route::post('/events/{slug}/upload', [EventPublicController::class, 'upload'])->name('events.upload');
});
// Protected tenant API routes (JWT tenants via OAuth guard)
Route::middleware(['tenant.token', 'tenant.isolation'])->prefix('tenant')->group(function () {
Route::middleware(['tenant.token', 'tenant.isolation', 'throttle:tenant-api'])->prefix('tenant')->group(function () {
Route::get('me', [OAuthController::class, 'me'])->name('tenant.me');
// Events CRUD
Route::apiResource('events', \App\Http\Controllers\Api\Tenant\EventController::class)->only(['index', 'show', 'update', 'destroy'])->parameters([
'events' => 'event:slug',
]);
Route::middleware('credit.check')->post('events', [\App\Http\Controllers\Api\Tenant\EventController::class, 'store'])->name('tenant.events.store');
Route::post('events/bulk-status', [\App\Http\Controllers\Api\Tenant\EventController::class, 'bulkUpdateStatus'])->name('tenant.events.bulk-status');
Route::get('events/search', [\App\Http\Controllers\Api\Tenant\EventController::class, 'search'])->name('tenant.events.search');
// Tasks CRUD and operations
Route::apiResource('tasks', \App\Http\Controllers\Api\Tenant\TaskController::class);
Route::post('tasks/{task}/assign-event/{event}', [\App\Http\Controllers\Api\Tenant\TaskController::class, 'assignToEvent'])
Route::apiResource('events', EventController::class)
->only(['index', 'show', 'destroy'])
->parameters(['events' => 'event:slug']);
Route::middleware('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::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}', [\App\Http\Controllers\Api\Tenant\TaskController::class, 'bulkAssignToEvent'])
Route::post('tasks/bulk-assign-event/{event}', [TaskController::class, 'bulkAssignToEvent'])
->name('tenant.tasks.bulk-assign-to-event');
Route::get('tasks/event/{event}', [\App\Http\Controllers\Api\Tenant\TaskController::class, 'forEvent'])
Route::get('tasks/event/{event}', [TaskController::class, 'forEvent'])
->name('tenant.tasks.for-event');
Route::get('tasks/collection/{collection}', [\App\Http\Controllers\Api\Tenant\TaskController::class, 'fromCollection'])
Route::get('tasks/collection/{collection}', [TaskController::class, 'fromCollection'])
->name('tenant.tasks.from-collection');
// Settings routes
Route::prefix('settings')->group(function () {
Route::get('/', [\App\Http\Controllers\Api\Tenant\SettingsController::class, 'index'])
Route::get('/', [SettingsController::class, 'index'])
->name('tenant.settings.index');
Route::post('/', [\App\Http\Controllers\Api\Tenant\SettingsController::class, 'update'])
Route::post('/', [SettingsController::class, 'update'])
->name('tenant.settings.update');
Route::post('/reset', [\App\Http\Controllers\Api\Tenant\SettingsController::class, 'reset'])
Route::post('/reset', [SettingsController::class, 'reset'])
->name('tenant.settings.reset');
Route::post('/validate-domain', [\App\Http\Controllers\Api\Tenant\SettingsController::class, 'validateDomain'])
Route::post('/validate-domain', [SettingsController::class, 'validateDomain'])
->name('tenant.settings.validate-domain');
});
@@ -70,5 +76,3 @@ Route::prefix('v1')->name('api.v1.')->group(function () {
});
});
});