loop = LoopFactory::create(); $this->router = new RouteGenerator(); } public function setHost(string $host) { $this->host = $host; return $this; } public function setPort(int $port) { $this->port = $port; return $this; } public function setLoop(LoopInterface $loop) { $this->loop = $loop; return $this; } public function setHostname(string $hostname) { $this->hostname = $hostname; return $this; } protected function addExposeRoutes() { $this->router->get('/expose/control', ControlMessageController::class, 'request.headers.get("x-expose-control") matches "/enabled/i"'); $this->router->addSymfonyRoute('tunnel', new Route('/{__catchall__}', [ '_controller' => app(TunnelMessageController::class), ], [ '__catchall__' => '.*' ])); } protected function addAdminRoutes() { $adminCondition = 'request.headers.get("Host") matches "/'.config('expose.dashboard_subdomain').'./i"'; $this->router->get('/', LoginController::class, $adminCondition); $this->router->post('/', VerifyLoginController::class, $adminCondition); $this->router->get('/users', ListUsersController::class, $adminCondition); $this->router->post('/users', StoreUsersController::class, $adminCondition); $this->router->delete('/users/delete/{id}', DeleteUsersController::class, $adminCondition); $this->router->get('/sites', ListSitesController::class, $adminCondition); } protected function bindConfiguration() { app()->singleton(Configuration::class, function ($app) { return new Configuration($this->hostname, $this->port); }); } protected function bindSubdomainGenerator() { app()->singleton(SubdomainGenerator::class, function ($app) { return $app->make(RandomSubdomainGenerator::class); }); } protected function bindConnectionManager() { app()->singleton(ConnectionManagerContract::class, function ($app) { return $app->make(ConnectionManager::class); }); } public function createServer() { $socket = new Server("{$this->host}:{$this->port}", $this->loop); $this->bindConfiguration(); $this->bindSubdomainGenerator(); $this->bindDatabase(); $this->ensureDatabaseIsInitialized(); $this->bindConnectionManager(); $this->addAdminRoutes(); $this->addExposeRoutes(); $urlMatcher = new UrlMatcher($this->router->getRoutes(), new RequestContext); $router = new Router($urlMatcher); $http = new HttpServer($router); return new IoServer($http, $socket, $this->loop); } protected function bindDatabase() { app()->singleton(DatabaseInterface::class, function() { $factory = new \Clue\React\SQLite\Factory($this->loop); return $factory->openLazy(base_path('database/expose.db')); }); } protected function ensureDatabaseIsInitialized() { /** @var DatabaseInterface $db */ $db = app(DatabaseInterface::class); $migrations = (new Finder()) ->files() ->ignoreDotFiles(true) ->in(database_path('migrations')) ->name('*.sql'); /** @var SplFileInfo $migration */ foreach ($migrations as $migration) { $db->exec($migration->getContents()); } } public function validateAuthTokens(bool $validate) { config()->set('expose.validate_auth_tokens', $validate); return $this; } }