Route::middleware([ 'edge.token', VerifyAllowedDomain::class, EnsureIpNotBlocked::class, ])->group(function () { Route::get('visitors/challenge', [VisitorController::class, 'challenge']) ->middleware(['throttle:visitor-challenge', BlockFakeVisitors::class]) ->name('api.visitors.challenge'); Route::post('visitors/register', [VisitorController::class, 'register']) ->middleware(['throttle:visitor-register', BlockFakeVisitors::class]) ->name('api.visitors.register'); // ❌ DEPRECATED: HTTP Heartbeat has been replaced with WebSocket // Route::post('visitors/heartbeat', [VisitorController::class, 'heartbeat']) // ->withoutMiddleware('edge.token') // ->middleware(['throttle:visitor-heartbeat', BlockFakeVisitors::class]) // ->name('api.visitors.heartbeat'); // WebSocket-based Visitor Heartbeat Endpoints (replaces HTTP polling) Route::post('visitors/websocket/connect', [VisitorWebSocketController::class, 'connect']) ->middleware(['throttle:visitor-register', BlockFakeVisitors::class]) ->name('api.visitors.websocket.connect'); Route::post('visitors/websocket/heartbeat', [VisitorWebSocketController::class, 'heartbeat']) ->middleware(['throttle:visitor-heartbeat', BlockFakeVisitors::class]) ->name('api.visitors.websocket.heartbeat'); Route::post('visitors/websocket/disconnect', [VisitorWebSocketController::class, 'disconnect']) ->middleware(['throttle:visitor-heartbeat', BlockFakeVisitors::class]) ->name('api.visitors.websocket.disconnect'); }); });