diff --git a/app/Http/Controllers/Api/Application/Databases/DatabaseController.php b/app/Http/Controllers/Api/Application/Databases/DatabaseController.php index 135f31a89..f06e9b05d 100644 --- a/app/Http/Controllers/Api/Application/Databases/DatabaseController.php +++ b/app/Http/Controllers/Api/Application/Databases/DatabaseController.php @@ -12,6 +12,7 @@ use Pterodactyl\Transformers\Api\Application\DatabaseHostTransformer; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Requests\Api\Application\Databases\GetDatabaseRequest; use Pterodactyl\Http\Requests\Api\Application\Databases\GetDatabasesRequest; +use Pterodactyl\Http\Requests\Api\Application\Databases\DatabaseNodesRequest; use Pterodactyl\Http\Requests\Api\Application\Databases\StoreDatabaseRequest; use Pterodactyl\Http\Requests\Api\Application\Databases\UpdateDatabaseRequest; use Pterodactyl\Http\Requests\Api\Application\Databases\DeleteDatabaseRequest; @@ -135,4 +136,48 @@ class DatabaseController extends ApplicationApiController return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); } + + /** + * ? + * + * @param \Pterodactyl\Http\Requests\Api\Application\Databases\DatabaseNodesRequest $request + * @param \Pterodactyl\Models\Mount $mount + * + * @return array + */ + public function addNodes(DatabaseNodesRequest $request, DatabaseHost $databaseHost): array + { + $data = $request->validated(); + + $nodes = $data['nodes'] ?? []; + if (count($nodes) > 0) { + $databaseHost->nodes()->syncWithoutDetaching($nodes); + } + + return $this->fractal->item($databaseHost) + ->transformWith($this->getTransformer(DatabaseHostTransformer::class)) + ->toArray(); + } + + /** + * ? + * + * @param \Pterodactyl\Http\Requests\Api\Application\Databases\DatabaseNodesRequest $request + * @param \Pterodactyl\Models\Mount $mount + * + * @return array + */ + public function deleteNodes(DatabaseNodesRequest $request, DatabaseHost $databaseHost): array + { + $data = $request->validated(); + + $nodes = $data['nodes'] ?? []; + if (count($nodes) > 0) { + $databaseHost->nodes()->detach($nodes); + } + + return $this->fractal->item($databaseHost) + ->transformWith($this->getTransformer(DatabaseHostTransformer::class)) + ->toArray(); + } } diff --git a/app/Http/Controllers/Api/Application/Mounts/MountController.php b/app/Http/Controllers/Api/Application/Mounts/MountController.php index 478cf6c77..39987ada9 100644 --- a/app/Http/Controllers/Api/Application/Mounts/MountController.php +++ b/app/Http/Controllers/Api/Application/Mounts/MountController.php @@ -9,11 +9,11 @@ use Pterodactyl\Transformers\Api\Application\MountTransformer; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Pterodactyl\Http\Requests\Api\Application\Mounts\GetMountRequest; use Pterodactyl\Http\Requests\Api\Application\Mounts\GetMountsRequest; +use Pterodactyl\Http\Requests\Api\Application\Mounts\MountEggsRequest; +use Pterodactyl\Http\Requests\Api\Application\Mounts\MountNodesRequest; use Pterodactyl\Http\Requests\Api\Application\Mounts\StoreMountRequest; use Pterodactyl\Http\Requests\Api\Application\Mounts\UpdateMountRequest; use Pterodactyl\Http\Requests\Api\Application\Mounts\DeleteMountRequest; -use Pterodactyl\Http\Requests\Api\Application\Mounts\MountAddEggsRequest; -use Pterodactyl\Http\Requests\Api\Application\Mounts\MountAddNodesRequest; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; class MountController extends ApplicationApiController @@ -123,12 +123,12 @@ class MountController extends ApplicationApiController /** * ? * - * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountAddEggsRequest $request + * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountEggsRequest $request * @param \Pterodactyl\Models\Mount $mount * * @return array */ - public function addEggs(MountAddEggsRequest $request, Mount $mount): array + public function addEggs(MountEggsRequest $request, Mount $mount): array { $data = $request->validated(); @@ -145,12 +145,12 @@ class MountController extends ApplicationApiController /** * ? * - * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountAddNodesRequest $request + * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountNodesRequest $request * @param \Pterodactyl\Models\Mount $mount * * @return array */ - public function addNodes(MountAddNodesRequest $request, Mount $mount): array + public function addNodes(MountNodesRequest $request, Mount $mount): array { $data = $request->validated(); @@ -167,12 +167,12 @@ class MountController extends ApplicationApiController /** * ? * - * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountAddEggsRequest $request + * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountEggsRequest $request * @param \Pterodactyl\Models\Mount $mount * * @return array */ - public function deleteEggs(MountAddEggsRequest $request, Mount $mount): array + public function deleteEggs(MountEggsRequest $request, Mount $mount): array { $data = $request->validated(); @@ -189,12 +189,12 @@ class MountController extends ApplicationApiController /** * ? * - * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountAddNodesRequest $request + * @param \Pterodactyl\Http\Requests\Api\Application\Mounts\MountNodesRequest $request * @param \Pterodactyl\Models\Mount $mount * * @return array */ - public function deleteNodes(MountAddNodesRequest $request, Mount $mount): array + public function deleteNodes(MountNodesRequest $request, Mount $mount): array { $data = $request->validated(); diff --git a/app/Http/Requests/Api/Application/Databases/DatabaseNodesRequest.php b/app/Http/Requests/Api/Application/Databases/DatabaseNodesRequest.php new file mode 100644 index 000000000..7bd16d0e6 --- /dev/null +++ b/app/Http/Requests/Api/Application/Databases/DatabaseNodesRequest.php @@ -0,0 +1,31 @@ + 'required|exists:nodes,id']; + } +} diff --git a/app/Http/Requests/Api/Application/Mounts/MountAddEggsRequest.php b/app/Http/Requests/Api/Application/Mounts/MountEggsRequest.php similarity index 91% rename from app/Http/Requests/Api/Application/Mounts/MountAddEggsRequest.php rename to app/Http/Requests/Api/Application/Mounts/MountEggsRequest.php index 26d257aa0..297ec46c8 100644 --- a/app/Http/Requests/Api/Application/Mounts/MountAddEggsRequest.php +++ b/app/Http/Requests/Api/Application/Mounts/MountEggsRequest.php @@ -5,7 +5,7 @@ namespace Pterodactyl\Http\Requests\Api\Application\Mounts; use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; -class MountAddEggsRequest extends ApplicationApiRequest +class MountEggsRequest extends ApplicationApiRequest { /** * @var string diff --git a/app/Http/Requests/Api/Application/Mounts/MountAddNodesRequest.php b/app/Http/Requests/Api/Application/Mounts/MountNodesRequest.php similarity index 90% rename from app/Http/Requests/Api/Application/Mounts/MountAddNodesRequest.php rename to app/Http/Requests/Api/Application/Mounts/MountNodesRequest.php index 94a90d89f..e14d816c7 100644 --- a/app/Http/Requests/Api/Application/Mounts/MountAddNodesRequest.php +++ b/app/Http/Requests/Api/Application/Mounts/MountNodesRequest.php @@ -5,7 +5,7 @@ namespace Pterodactyl\Http\Requests\Api\Application\Mounts; use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest; -class MountAddNodesRequest extends ApplicationApiRequest +class MountNodesRequest extends ApplicationApiRequest { /** * @var string diff --git a/app/Models/DatabaseHost.php b/app/Models/DatabaseHost.php index 21ab42284..a005974e5 100644 --- a/app/Models/DatabaseHost.php +++ b/app/Models/DatabaseHost.php @@ -8,12 +8,11 @@ namespace Pterodactyl\Models; * @property string $host * @property int $port * @property string $username - * @property int $node_id * @property \Carbon\Carbon|null $created_at * @property \Carbon\Carbon|null $updated_at * - * @property \Pterodactyl\Models\Node|null $node * @property \Pterodactyl\Models\Database[]|\Illuminate\Database\Eloquent\Collection $databases + * @property \Pterodactyl\Models\Node[]|\Illuminate\Database\Eloquent\Collection $nodes */ class DatabaseHost extends Model { @@ -43,7 +42,7 @@ class DatabaseHost extends Model * @var array */ protected $fillable = [ - 'name', 'host', 'port', 'username', 'password', 'max_databases', 'node_id', + 'name', 'host', 'port', 'username', 'password', 'max_databases', ]; /** @@ -54,7 +53,6 @@ class DatabaseHost extends Model protected $casts = [ 'id' => 'integer', 'max_databases' => 'integer', - 'node_id' => 'integer', ]; /** @@ -68,21 +66,10 @@ class DatabaseHost extends Model 'port' => 'required|numeric|between:1,65535', 'username' => 'required|string|max:32', 'password' => 'nullable|string', - 'node_id' => 'sometimes|nullable|integer|exists:nodes,id', ]; /** - * Gets the node associated with a database host. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function node() - { - return $this->belongsTo(Node::class); - } - - /** - * Gets the databases associated with this host. + * Gets the databases associated with a database host. * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -90,4 +77,14 @@ class DatabaseHost extends Model { return $this->hasMany(Database::class); } + + /** + * Gets the nodes associated with a database host. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function nodes() + { + return $this->belongsToMany(Node::class); + } } diff --git a/app/Models/DatabaseHostNode.php b/app/Models/DatabaseHostNode.php new file mode 100644 index 000000000..d682a3562 --- /dev/null +++ b/app/Models/DatabaseHostNode.php @@ -0,0 +1,27 @@ +loadMissing('databases'); - return $this->collection($model->getRelation('databases'), $this->makeTransformer(ServerDatabaseTransformer::class), Database::RESOURCE_NAME); + return $this->collection( + $model->getRelation('databases'), + $this->makeTransformer(ServerDatabaseTransformer::class), + Database::RESOURCE_NAME + ); + } + + + + /** + * Return the nodes associated with this mount. + * + * @param \Pterodactyl\Models\Mount $mount + * + * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource + * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function includeNodes(DatabaseHost $model) + { + if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + return $this->null(); + } + + $model->loadMissing('nodes'); + + return $this->collection( + $model->getRelation('nodes'), + $this->makeTransformer(NodeTransformer::class), + Node::RESOURCE_NAME + ); } } diff --git a/app/Transformers/Api/Application/MountTransformer.php b/app/Transformers/Api/Application/MountTransformer.php index be534af56..d192edf49 100644 --- a/app/Transformers/Api/Application/MountTransformer.php +++ b/app/Transformers/Api/Application/MountTransformer.php @@ -2,7 +2,10 @@ namespace Pterodactyl\Transformers\Api\Application; +use Pterodactyl\Models\Egg; +use Pterodactyl\Models\Node; use Pterodactyl\Models\Mount; +use Pterodactyl\Models\Server; use Pterodactyl\Services\Acl\Api\AdminAcl; class MountTransformer extends BaseTransformer @@ -64,7 +67,7 @@ class MountTransformer extends BaseTransformer return $this->collection( $mount->getRelation('eggs'), $this->makeTransformer(EggTransformer::class), - 'egg', + Egg::RESOURCE_NAME ); } @@ -88,7 +91,7 @@ class MountTransformer extends BaseTransformer return $this->collection( $mount->getRelation('nodes'), $this->makeTransformer(NodeTransformer::class), - 'node', + Node::RESOURCE_NAME ); } @@ -112,7 +115,7 @@ class MountTransformer extends BaseTransformer return $this->collection( $mount->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), - 'server', + Server::RESOURCE_NAME ); } } diff --git a/database/migrations/2021_01_15_155057_add_database_host_node_table.php b/database/migrations/2021_01_15_155057_add_database_host_node_table.php new file mode 100644 index 000000000..0f7c93459 --- /dev/null +++ b/database/migrations/2021_01_15_155057_add_database_host_node_table.php @@ -0,0 +1,56 @@ +integer('node_id')->unique(); + $table->integer('database_host_id'); + + $table->unique(['node_id', 'database_host_id']); + }); + + DB::transaction(function () { + foreach (DB::select('SELECT id, node_id FROM database_hosts') as $datum) { + if (! is_null($datum->node_id)) { + DB::insert('INSERT INTO database_host_node (node_id, database_host_id) VALUES (?, ?)', [ + $datum->node_id, + $datum->id + ]); + } + } + }); + + Schema::table('database_hosts', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->dropColumn('node_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('database_hosts', function (Blueprint $table) { + $table->integer('node_id')->unsigned()->nullable()->after('max_databases'); + + $table->foreign('node_id')->references('id')->on('nodes')->onDelete('set null'); + }); + + Schema::dropIfExists('database_host_node'); + } +} diff --git a/routes/api-application.php b/routes/api-application.php index fd7253a02..1492d7840 100644 --- a/routes/api-application.php +++ b/routes/api-application.php @@ -18,9 +18,12 @@ Route::group(['prefix' => '/databases'], function () { Route::post('/', 'Databases\DatabaseController@store'); + Route::put('/{databaseHost}/nodes', 'Databases\DatabaseController@addNodes'); + Route::patch('/{databaseHost}', 'Databases\DatabaseController@update'); Route::delete('/{databaseHost}', 'Databases\DatabaseController@delete'); + Route::delete('/{databaseHost}/nodes', 'Databases\DatabaseController@deleteNodes'); }); /*