group(function (): void { Route::post('register', [AuthController::class, 'register'])->middleware('throttle:auth'); Route::post('login', [AuthController::class, 'login'])->middleware('throttle:auth'); Route::post('mobile/register', [AuthController::class, 'mobileRegister'])->middleware('throttle:auth'); Route::post('mobile/login', [AuthController::class, 'mobileLogin'])->middleware('throttle:auth'); Route::post('mobile/social/nonce', [MobileSocialAuthController::class, 'nonce'])->middleware('throttle:auth'); Route::post('mobile/social', [MobileSocialAuthController::class, 'store'])->middleware('throttle:auth'); Route::post('forgot-password', [AuthController::class, 'forgotPassword']) ->middleware('throttle:6,1') ->name('password.email'); Route::post('reset-password', [AuthController::class, 'resetPassword']) ->middleware('throttle:6,1') ->name('password.update'); Route::get('email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail']) ->middleware(['signed:relative', 'throttle:6,1']) ->name('verification.verify'); Route::post('email/verification-notification', [AuthController::class, 'sendVerificationEmail']) ->middleware('throttle:6,1') ->name('verification.send'); Route::middleware('auth:sanctum')->group(function (): void { Route::post('logout', [AuthController::class, 'logout']); Route::get('me', [AuthController::class, 'me']); Route::patch('me', [AuthController::class, 'update'])->middleware(['verified', 'throttle:content-write']); Route::patch('me/password', [AuthController::class, 'updatePassword'])->middleware('throttle:account-action'); Route::delete('me', [AuthController::class, 'destroy'])->middleware('throttle:account-action'); }); }); // Health Route::get('/health', function () { return response()->json(['status' => 'ok']); }); Route::prefix('legal-documents')->group(function (): void { Route::get('/', [LegalDocumentController::class, 'index'])->name('legal-documents.index'); Route::get('{slug}', [LegalDocumentController::class, 'show'])->name('legal-documents.show'); }); Route::middleware(['auth:sanctum', 'verified', 'not_suspended'])->group(function (): void { Route::post('nutrition-onboarding', NutritionOnboardingController::class) ->middleware('throttle:account-action') ->name('nutrition-onboarding.complete'); }); Route::middleware(['auth:sanctum', 'verified', 'not_suspended', 'onboarded'])->group(function (): void { Route::patch('nutrition-profile', NutritionProfileController::class) ->middleware('throttle:account-action') ->name('nutrition-profile.update'); Route::get('nutrition-plans', [NutritionPlanController::class, 'index'])->name('nutrition-plans.index'); Route::get('nutrition-plans/current', [NutritionPlanController::class, 'current'])->name('nutrition-plans.current'); Route::get('weight-entries', [WeightEntryController::class, 'index'])->name('weight-entries.index'); Route::post('weight-entries', [WeightEntryController::class, 'store']) ->middleware('throttle:content-write') ->name('weight-entries.store'); Route::delete('weight-entries/{weightEntry}', [WeightEntryController::class, 'destroy']) ->middleware('throttle:content-write') ->name('weight-entries.destroy'); Route::get('weekly-reviews', [WeeklyReviewController::class, 'index'])->name('weekly-reviews.index'); Route::get('weekly-reviews/current', [WeeklyReviewController::class, 'current'])->name('weekly-reviews.current'); Route::post('weekly-reviews/{weeklyReview}/accept', [WeeklyReviewController::class, 'accept']) ->middleware('throttle:account-action') ->name('weekly-reviews.accept'); }); Route::middleware(['auth:sanctum', 'verified', 'not_suspended', 'onboarded'])->group(function (): void { Route::post('device-tokens', [DeviceTokenController::class, 'store'])->middleware('throttle:device-token'); Route::delete('device-tokens', [DeviceTokenController::class, 'destroy'])->middleware('throttle:device-token'); Route::get('notifications', [NotificationController::class, 'index'])->name('notifications.index'); Route::get('notifications/unread-count', [NotificationController::class, 'unreadCount'])->name('notifications.unread-count'); Route::patch('notifications/read-all', [NotificationController::class, 'markAllAsRead'])->name('notifications.read-all'); Route::patch('notifications/{notification}/read', [NotificationController::class, 'markAsRead'])->name('notifications.read'); Route::get('notification-preferences', [NotificationPreferenceController::class, 'show'])->name('notification-preferences.show'); Route::patch('notification-preferences', [NotificationPreferenceController::class, 'update'])->middleware('throttle:account-action')->name('notification-preferences.update'); }); Route::post('webhooks/revenuecat', [RevenueCatController::class, 'webhook']) ->middleware('throttle:60,1') ->name('revenuecat.webhook'); // Billing Route::middleware(['auth:sanctum', 'verified', 'not_suspended', 'onboarded'])->prefix('billing')->group(function (): void { Route::post('sync', [RevenueCatController::class, 'sync']) ->middleware('throttle:account-action') ->name('billing.sync'); }); Route::middleware(['auth:sanctum', 'verified', 'not_suspended', 'onboarded']) ->prefix('certification') ->group(function (): void { Route::get('/', [CertificationController::class, 'show'])->name('certification.show'); Route::post('identity-document', [CertificationController::class, 'store']) ->middleware('throttle:account-action') ->name('certification.identity.store'); }); // Workouts Route::middleware(['auth:sanctum', 'verified', 'not_suspended', 'onboarded'])->group(function (): void { Route::get('workouts/calendar', [WorkoutSessionController::class, 'calendar'])->name('workouts.calendar'); Route::get('workouts', [WorkoutSessionController::class, 'index'])->name('workouts.index'); Route::post('workouts', [WorkoutSessionController::class, 'store'])->middleware('throttle:content-write')->name('workouts.store'); Route::get('strava/status', [StravaController::class, 'status'])->name('strava.status'); Route::get('strava/authorization-url', [StravaController::class, 'authorizationUrl'])->middleware('throttle:external-service')->name('strava.authorization-url'); Route::post('strava/callback', [StravaController::class, 'callback'])->middleware('throttle:external-service')->name('strava.callback'); Route::delete('strava/connection', [StravaController::class, 'disconnect'])->middleware('throttle:external-service')->name('strava.disconnect'); Route::post('strava/sync', [StravaController::class, 'sync'])->middleware('throttle:external-service')->name('strava.sync'); }); // Meals Route::middleware(['auth:sanctum', 'verified', 'not_suspended', 'onboarded'])->group(function (): void { Route::get('search', SearchController::class)->name('search'); Route::get('users/{user}', [PublicUserProfileController::class, 'show'])->name('users.show'); Route::get('me/blocked-users', [UserBlockController::class, 'index'])->name('users.blocks.index'); Route::post('users/{user}/block', [UserBlockController::class, 'store'])->middleware('throttle:engagement')->name('users.blocks.store'); Route::delete('users/{user}/block', [UserBlockController::class, 'destroy'])->middleware('throttle:engagement')->name('users.blocks.destroy'); Route::post('users/{user}/follow', [FollowController::class, 'follow'])->middleware('throttle:engagement')->name('users.follow'); Route::delete('users/{user}/follow', [FollowController::class, 'unfollow'])->middleware('throttle:engagement')->name('users.unfollow'); Route::post('users/{user}/accept', [FollowController::class, 'accept'])->middleware('throttle:engagement')->name('users.accept'); Route::post('users/{user}/reject', [FollowController::class, 'reject'])->middleware('throttle:engagement')->name('users.reject'); Route::get('users/{user}/followers', [FollowController::class, 'followers'])->name('users.followers'); Route::get('users/{user}/following', [FollowController::class, 'following'])->name('users.following'); Route::post('users/{user}/reports', [ReportController::class, 'storeUser']) ->middleware('throttle:reports') ->name('users.reports.store'); Route::get('me/meal-posts', [MealPostController::class, 'mine'])->name('meal-posts.mine'); Route::get('meal-posts/stats', [MealPostController::class, 'stats']); Route::get('meal-posts/calendar', [MealPostController::class, 'calendar']); Route::post('meal-posts/analyze-image', [MealImageAnalysisController::class, 'store'])->middleware('throttle:ai-analysis'); Route::post('meal-posts/{mealPost}/like', [MealPostController::class, 'like'])->middleware('throttle:engagement')->name('meal-posts.like'); Route::delete('meal-posts/{mealPost}/like', [MealPostController::class, 'unlike'])->middleware('throttle:engagement')->name('meal-posts.unlike'); Route::post('meal-posts/{mealPost}/reports', [ReportController::class, 'storeMealPost']) ->middleware('throttle:reports') ->name('meal-posts.reports.store'); Route::get('meal-posts/{mealPost}/my-review', [PostReviewsController::class, 'mine'])->name('meal-posts.reviews.mine'); Route::get('meal-posts/{mealPost}/reviews', [PostReviewsController::class, 'index'])->name('meal-posts.reviews.index'); Route::post('meal-posts/{mealPost}/reviews', [PostReviewsController::class, 'store'])->middleware('throttle:content-write')->name('meal-posts.reviews.store'); Route::get('post-reviews/{postReview}', [PostReviewsController::class, 'show'])->name('post-reviews.show'); Route::put('post-reviews/{postReview}', [PostReviewsController::class, 'update'])->middleware('throttle:content-write')->name('post-reviews.update'); Route::patch('post-reviews/{postReview}', [PostReviewsController::class, 'update'])->middleware('throttle:content-write')->name('post-reviews.patch'); Route::delete('post-reviews/{postReview}', [PostReviewsController::class, 'destroy'])->middleware('throttle:content-write')->name('post-reviews.destroy'); Route::post('post-reviews/{postReview}/reports', [ReportController::class, 'storePostReview']) ->middleware('throttle:reports') ->name('post-reviews.reports.store'); Route::apiResource('meal-posts', MealPostController::class) ->except(['update']) ->middlewareFor(['store', 'destroy'], 'throttle:content-write'); Route::put('meal-posts/{mealPost}', [MealPostController::class, 'update'])->middleware('throttle:content-write')->name('meal-posts.update'); Route::patch('meal-posts/{mealPost}', [MealPostController::class, 'update'])->middleware('throttle:content-write')->name('meal-posts.patch'); });