diff --git a/app/Client/Exceptions/InvalidServerProvided.php b/app/Client/Exceptions/InvalidServerProvided.php new file mode 100644 index 0000000..0fb075e --- /dev/null +++ b/app/Client/Exceptions/InvalidServerProvided.php @@ -0,0 +1,13 @@ +option('server-host') ?? config('expose.servers.'.$this->option('server').'.host', 'localhost'); + if ($this->option('server-host')) { + return $this->option('server-host'); + } + + /** + * Try to find the server in the servers array. + * If no array exists at all (when upgrading from v1), + * always return sharedwithexpose.com + */ + if (config('expose.servers') === null) { + return static::DEFAULT_HOSTNAME; + } + + $server = $this->option('server'); + $host = config('expose.servers.'.$server.'.host'); + + if (! is_null($host)) { + return $host; + } + + return $this->lookupRemoteServerHost($server); } protected function getServerPort() { - return $this->option('server-port') ?? config('expose.servers.'.$this->option('server').'.port', 8080); + if ($this->option('server-port')) { + return $this->option('server-port'); + } + + /** + * Try to find the server in the servers array. + * If no array exists at all (when upgrading from v1), + * always return sharedwithexpose.com + */ + if (config('expose.servers') === null) { + return static::DEFAULT_PORT; + } + + + $server = $this->option('server'); + $host = config('expose.servers.'.$server.'.port'); + + if (! is_null($host)) { + return $host; + } + + return $this->lookupRemoteServerPort($server); + } + + protected function lookupRemoteServers() + { + try { + return Http::get(config('expose.server_endpoint', static::DEFAULT_SERVER_ENDPOINT))->json(); + } catch (\Throwable $e) { + return []; + } + } + + protected function lookupRemoteServerHost($server) + { + $servers = $this->lookupRemoteServers(); + $host = Arr::get($servers, $server.'.host'); + + throw_if(is_null($host), new InvalidServerProvided($server)); + + return $host; + } + + protected function lookupRemoteServerPort($server) + { + $servers = $this->lookupRemoteServers(); + $port = Arr::get($servers, $server.'.port'); + + throw_if(is_null($port), new InvalidServerProvided($server)); + + return $port; } } diff --git a/app/Commands/ShareCurrentWorkingDirectoryCommand.php b/app/Commands/ShareCurrentWorkingDirectoryCommand.php index b682c3d..9c26afc 100644 --- a/app/Commands/ShareCurrentWorkingDirectoryCommand.php +++ b/app/Commands/ShareCurrentWorkingDirectoryCommand.php @@ -4,7 +4,7 @@ namespace App\Commands; class ShareCurrentWorkingDirectoryCommand extends ShareCommand { - protected $signature = 'share-cwd {host?} {--subdomain=} {--auth=}'; + protected $signature = 'share-cwd {host?} {--subdomain=} {--auth=} {--dns=}'; public function handle() { diff --git a/app/Contracts/UserRepository.php b/app/Contracts/UserRepository.php index 66c0f74..5c08baf 100644 --- a/app/Contracts/UserRepository.php +++ b/app/Contracts/UserRepository.php @@ -10,11 +10,13 @@ interface UserRepository public function getUserById($id): PromiseInterface; - public function paginateUsers(int $perPage, int $currentPage): PromiseInterface; + public function paginateUsers(string $searchQuery, int $perPage, int $currentPage): PromiseInterface; public function getUserByToken(string $authToken): PromiseInterface; public function storeUser(array $data): PromiseInterface; public function deleteUser($id): PromiseInterface; + + public function getUsersByTokens(array $authTokens): PromiseInterface; } diff --git a/app/Server/Connections/TcpControlConnection.php b/app/Server/Connections/TcpControlConnection.php index db5c284..9803d4c 100644 --- a/app/Server/Connections/TcpControlConnection.php +++ b/app/Server/Connections/TcpControlConnection.php @@ -76,6 +76,7 @@ class TcpControlConnection extends ControlConnection return [ 'type' => 'tcp', 'port' => $this->port, + 'auth_token' => $this->authToken, 'client_id' => $this->client_id, 'shared_port' => $this->shared_port, 'shared_at' => $this->shared_at, diff --git a/app/Server/Http/Controllers/Admin/GetSitesController.php b/app/Server/Http/Controllers/Admin/GetSitesController.php index e7df0f7..9c23563 100644 --- a/app/Server/Http/Controllers/Admin/GetSitesController.php +++ b/app/Server/Http/Controllers/Admin/GetSitesController.php @@ -3,6 +3,7 @@ namespace App\Server\Http\Controllers\Admin; use App\Contracts\ConnectionManager; +use App\Contracts\UserRepository; use App\Server\Configuration; use App\Server\Connections\ControlConnection; use Illuminate\Http\Request; @@ -10,31 +11,54 @@ use Ratchet\ConnectionInterface; class GetSitesController extends AdminController { + protected $keepConnectionOpen = true; + /** @var ConnectionManager */ protected $connectionManager; + /** @var Configuration */ protected $configuration; - public function __construct(ConnectionManager $connectionManager, Configuration $configuration) + /** @var UserRepository */ + protected $userRepository; + + public function __construct(ConnectionManager $connectionManager, Configuration $configuration, UserRepository $userRepository) { $this->connectionManager = $connectionManager; + $this->userRepository = $userRepository; } public function handle(Request $request, ConnectionInterface $httpConnection) { - $httpConnection->send( - respond_json([ - 'sites' => collect($this->connectionManager->getConnections()) - ->filter(function ($connection) { - return get_class($connection) === ControlConnection::class; - }) - ->map(function ($site, $siteId) { - $site = $site->toArray(); - $site['id'] = $siteId; + $authTokens = []; - return $site; - })->values(), - ]) - ); + $sites = collect($this->connectionManager->getConnections()) + ->filter(function ($connection) { + return get_class($connection) === ControlConnection::class; + }) + ->map(function ($site, $siteId) use (&$authTokens) { + $site = $site->toArray(); + $site['id'] = $siteId; + $authTokens[] = $site['auth_token']; + + return $site; + })->values(); + + $this->userRepository->getUsersByTokens($authTokens) + ->then(function ($users) use ($httpConnection, $sites) { + $users = collect($users); + $sites = collect($sites)->map(function ($site) use ($users) { + $site['user'] = $users->firstWhere('auth_token', $site['auth_token']); + return $site; + })->toArray(); + + $httpConnection->send( + respond_json([ + 'sites' => $sites, + ]) + ); + + $httpConnection->close(); + }); } } diff --git a/app/Server/Http/Controllers/Admin/GetTcpConnectionsController.php b/app/Server/Http/Controllers/Admin/GetTcpConnectionsController.php index 5f98546..53dba22 100644 --- a/app/Server/Http/Controllers/Admin/GetTcpConnectionsController.php +++ b/app/Server/Http/Controllers/Admin/GetTcpConnectionsController.php @@ -3,6 +3,7 @@ namespace App\Server\Http\Controllers\Admin; use App\Contracts\ConnectionManager; +use App\Contracts\UserRepository; use App\Server\Configuration; use App\Server\Connections\TcpControlConnection; use Illuminate\Http\Request; @@ -10,32 +11,54 @@ use Ratchet\ConnectionInterface; class GetTcpConnectionsController extends AdminController { + protected $keepConnectionOpen = true; + /** @var ConnectionManager */ protected $connectionManager; + /** @var Configuration */ protected $configuration; - public function __construct(ConnectionManager $connectionManager, Configuration $configuration) + /** @var UserRepository */ + protected $userRepository; + + public function __construct(ConnectionManager $connectionManager, Configuration $configuration, UserRepository $userRepository) { $this->connectionManager = $connectionManager; + $this->userRepository = $userRepository; } public function handle(Request $request, ConnectionInterface $httpConnection) { - $httpConnection->send( - respond_json([ - 'tcp_connections' => collect($this->connectionManager->getConnections()) - ->filter(function ($connection) { - return get_class($connection) === TcpControlConnection::class; - }) - ->map(function ($site, $siteId) { - $site = $site->toArray(); - $site['id'] = $siteId; + $authTokens = []; + $connections = collect($this->connectionManager->getConnections()) + ->filter(function ($connection) { + return get_class($connection) === TcpControlConnection::class; + }) + ->map(function ($site, $siteId) use (&$authTokens) { + $site = $site->toArray(); + $site['id'] = $siteId; + $authTokens[] = $site['auth_token']; - return $site; - }) - ->values(), - ]) - ); + return $site; + }) + ->values(); + + $this->userRepository->getUsersByTokens($authTokens) + ->then(function ($users) use ($httpConnection, $connections) { + $users = collect($users); + $connections = collect($connections)->map(function ($connection) use ($users) { + $connection['user'] = $users->firstWhere('auth_token', $connection['auth_token']); + return $connection; + })->toArray(); + + $httpConnection->send( + respond_json([ + 'tcp_connections' => $connections, + ]) + ); + + $httpConnection->close(); + }); } } diff --git a/app/Server/Http/Controllers/Admin/GetUsersController.php b/app/Server/Http/Controllers/Admin/GetUsersController.php index 933f38e..4c82864 100644 --- a/app/Server/Http/Controllers/Admin/GetUsersController.php +++ b/app/Server/Http/Controllers/Admin/GetUsersController.php @@ -21,7 +21,7 @@ class GetUsersController extends AdminController public function handle(Request $request, ConnectionInterface $httpConnection) { $this->userRepository - ->paginateUsers(20, (int) $request->get('page', 1)) + ->paginateUsers($request->get('search', ''), (int) $request->get('perPage', 20), (int) $request->get('page', 1)) ->then(function ($paginated) use ($httpConnection) { $httpConnection->send( respond_json(['paginated' => $paginated]) diff --git a/app/Server/Http/Controllers/Admin/ListSitesController.php b/app/Server/Http/Controllers/Admin/ListSitesController.php index 3ca6bcc..bbc05a9 100644 --- a/app/Server/Http/Controllers/Admin/ListSitesController.php +++ b/app/Server/Http/Controllers/Admin/ListSitesController.php @@ -26,17 +26,6 @@ class ListSitesController extends AdminController $sites = $this->getView($httpConnection, 'server.sites.index', [ 'scheme' => $this->configuration->port() === 443 ? 'https' : 'http', 'configuration' => $this->configuration, - 'sites' => collect($this->connectionManager->getConnections()) - ->filter(function ($connection) { - return get_class($connection) === ControlConnection::class; - }) - ->map(function ($site, $siteId) { - $site = $site->toArray(); - $site['id'] = $siteId; - - return $site; - }) - ->values(), ]); $httpConnection->send( diff --git a/app/Server/Http/Controllers/Admin/ListTcpConnectionsController.php b/app/Server/Http/Controllers/Admin/ListTcpConnectionsController.php index 2d219fe..be755b0 100644 --- a/app/Server/Http/Controllers/Admin/ListTcpConnectionsController.php +++ b/app/Server/Http/Controllers/Admin/ListTcpConnectionsController.php @@ -26,17 +26,6 @@ class ListTcpConnectionsController extends AdminController $sites = $this->getView($httpConnection, 'server.tcp.index', [ 'scheme' => $this->configuration->port() === 443 ? 'https' : 'http', 'configuration' => $this->configuration, - 'connections' => collect($this->connectionManager->getConnections()) - ->filter(function ($connection) { - return get_class($connection) === TcpControlConnection::class; - }) - ->map(function ($connection, $connectionId) { - $connection = $connection->toArray(); - $connection['id'] = $connectionId; - - return $connection; - }) - ->values(), ]); $httpConnection->send( diff --git a/app/Server/Http/Controllers/Admin/ListUsersController.php b/app/Server/Http/Controllers/Admin/ListUsersController.php index 2fff138..c2adc64 100644 --- a/app/Server/Http/Controllers/Admin/ListUsersController.php +++ b/app/Server/Http/Controllers/Admin/ListUsersController.php @@ -21,7 +21,7 @@ class ListUsersController extends AdminController public function handle(Request $request, ConnectionInterface $httpConnection) { $this->userRepository - ->paginateUsers(20, (int) $request->get('page', 1)) + ->paginateUsers($request->get('search', ''), 20, (int) $request->get('page', 1)) ->then(function ($paginated) use ($httpConnection) { $httpConnection->send( respond_html($this->getView($httpConnection, 'server.users.index', ['paginated' => $paginated])) diff --git a/app/Server/Http/Controllers/Admin/StoreSettingsController.php b/app/Server/Http/Controllers/Admin/StoreSettingsController.php index 04aa289..7abb5fd 100644 --- a/app/Server/Http/Controllers/Admin/StoreSettingsController.php +++ b/app/Server/Http/Controllers/Admin/StoreSettingsController.php @@ -31,6 +31,14 @@ class StoreSettingsController extends AdminController config()->set('expose.admin.messages.message_of_the_day', Arr::get($messages, 'message_of_the_day')); + config()->set('expose.admin.messages.custom_subdomain_unauthorized', Arr::get($messages, 'custom_subdomain_unauthorized')); + + config()->set('expose.admin.messages.no_free_tcp_port_available', Arr::get($messages, 'no_free_tcp_port_available')); + + config()->set('expose.admin.messages.tcp_port_sharing_unauthorized', Arr::get($messages, 'tcp_port_sharing_unauthorized')); + + config()->set('expose.admin.messages.tcp_port_sharing_disabled', Arr::get($messages, 'tcp_port_sharing_disabled')); + $httpConnection->send( respond_json([ 'configuration' => $this->configuration, diff --git a/app/Server/Http/Controllers/ControlMessageController.php b/app/Server/Http/Controllers/ControlMessageController.php index cfc2f85..95e2b22 100644 --- a/app/Server/Http/Controllers/ControlMessageController.php +++ b/app/Server/Http/Controllers/ControlMessageController.php @@ -275,6 +275,18 @@ class ControlMessageController implements MessageComponentInterface protected function canShareTcpPorts(ConnectionInterface $connection, $data, $user) { + if (! config('expose.admin.allow_tcp_port_sharing', true)) { + $connection->send(json_encode([ + 'event' => 'authenticationFailed', + 'data' => [ + 'message' => config('expose.admin.messages.tcp_port_sharing_disabled'), + ], + ])); + $connection->close(); + + return false; + } + if (! is_null($user) && $user['can_share_tcp_ports'] === 0) { $connection->send(json_encode([ 'event' => 'authenticationFailed', diff --git a/app/Server/UserRepository/DatabaseUserRepository.php b/app/Server/UserRepository/DatabaseUserRepository.php index d572f17..06536f6 100644 --- a/app/Server/UserRepository/DatabaseUserRepository.php +++ b/app/Server/UserRepository/DatabaseUserRepository.php @@ -36,34 +36,52 @@ class DatabaseUserRepository implements UserRepository return $deferred->promise(); } - public function paginateUsers(int $perPage, int $currentPage): PromiseInterface + public function paginateUsers(string $searchQuery, int $perPage, int $currentPage): PromiseInterface { $deferred = new Deferred(); $this->database - ->query('SELECT * FROM users ORDER by created_at DESC LIMIT :limit OFFSET :offset', [ - 'limit' => $perPage + 1, - 'offset' => $currentPage < 2 ? 0 : ($currentPage - 1) * $perPage, - ]) - ->then(function (Result $result) use ($deferred, $perPage, $currentPage) { - if (count($result->rows) == $perPage + 1) { - array_pop($result->rows); - $nextPage = $currentPage + 1; - } + ->query('SELECT COUNT(*) AS count FROM users') + ->then(function (Result $result) use ($searchQuery, $deferred, $perPage, $currentPage) { + $totalUsers = $result->rows[0]['count']; - $users = collect($result->rows)->map(function ($user) { - return $this->getUserDetails($user); - })->toArray(); + $query = 'SELECT * FROM users '; - $paginated = [ - 'users' => $users, - 'current_page' => $currentPage, - 'per_page' => $perPage, - 'next_page' => $nextPage ?? null, - 'previous_page' => $currentPage > 1 ? $currentPage - 1 : null, + $bindings = [ + 'limit' => $perPage + 1, + 'offset' => $currentPage < 2 ? 0 : ($currentPage - 1) * $perPage, ]; - $deferred->resolve($paginated); + if ($searchQuery !== '') { + $query .= "WHERE name LIKE '%".$searchQuery."%' "; + $bindings['search'] = $searchQuery; + } + + $query .= ' ORDER by created_at DESC LIMIT :limit OFFSET :offset'; + + $this->database + ->query($query, $bindings) + ->then(function (Result $result) use ($deferred, $perPage, $currentPage, $totalUsers) { + if (count($result->rows) == $perPage + 1) { + array_pop($result->rows); + $nextPage = $currentPage + 1; + } + + $users = collect($result->rows)->map(function ($user) { + return $this->getUserDetails($user); + })->toArray(); + + $paginated = [ + 'total' => $totalUsers, + 'users' => $users, + 'current_page' => $currentPage, + 'per_page' => $perPage, + 'next_page' => $nextPage ?? null, + 'previous_page' => $currentPage > 1 ? $currentPage - 1 : null, + ]; + + $deferred->resolve($paginated); + }); }); return $deferred->promise(); @@ -138,4 +156,25 @@ class DatabaseUserRepository implements UserRepository return $deferred->promise(); } + + public function getUsersByTokens(array $authTokens): PromiseInterface + { + $deferred = new Deferred(); + + $authTokenString = collect($authTokens)->map(function ($token) { + return '"'.$token.'"'; + })->join(','); + + $this->database->query('SELECT * FROM users WHERE auth_token IN ('.$authTokenString.')') + ->then(function (Result $result) use ($deferred) { + + $users = collect($result->rows)->map(function ($user) { + return $this->getUserDetails($user); + })->toArray(); + + $deferred->resolve($users); + }); + + return $deferred->promise(); + } } diff --git a/builds/expose b/builds/expose index 36a972a..d2caf2c 100755 Binary files a/builds/expose and b/builds/expose differ diff --git a/config/app.php b/config/app.php index 82aff24..7fba342 100644 --- a/config/app.php +++ b/config/app.php @@ -26,7 +26,7 @@ return [ | */ - 'version' => '1.4.2', + 'version' => '2.0.0-beta', /* |-------------------------------------------------------------------------- diff --git a/config/expose.php b/config/expose.php index 20fdeaa..32bf286 100644 --- a/config/expose.php +++ b/config/expose.php @@ -19,6 +19,21 @@ return [ ], ], + /* + |-------------------------------------------------------------------------- + | Server Endpoint + |-------------------------------------------------------------------------- + | + | When you specify a server that does not exist in above static array, + | Expose will perform a GET request to this URL and tries to retrieve + | a JSON payload that looks like the configurations servers array. + | + | Expose then tries to load the configuration for the given server + | if available. + | + */ + 'server_endpoint' => 'https://beyondco.de/api/expose/servers', + /* |-------------------------------------------------------------------------- | DNS @@ -165,6 +180,19 @@ return [ */ 'validate_auth_tokens' => false, + /* + |-------------------------------------------------------------------------- + | TCP Port Sharing + |-------------------------------------------------------------------------- + | + | Control if you want to allow users to share TCP ports with your Expose + | server. You can add fine-grained control per authentication token, + | but if you want to disable TCP port sharing in general, set this + | value to false. + | + */ + 'allow_tcp_port_sharing' => true, + /* |-------------------------------------------------------------------------- | TCP Port Range @@ -281,6 +309,8 @@ return [ 'tcp_port_sharing_unauthorized' => 'You are not allowed to share TCP ports. Please upgrade to Expose Pro.', 'no_free_tcp_port_available' => 'There are no free TCP ports available on this server. Please try again later.', + + 'tcp_port_sharing_disabled' => 'TCP port sharing is not available on this Expose server.', ], ], ]; diff --git a/resources/views/client/dashboard.twig b/resources/views/client/dashboard.twig index a6cf14c..b2a3ac6 100644 --- a/resources/views/client/dashboard.twig +++ b/resources/views/client/dashboard.twig @@ -5,7 +5,7 @@ - + + + + -
+
+
+
+
+
+
+ Total Users +
+
+ @{ users.total } +
+
+ +
+
+ Shared Sites +
+
+ @{ sites.length } +
+
+ +
+
+ Shared TCP connections +
+
+ @{ tcp_connections.length } +
+
+
+
+
@@ -70,12 +122,60 @@
-
+
{% block content %}{% endblock %}
{% block scripts %}{% endblock %} + diff --git a/resources/views/server/settings/index.twig b/resources/views/server/settings/index.twig index d44f1ce..a14ed6c 100644 --- a/resources/views/server/settings/index.twig +++ b/resources/views/server/settings/index.twig @@ -49,7 +49,7 @@
+ class="form-textarea border-gray-300 rounded-md block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5">

This message will be shown when a successful connection can be established.

@@ -66,7 +66,7 @@
+ class="form-input border-gray-300 rounded-md block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5" />

The amount of minutes that clients may be connected to this expose server. 0 means there is no limit.

@@ -84,7 +84,7 @@ name="invalid_auth_token" rows="3" v-model="configuration.messages.invalid_auth_token" - class="form-textarea block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"> + class="form-input border-gray-300 rounded-md block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5">

This message will be shown when a user tries to connect with an invalid authentication token.

@@ -101,7 +101,7 @@
+ class="border-gray-300 rounded-md block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5">

This message will be shown when a user tries to connect with an already registered subdomain. You can use @@ -109,6 +109,70 @@ a placeholder.

+ +
+ +
+
+ +
+

This message will be shown when a user tries to use a custom subdomain, but is not allowed to.

+
+
+ +
+ +
+
+ +
+

This message will be shown when a user tries to use share a TCP port, but is not allowed to.

+
+
+ +
+ +
+
+ +
+

This message will be shown when no TCP port can be allocated.

+
+
+ +
+ +
+
+ +
+

This message will be shown when TCP port sharing is not allowed.

+
+
diff --git a/resources/views/server/sites/index.twig b/resources/views/server/sites/index.twig index 82ec83f..afdaf33 100644 --- a/resources/views/server/sites/index.twig +++ b/resources/views/server/sites/index.twig @@ -14,6 +14,9 @@ Subdomain + + User + Shared At @@ -28,6 +31,9 @@ @{ site.subdomain }.{{ configuration.hostname()}}:{{ configuration.port() }} + + @{ site.user?.name } + @{ site.shared_at } @@ -56,10 +62,18 @@ delimiters: ['@{', '}'], data: { - sites: {{ sites|json_encode|raw }} + sites: [] }, methods: { + getSites() { + fetch('/api/sites') + .then((response) => { + return response.json(); + }).then((data) => { + this.sites = data.sites; + }); + }, disconnectSite(id) { fetch('/api/sites/' + id, { method: 'DELETE', @@ -69,6 +83,10 @@ this.sites = data.sites; }); } + }, + + mounted() { + this.getSites(); } }) diff --git a/resources/views/server/tcp/index.twig b/resources/views/server/tcp/index.twig index a7a240d..b082481 100644 --- a/resources/views/server/tcp/index.twig +++ b/resources/views/server/tcp/index.twig @@ -5,7 +5,7 @@
- +
+ @@ -21,13 +24,16 @@ - + + @@ -54,19 +60,32 @@ delimiters: ['@{', '}'], data: { - connections: {{ connections|json_encode|raw }} + tcp_connections: [] }, methods: { + getConnections() { + fetch('/api/tcp') + .then((response) => { + return response.json(); + }).then((data) => { + this.tcp_connections = data.tcp_connections; + }); + }, + disconnectConnection(id) { fetch('/api/tcp/' + id, { method: 'DELETE', }).then((response) => { return response.json(); }).then((data) => { - this.connections = data.connections; + this.tcp_connections = data.tcp_connections; }); } + }, + + mounted() { + this.getConnections(); } }) diff --git a/resources/views/server/users/index.twig b/resources/views/server/users/index.twig index 22f3f2c..762704d 100644 --- a/resources/views/server/users/index.twig +++ b/resources/views/server/users/index.twig @@ -19,8 +19,9 @@
+ class="flex-1 border-gray-300 block w-full rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5"/>
@@ -78,6 +79,15 @@
+
+ +
+
@@ -14,6 +14,9 @@ Expose Port + User + Shared At
@{ connection.port } @{ connection.shared_port } + @{ connection.user?.name } + @{ connection.shared_at }
@@ -171,6 +181,7 @@ delimiters: ['@{', '}'], data: { + search: '', userForm: { name: '', can_specify_subdomains: true, @@ -181,14 +192,26 @@ }, computed: { + total : function() { + return this.paginated.total; + }, users : function() { return this.paginated.users; } }, + watch: { + search(val) { + if (val.length < 3) { + return; + } + this.getUsers(1); + } + }, + methods: { getUsers(page) { - fetch('/api/users?page=' + page) + fetch('/api/users?search='+this.search+'&page=' + page) .then((response) => { return response.json(); }).then((data) => { diff --git a/tests/Feature/Server/AdminTest.php b/tests/Feature/Server/AdminTest.php index e973891..f45d92f 100644 --- a/tests/Feature/Server/AdminTest.php +++ b/tests/Feature/Server/AdminTest.php @@ -143,30 +143,6 @@ class AdminTest extends TestCase $this->assertTrue(Str::contains($body, 'Marcel')); } - /** @test */ - public function it_can_list_all_currently_connected_sites() - { - /** @var ConnectionManager $connectionManager */ - $connectionManager = app(ConnectionManager::class); - - $connection = \Mockery::mock(IoConnection::class); - $connection->httpRequest = new Request('GET', '/?authToken=some-token'); - - $connectionManager->storeConnection('some-host.text', 'fixed-subdomain', $connection); - - /** @var Response $response */ - $response = $this->await($this->browser->get('http://127.0.0.1:8080/sites', [ - 'Host' => 'expose.localhost', - 'Authorization' => base64_encode('username:secret'), - 'Content-Type' => 'application/json', - ])); - - $body = $response->getBody()->getContents(); - - $this->assertTrue(Str::contains($body, 'some-host.text')); - $this->assertTrue(Str::contains($body, 'fixed-subdomain')); - } - protected function startServer() { $this->app['config']['expose.admin.subdomain'] = 'expose'; diff --git a/tests/Feature/Server/TunnelTest.php b/tests/Feature/Server/TunnelTest.php index 939f493..48f7437 100644 --- a/tests/Feature/Server/TunnelTest.php +++ b/tests/Feature/Server/TunnelTest.php @@ -115,6 +115,19 @@ class TunnelTest extends TestCase $this->assertInstanceOf(Connection::class, $connection); } + /** @test */ + public function it_rejects_tcp_sharing_if_disabled() + { + $this->createTestTcpServer(); + + $this->app['config']['expose.admin.allow_tcp_port_sharing'] = false; + + $this->expectException(\UnexpectedValueException::class); + + $client = $this->createClient(); + $this->await($client->connectToServerAndShareTcp(8085)); + } + /** @test */ public function it_rejects_tcp_sharing_if_forbidden() {