Fix up database creation and handling code for servers; ref #2447
This commit is contained in:
parent
a4d7170fac
commit
8697185900
10 changed files with 513 additions and 91 deletions
|
@ -3,18 +3,29 @@
|
|||
namespace Pterodactyl\Services\Databases;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Database;
|
||||
use Pterodactyl\Helpers\Utilities;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Extensions\DynamicDatabaseConnection;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
|
||||
use Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException;
|
||||
use Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException;
|
||||
use Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException;
|
||||
|
||||
class DatabaseManagementService
|
||||
{
|
||||
/**
|
||||
* The regex used to validate that the database name passed through to the function is
|
||||
* in the expected format.
|
||||
*
|
||||
* @see \Pterodactyl\Services\Databases\DatabaseManagementService::generateUniqueDatabaseName()
|
||||
*/
|
||||
private const MATCH_NAME_REGEX = '/^(s[\d]+_)(.*)$/';
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
*/
|
||||
|
@ -31,7 +42,7 @@ class DatabaseManagementService
|
|||
private $encrypter;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
|
||||
* @var \Pterodactyl\Repositories\Eloquent\DatabaseRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
|
@ -50,13 +61,13 @@ class DatabaseManagementService
|
|||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\DatabaseRepository $repository
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
DynamicDatabaseConnection $dynamic,
|
||||
DatabaseRepositoryInterface $repository,
|
||||
DatabaseRepository $repository,
|
||||
Encrypter $encrypter
|
||||
) {
|
||||
$this->connection = $connection;
|
||||
|
@ -65,6 +76,21 @@ class DatabaseManagementService
|
|||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique database name for the given server. This name should be passed through when
|
||||
* calling this handle function for this service, otherwise the database will be created with
|
||||
* whatever name is provided.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $serverId
|
||||
* @return string
|
||||
*/
|
||||
public static function generateUniqueDatabaseName(string $name, int $serverId): string
|
||||
{
|
||||
// Max of 48 characters, including the s123_ that we append to the front.
|
||||
return sprintf('s%d_%s', $serverId, substr($name, 0, 48 - strlen("s{$serverId}_")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set wether or not this class should validate that the server has enough slots
|
||||
* left before creating the new database.
|
||||
|
@ -104,12 +130,15 @@ class DatabaseManagementService
|
|||
}
|
||||
}
|
||||
|
||||
// Max of 48 characters, including the s123_ that we append to the front.
|
||||
$truncatedName = substr($data['database'], 0, 48 - strlen("s{$server->id}_"));
|
||||
// Protect against developer mistakes...
|
||||
if (empty($data['database']) || ! preg_match(self::MATCH_NAME_REGEX, $data['database'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'The database name passed to DatabaseManagementService::handle MUST be prefixed with "s{server_id}_".'
|
||||
);
|
||||
}
|
||||
|
||||
$data = array_merge($data, [
|
||||
'server_id' => $server->id,
|
||||
'database' => $truncatedName,
|
||||
'username' => sprintf('u%d_%s', $server->id, str_random(10)),
|
||||
'password' => $this->encrypter->encrypt(
|
||||
Utilities::randomStringWithSpecialCharacters(24)
|
||||
|
@ -120,7 +149,8 @@ class DatabaseManagementService
|
|||
|
||||
try {
|
||||
return $this->connection->transaction(function () use ($data, &$database) {
|
||||
$database = $this->repository->createIfNotExists($data);
|
||||
$database = $this->createModel($data);
|
||||
|
||||
$this->dynamic->set('dynamic', $data['database_host_id']);
|
||||
|
||||
$this->repository->createDatabase($database->database);
|
||||
|
@ -139,7 +169,7 @@ class DatabaseManagementService
|
|||
$this->repository->dropUser($database->username, $database->remote);
|
||||
$this->repository->flush();
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
} catch (Exception $deletionException) {
|
||||
// Do nothing here. We've already encountered an issue before this point so no
|
||||
// reason to prioritize this error over the initial one.
|
||||
}
|
||||
|
@ -166,4 +196,33 @@ class DatabaseManagementService
|
|||
|
||||
return $database->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the database if there is not an identical match in the DB. While you can technically
|
||||
* have the same name across multiple hosts, for the sake of keeping this logic easy to understand
|
||||
* and avoiding user confusion we will ignore the specific host and just look across all hosts.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Database
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function createModel(array $data): Database
|
||||
{
|
||||
$exists = Database::query()->where('server_id', $data['server_id'])
|
||||
->where('database', $data['database'])
|
||||
->exists();
|
||||
|
||||
if ($exists) {
|
||||
throw new DuplicateDatabaseNameException(
|
||||
'A database with that name already exists for this server.'
|
||||
);
|
||||
}
|
||||
|
||||
$database = (new Database)->forceFill($data);
|
||||
$database->saveOrFail();
|
||||
|
||||
return $database;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue