mirror of
https://github.com/bitinflow/expose.git
synced 2026-03-13 13:35:54 +00:00
Add the ability to log users and their shared subdomains
This commit is contained in:
@@ -6,7 +6,7 @@ use App\Server\Connections\ControlConnection;
|
|||||||
use App\Server\Connections\HttpConnection;
|
use App\Server\Connections\HttpConnection;
|
||||||
use Ratchet\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
interface ConnectionManager
|
interface connectionmanager
|
||||||
{
|
{
|
||||||
public function storeConnection(string $host, ?string $subdomain, ?string $serverHost, ConnectionInterface $connection): ControlConnection;
|
public function storeConnection(string $host, ?string $subdomain, ?string $serverHost, ConnectionInterface $connection): ControlConnection;
|
||||||
|
|
||||||
|
|||||||
14
app/Contracts/LoggerRepository.php
Normal file
14
app/Contracts/LoggerRepository.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Contracts;
|
||||||
|
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
|
||||||
|
interface LoggerRepository
|
||||||
|
{
|
||||||
|
public function logSubdomain($authToken, $subdomain);
|
||||||
|
|
||||||
|
public function getLogs(): PromiseInterface;
|
||||||
|
|
||||||
|
public function getLogsBySubdomain($subdomain): PromiseInterface;
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ use App\Contracts\StatisticsCollector;
|
|||||||
use App\Contracts\SubdomainGenerator;
|
use App\Contracts\SubdomainGenerator;
|
||||||
use App\Http\QueryParameters;
|
use App\Http\QueryParameters;
|
||||||
use App\Server\Exceptions\NoFreePortAvailable;
|
use App\Server\Exceptions\NoFreePortAvailable;
|
||||||
|
use App\Contracts\LoggerRepository;
|
||||||
use Ratchet\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
use React\EventLoop\LoopInterface;
|
use React\EventLoop\LoopInterface;
|
||||||
use React\Socket\Server;
|
use React\Socket\Server;
|
||||||
@@ -28,11 +29,15 @@ class ConnectionManager implements ConnectionManagerContract
|
|||||||
/** @var StatisticsCollector */
|
/** @var StatisticsCollector */
|
||||||
protected $statisticsCollector;
|
protected $statisticsCollector;
|
||||||
|
|
||||||
public function __construct(SubdomainGenerator $subdomainGenerator, StatisticsCollector $statisticsCollector, LoopInterface $loop)
|
/** @var LoggerRepository */
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
public function __construct(SubdomainGenerator $subdomainGenerator, StatisticsCollector $statisticsCollector, LoggerRepository $logger, LoopInterface $loop)
|
||||||
{
|
{
|
||||||
$this->subdomainGenerator = $subdomainGenerator;
|
$this->subdomainGenerator = $subdomainGenerator;
|
||||||
$this->loop = $loop;
|
$this->loop = $loop;
|
||||||
$this->statisticsCollector = $statisticsCollector;
|
$this->statisticsCollector = $statisticsCollector;
|
||||||
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function limitConnectionLength(ControlConnection $connection, int $maximumConnectionLength)
|
public function limitConnectionLength(ControlConnection $connection, int $maximumConnectionLength)
|
||||||
@@ -67,6 +72,8 @@ class ConnectionManager implements ConnectionManagerContract
|
|||||||
|
|
||||||
$this->statisticsCollector->siteShared($this->getAuthTokenFromConnection($connection));
|
$this->statisticsCollector->siteShared($this->getAuthTokenFromConnection($connection));
|
||||||
|
|
||||||
|
$this->logger->logSubdomain($storedConnection->authToken, $storedConnection->subdomain);
|
||||||
|
|
||||||
return $storedConnection;
|
return $storedConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ use App\Server\Http\Controllers\Admin\DeleteSubdomainController;
|
|||||||
use App\Server\Http\Controllers\Admin\DeleteUsersController;
|
use App\Server\Http\Controllers\Admin\DeleteUsersController;
|
||||||
use App\Server\Http\Controllers\Admin\DisconnectSiteController;
|
use App\Server\Http\Controllers\Admin\DisconnectSiteController;
|
||||||
use App\Server\Http\Controllers\Admin\DisconnectTcpConnectionController;
|
use App\Server\Http\Controllers\Admin\DisconnectTcpConnectionController;
|
||||||
|
use App\Server\Http\Controllers\Admin\GetLogsController;
|
||||||
|
use App\Server\Http\Controllers\Admin\GetLogsForSubdomainController;
|
||||||
use App\Server\Http\Controllers\Admin\GetSettingsController;
|
use App\Server\Http\Controllers\Admin\GetSettingsController;
|
||||||
use App\Server\Http\Controllers\Admin\GetSiteDetailsController;
|
use App\Server\Http\Controllers\Admin\GetSiteDetailsController;
|
||||||
use App\Server\Http\Controllers\Admin\GetSitesController;
|
use App\Server\Http\Controllers\Admin\GetSitesController;
|
||||||
@@ -36,6 +38,8 @@ use App\Server\Http\Controllers\Admin\StoreUsersController;
|
|||||||
use App\Server\Http\Controllers\ControlMessageController;
|
use App\Server\Http\Controllers\ControlMessageController;
|
||||||
use App\Server\Http\Controllers\TunnelMessageController;
|
use App\Server\Http\Controllers\TunnelMessageController;
|
||||||
use App\Server\Http\Router;
|
use App\Server\Http\Router;
|
||||||
|
use App\Server\LoggerRepository\NullLogger;
|
||||||
|
use App\Contracts\LoggerRepository;
|
||||||
use App\Server\StatisticsCollector\DatabaseStatisticsCollector;
|
use App\Server\StatisticsCollector\DatabaseStatisticsCollector;
|
||||||
use App\Server\StatisticsRepository\DatabaseStatisticsRepository;
|
use App\Server\StatisticsRepository\DatabaseStatisticsRepository;
|
||||||
use App\Server\SubdomainRepository\DatabaseSubdomainRepository;
|
use App\Server\SubdomainRepository\DatabaseSubdomainRepository;
|
||||||
@@ -144,6 +148,8 @@ class Factory
|
|||||||
$this->router->get('/api/users', GetUsersController::class, $adminCondition);
|
$this->router->get('/api/users', GetUsersController::class, $adminCondition);
|
||||||
$this->router->post('/api/users', StoreUsersController::class, $adminCondition);
|
$this->router->post('/api/users', StoreUsersController::class, $adminCondition);
|
||||||
$this->router->get('/api/users/{id}', GetUserDetailsController::class, $adminCondition);
|
$this->router->get('/api/users/{id}', GetUserDetailsController::class, $adminCondition);
|
||||||
|
$this->router->get('/api/logs', GetLogsController::class, $adminCondition);
|
||||||
|
$this->router->get('/api/logs/{subdomain}', GetLogsForSubdomainController::class, $adminCondition);
|
||||||
$this->router->post('/api/domains', StoreDomainController::class, $adminCondition);
|
$this->router->post('/api/domains', StoreDomainController::class, $adminCondition);
|
||||||
$this->router->delete('/api/domains/{domain}', DeleteSubdomainController::class, $adminCondition);
|
$this->router->delete('/api/domains/{domain}', DeleteSubdomainController::class, $adminCondition);
|
||||||
$this->router->post('/api/subdomains', StoreSubdomainController::class, $adminCondition);
|
$this->router->post('/api/subdomains', StoreSubdomainController::class, $adminCondition);
|
||||||
@@ -190,6 +196,7 @@ class Factory
|
|||||||
$this->bindConfiguration()
|
$this->bindConfiguration()
|
||||||
->bindSubdomainGenerator()
|
->bindSubdomainGenerator()
|
||||||
->bindUserRepository()
|
->bindUserRepository()
|
||||||
|
->bindLoggerRepository()
|
||||||
->bindSubdomainRepository()
|
->bindSubdomainRepository()
|
||||||
->bindDomainRepository()
|
->bindDomainRepository()
|
||||||
->bindDatabase()
|
->bindDatabase()
|
||||||
@@ -238,6 +245,15 @@ class Factory
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function bindLoggerRepository()
|
||||||
|
{
|
||||||
|
app()->singleton(LoggerRepository::class, function () {
|
||||||
|
return app(config('expose.admin.logger_repository', NullLogger::class));
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function bindDomainRepository()
|
protected function bindDomainRepository()
|
||||||
{
|
{
|
||||||
app()->singleton(DomainRepository::class, function () {
|
app()->singleton(DomainRepository::class, function () {
|
||||||
|
|||||||
38
app/Server/Http/Controllers/Admin/GetLogsController.php
Normal file
38
app/Server/Http/Controllers/Admin/GetLogsController.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Server\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Contracts\LoggerRepository;
|
||||||
|
use App\Contracts\UserRepository;
|
||||||
|
use App\Server\Configuration;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class GetLogsController extends AdminController
|
||||||
|
{
|
||||||
|
protected $keepConnectionOpen = true;
|
||||||
|
|
||||||
|
/** @var Configuration */
|
||||||
|
protected $configuration;
|
||||||
|
|
||||||
|
/** @var LoggerRepository */
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
public function __construct(LoggerRepository $logger)
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request, ConnectionInterface $httpConnection)
|
||||||
|
{
|
||||||
|
$subdomain = $request->get('subdomain');
|
||||||
|
$this->logger->getLogs()
|
||||||
|
->then(function ($logs) use ($httpConnection) {
|
||||||
|
$httpConnection->send(
|
||||||
|
respond_json(['logs' => $logs])
|
||||||
|
);
|
||||||
|
|
||||||
|
$httpConnection->close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Server\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Contracts\LoggerRepository;
|
||||||
|
use App\Contracts\UserRepository;
|
||||||
|
use App\Server\Configuration;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class GetLogsForSubdomainController extends AdminController
|
||||||
|
{
|
||||||
|
protected $keepConnectionOpen = true;
|
||||||
|
|
||||||
|
/** @var Configuration */
|
||||||
|
protected $configuration;
|
||||||
|
|
||||||
|
/** @var LoggerRepository */
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
public function __construct(LoggerRepository $logger)
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request, ConnectionInterface $httpConnection)
|
||||||
|
{
|
||||||
|
$subdomain = $request->get('subdomain');
|
||||||
|
$this->logger->getLogsBySubdomain($subdomain)
|
||||||
|
->then(function ($logs) use ($httpConnection) {
|
||||||
|
$httpConnection->send(
|
||||||
|
respond_json(['logs' => $logs])
|
||||||
|
);
|
||||||
|
|
||||||
|
$httpConnection->close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
83
app/Server/LoggerRepository/DatabaseLogger.php
Normal file
83
app/Server/LoggerRepository/DatabaseLogger.php
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Server\LoggerRepository;
|
||||||
|
|
||||||
|
use App\Contracts\UserRepository;
|
||||||
|
use Clue\React\SQLite\DatabaseInterface;
|
||||||
|
use App\Contracts\LoggerRepository;
|
||||||
|
use Clue\React\SQLite\Result;
|
||||||
|
use React\Promise\Deferred;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
|
||||||
|
class DatabaseLogger implements LoggerRepository
|
||||||
|
{
|
||||||
|
/** @var DatabaseInterface */
|
||||||
|
protected $database;
|
||||||
|
|
||||||
|
public function __construct(DatabaseInterface $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logSubdomain($authToken, $subdomain)
|
||||||
|
{
|
||||||
|
app(UserRepository::class)->getUserByToken($authToken)
|
||||||
|
->then(function ($user) use ($subdomain) {
|
||||||
|
$this->database->query("
|
||||||
|
INSERT INTO logs (user_id, subdomain, created_at)
|
||||||
|
VALUES (:user_id, :subdomain, DATETIME('now'))
|
||||||
|
", [
|
||||||
|
'user_id' => $user['id'],
|
||||||
|
'subdomain' => $subdomain,
|
||||||
|
])->then(function () {
|
||||||
|
$this->cleanOldLogs();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cleanOldLogs()
|
||||||
|
{
|
||||||
|
$this->database->query("DELETE FROM logs WHERE created_at < date('now', '-10 day')");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogsBySubdomain($subdomain): PromiseInterface
|
||||||
|
{
|
||||||
|
$deferred = new Deferred();
|
||||||
|
|
||||||
|
$this->database
|
||||||
|
->query("
|
||||||
|
SELECT
|
||||||
|
logs.id AS log_id,
|
||||||
|
logs.subdomain,
|
||||||
|
users.*
|
||||||
|
FROM logs
|
||||||
|
INNER JOIN users
|
||||||
|
ON users.id = logs.user_id
|
||||||
|
WHERE logs.subdomain = :subdomain", ['subdomain' => $subdomain])
|
||||||
|
->then(function (Result $result) use ($deferred) {
|
||||||
|
$deferred->resolve($result->rows);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $deferred->promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogs(): PromiseInterface
|
||||||
|
{
|
||||||
|
$deferred = new Deferred();
|
||||||
|
|
||||||
|
$this->database
|
||||||
|
->query("
|
||||||
|
SELECT
|
||||||
|
logs.id AS log_id,
|
||||||
|
logs.subdomain,
|
||||||
|
users.*
|
||||||
|
FROM logs
|
||||||
|
INNER JOIN users
|
||||||
|
ON users.id = logs.user_id")
|
||||||
|
->then(function (Result $result) use ($deferred) {
|
||||||
|
$deferred->resolve($result->rows);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $deferred->promise();
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/Server/LoggerRepository/NullLogger.php
Normal file
25
app/Server/LoggerRepository/NullLogger.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Server\LoggerRepository;
|
||||||
|
|
||||||
|
use App\Contracts\LoggerRepository;
|
||||||
|
use Clue\React\SQLite\DatabaseInterface;
|
||||||
|
use React\Promise\PromiseInterface;
|
||||||
|
|
||||||
|
class NullLogger implements LoggerRepository
|
||||||
|
{
|
||||||
|
public function logSubdomain($authToken, $subdomain)
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogsBySubdomain($subdomain): PromiseInterface
|
||||||
|
{
|
||||||
|
return \React\Promise\resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogs(): PromiseInterface
|
||||||
|
{
|
||||||
|
return \React\Promise\resolve([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -327,6 +327,8 @@ return [
|
|||||||
|
|
||||||
'subdomain_repository' => \App\Server\SubdomainRepository\DatabaseSubdomainRepository::class,
|
'subdomain_repository' => \App\Server\SubdomainRepository\DatabaseSubdomainRepository::class,
|
||||||
|
|
||||||
|
'logger_repository' => \App\Server\LoggerRepository\NullLogger::class,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Messages
|
| Messages
|
||||||
|
|||||||
6
database/migrations/09_create_logs_table.sql
Normal file
6
database/migrations/09_create_logs_table.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS logs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
subdomain STRING NOT NULL,
|
||||||
|
created_at DATETIME
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user