33namespace Pterodactyl \Services \Databases ;
44
55use Exception ;
6+ use Pterodactyl \Models \Server ;
67use Pterodactyl \Models \Database ;
78use Pterodactyl \Helpers \Utilities ;
8- use Illuminate \Database \DatabaseManager ;
9+ use Illuminate \Database \ConnectionInterface ;
910use Illuminate \Contracts \Encryption \Encrypter ;
1011use Pterodactyl \Extensions \DynamicDatabaseConnection ;
1112use Pterodactyl \Contracts \Repository \DatabaseRepositoryInterface ;
13+ use Pterodactyl \Exceptions \Service \Database \TooManyDatabasesException ;
14+ use Pterodactyl \Exceptions \Service \Database \DatabaseClientFeatureNotEnabledException ;
1215
1316class DatabaseManagementService
1417{
1518 /**
16- * @var \Illuminate\Database\DatabaseManager
19+ * @var \Illuminate\Database\ConnectionInterface
1720 */
18- private $ database ;
21+ private $ connection ;
1922
2023 /**
2124 * @var \Pterodactyl\Extensions\DynamicDatabaseConnection
@@ -33,84 +36,113 @@ class DatabaseManagementService
3336 private $ repository ;
3437
3538 /**
39+ * Determines if the service should validate the user's ability to create an additional
40+ * database for this server. In almost all cases this should be true, but to keep things
41+ * flexible you can also set it to false and create more databases than the server is
42+ * allocated.
43+ *
3644 * @var bool
3745 */
38- protected $ useRandomHost = false ;
46+ protected $ validateDatabaseLimit = true ;
3947
4048 /**
4149 * CreationService constructor.
4250 *
43- * @param \Illuminate\Database\DatabaseManager $database
51+ * @param \Illuminate\Database\ConnectionInterface $connection
4452 * @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic
4553 * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
4654 * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
4755 */
4856 public function __construct (
49- DatabaseManager $ database ,
57+ ConnectionInterface $ connection ,
5058 DynamicDatabaseConnection $ dynamic ,
5159 DatabaseRepositoryInterface $ repository ,
5260 Encrypter $ encrypter
5361 ) {
54- $ this ->database = $ database ;
62+ $ this ->connection = $ connection ;
5563 $ this ->dynamic = $ dynamic ;
5664 $ this ->encrypter = $ encrypter ;
5765 $ this ->repository = $ repository ;
5866 }
5967
68+ /**
69+ * Set wether or not this class should validate that the server has enough slots
70+ * left before creating the new database.
71+ *
72+ * @param bool $validate
73+ * @return $this
74+ */
75+ public function setValidateDatabaseLimit (bool $ validate ): self
76+ {
77+ $ this ->validateDatabaseLimit = $ validate ;
78+
79+ return $ this ;
80+ }
81+
6082 /**
6183 * Create a new database that is linked to a specific host.
6284 *
63- * @param int $server
85+ * @param \Pterodactyl\Models\Server $server
6486 * @param array $data
6587 * @return \Pterodactyl\Models\Database
6688 *
67- * @throws \Exception
89+ * @throws \Throwable
90+ * @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException
91+ * @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException
6892 */
69- public function create ($ server , array $ data )
93+ public function create (Server $ server , array $ data )
7094 {
71- $ data ['server_id ' ] = $ server ;
72- $ data ['database ' ] = sprintf ('s%d_%s ' , $ server , $ data ['database ' ]);
73- $ data ['username ' ] = sprintf ('u%d_%s ' , $ server , str_random (10 ));
74- $ data ['password ' ] = $ this ->encrypter ->encrypt (
75- Utilities::randomStringWithSpecialCharacters (24 )
76- );
77-
78- $ this ->database ->beginTransaction ();
95+ if (! config ('pterodactyl.client_features.databases.enabled ' )) {
96+ throw new DatabaseClientFeatureNotEnabledException ;
97+ }
98+
99+ if ($ this ->validateDatabaseLimit ) {
100+ // If the server has a limit assigned and we've already reached that limit, throw back
101+ // an exception and kill the process.
102+ if (! is_null ($ server ->database_limit ) && $ server ->databases ()->count () >= $ server ->database_limit ) {
103+ throw new TooManyDatabasesException ;
104+ }
105+ }
106+
107+ $ data = array_merge ($ data , [
108+ 'server_id ' => $ server ->id ,
109+ 'database ' => sprintf ('s%d_%s ' , $ server ->id , $ data ['database ' ]),
110+ 'username ' => sprintf ('u%d_%s ' , $ server ->id , str_random (10 )),
111+ 'password ' => $ this ->encrypter ->encrypt (
112+ Utilities::randomStringWithSpecialCharacters (24 )
113+ ),
114+ ]);
115+
116+ $ database = null ;
117+
79118 try {
80- $ database = $ this ->repository ->createIfNotExists ($ data );
81- $ this ->dynamic ->set ('dynamic ' , $ data ['database_host_id ' ]);
82-
83- $ this ->repository ->createDatabase ($ database ->database );
84- $ this ->repository ->createUser (
85- $ database ->username ,
86- $ database ->remote ,
87- $ this ->encrypter ->decrypt ($ database ->password ),
88- $ database ->max_connections
89- );
90- $ this ->repository ->assignUserToDatabase (
91- $ database ->database ,
92- $ database ->username ,
93- $ database ->remote
94- );
95- $ this ->repository ->flush ();
96-
97- $ this ->database ->commit ();
98- } catch (Exception $ ex ) {
119+ return $ this ->connection ->transaction (function () use ($ data , &$ database ) {
120+ $ database = $ this ->repository ->createIfNotExists ($ data );
121+ $ this ->dynamic ->set ('dynamic ' , $ data ['database_host_id ' ]);
122+
123+ $ this ->repository ->createDatabase ($ database ->database );
124+ $ this ->repository ->createUser (
125+ $ database ->username , $ database ->remote , $ this ->encrypter ->decrypt ($ database ->password ), $ database ->max_connections
126+ );
127+ $ this ->repository ->assignUserToDatabase ($ database ->database , $ database ->username , $ database ->remote );
128+ $ this ->repository ->flush ();
129+
130+ return $ database ;
131+ });
132+ } catch (Exception $ exception ) {
99133 try {
100- if (isset ( $ database ) && $ database instanceof Database) {
134+ if ($ database instanceof Database) {
101135 $ this ->repository ->dropDatabase ($ database ->database );
102136 $ this ->repository ->dropUser ($ database ->username , $ database ->remote );
103137 $ this ->repository ->flush ();
104138 }
105- } catch (Exception $ exTwo ) {
106- // ignore an exception
139+ } catch (Exception $ exception ) {
140+ // Do nothing here. We've already encountered an issue before this point so no
141+ // reason to prioritize this error over the initial one.
107142 }
108143
109- $ this ->database ->rollBack ();
110- throw $ ex ;
144+ throw $ exception ;
111145 }
112-
113- return $ database ;
114146 }
115147
116148 /**
0 commit comments