mirror of
https://github.com/bitinflow/expose.git
synced 2026-03-13 13:35:54 +00:00
wip
This commit is contained in:
13
app/Client/Exceptions/InvalidServerProvided.php
Normal file
13
app/Client/Exceptions/InvalidServerProvided.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Client\Exceptions;
|
||||
|
||||
class InvalidServerProvided extends \Exception
|
||||
{
|
||||
public function __construct($server)
|
||||
{
|
||||
$message = "No such server {$server}.";
|
||||
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,20 @@
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Client\Exceptions\InvalidServerProvided;
|
||||
use App\Logger\CliRequestLogger;
|
||||
use Illuminate\Console\Parser;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use LaravelZero\Framework\Commands\Command;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
|
||||
abstract class ServerAwareCommand extends Command
|
||||
{
|
||||
const DEFAULT_HOSTNAME = 'sharedwithexpose.com';
|
||||
const DEFAULT_PORT = 443;
|
||||
const DEFAULT_SERVER_ENDPOINT = 'https://beyondco.de/api/expose/servers';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
@@ -31,11 +38,81 @@ abstract class ServerAwareCommand extends Command
|
||||
|
||||
protected function getServerHost()
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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]))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user