Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into Settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Poseidon281 committed May 19, 2024
2 parents 0ec9d0b + b1f99ca commit 91a04c8
Show file tree
Hide file tree
Showing 12 changed files with 374 additions and 1 deletion.
165 changes: 165 additions & 0 deletions app/Http/Controllers/Api/Application/Mounts/MountController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?php

namespace App\Http\Controllers\Api\Application\Mounts;

use Ramsey\Uuid\Uuid;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Translation\Translator;
use Spatie\QueryBuilder\QueryBuilder;
use App\Models\Mount;
use App\Http\Controllers\Api\Application\ApplicationApiController;
use App\Transformers\Api\Application\MountTransformer;
use App\Http\Requests\Api\Application\Mounts\GetMountRequest;
use App\Http\Requests\Api\Application\Mounts\StoreMountRequest;
use App\Http\Requests\Api\Application\Mounts\DeleteMountRequest;
use App\Http\Requests\Api\Application\Mounts\UpdateMountRequest;
use App\Exceptions\Service\HasActiveServersException;

class MountController extends ApplicationApiController
{
/**
* MountController constructor.
*/
public function __construct(
protected Translator $translator
) {
parent::__construct();
}

/**
* Return all the mounts currently available on the Panel.
*/
public function index(GetMountRequest $request): array
{
$mounts = QueryBuilder::for(Mount::query())
->allowedFilters(['uuid', 'name'])
->allowedSorts(['id', 'uuid'])
->paginate($request->query('per_page') ?? 50);

return $this->fractal->collection($mounts)
->transformWith($this->getTransformer(MountTransformer::class))
->toArray();
}

/**
* Return data for a single instance of a mount.
*/
public function view(GetMountRequest $request, Mount $mount): array
{
return $this->fractal->item($mount)
->transformWith($this->getTransformer(MountTransformer::class))
->toArray();
}

/**
* Create a new mount on the Panel. Returns the created mount and an HTTP/201
* status response on success.
*
* @throws \App\Exceptions\Model\DataValidationException
*/
public function store(StoreMountRequest $request): JsonResponse
{
$model = (new Mount())->fill($request->validated());
$model->forceFill(['uuid' => Uuid::uuid4()->toString()]);

$model->saveOrFail();
$mount = $model->fresh();

return $this->fractal->item($mount)
->transformWith($this->getTransformer(MountTransformer::class))
->addMeta([
'resource' => route('api.application.mounts.view', [
'mount' => $mount->id,
]),
])
->respond(201);
}

/**
* Update an existing mount on the Panel.
*
* @throws \Throwable
*/
public function update(UpdateMountRequest $request, Mount $mount): array
{
$mount->forceFill($request->validated())->save();

return $this->fractal->item($mount)
->transformWith($this->getTransformer(MountTransformer::class))
->toArray();
}

/**
* Deletes a given mount from the Panel as long as there are no servers
* currently attached to it.
*
* @throws \App\Exceptions\Service\HasActiveServersException
*/
public function delete(DeleteMountRequest $request, Mount $mount): JsonResponse
{
if ($mount->servers()->count() > 0) {
throw new HasActiveServersException($this->translator->get('exceptions.mount.servers_attached'));
}

$mount->delete();

return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
}

/**
* Adds eggs to the mount's many-to-many relation.
*/
public function addEggs(Request $request, Mount $mount): array
{
$validatedData = $request->validate([
'eggs' => 'required|exists:eggs,id',
]);

$eggs = $validatedData['eggs'] ?? [];
if (count($eggs) > 0) {
$mount->eggs()->attach($eggs);
}

return $this->fractal->item($mount)
->transformWith($this->getTransformer(MountTransformer::class))
->toArray();
}

/**
* Adds nodes to the mount's many-to-many relation.
*/
public function addNodes(Request $request, Mount $mount): array
{
$data = $request->validate(['nodes' => 'required|exists:nodes,id']);

$nodes = $data['nodes'] ?? [];
if (count($nodes) > 0) {
$mount->nodes()->attach($nodes);
}

return $this->fractal->item($mount)
->transformWith($this->getTransformer(MountTransformer::class))
->toArray();
}

/**
* Deletes an egg from the mount's many-to-many relation.
*/
public function deleteEgg(Mount $mount, int $egg_id): JsonResponse
{
$mount->eggs()->detach($egg_id);

return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
}

/**
* Deletes a node from the mount's many-to-many relation.
*/
public function deleteNode(Mount $mount, int $node_id): JsonResponse
{
$mount->nodes()->detach($node_id);

return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
}
}
13 changes: 13 additions & 0 deletions app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App\Http\Requests\Api\Application\Mounts;

use App\Services\Acl\Api\AdminAcl;
use App\Http\Requests\Api\Application\ApplicationApiRequest;

class DeleteMountRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_MOUNTS;

protected int $permission = AdminAcl::WRITE;
}
13 changes: 13 additions & 0 deletions app/Http/Requests/Api/Application/Mounts/GetMountRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App\Http\Requests\Api\Application\Mounts;

use App\Services\Acl\Api\AdminAcl;
use App\Http\Requests\Api\Application\ApplicationApiRequest;

class GetMountRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_MOUNTS;

protected int $permission = AdminAcl::READ;
}
13 changes: 13 additions & 0 deletions app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App\Http\Requests\Api\Application\Mounts;

use App\Services\Acl\Api\AdminAcl;
use App\Http\Requests\Api\Application\ApplicationApiRequest;

class StoreMountRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_MOUNTS;

protected int $permission = AdminAcl::WRITE;
}
20 changes: 20 additions & 0 deletions app/Http/Requests/Api/Application/Mounts/UpdateMountRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Requests\Api\Application\Mounts;

use App\Models\Mount;

class UpdateMountRequest extends StoreMountRequest
{
/**
* Apply validation rules to this request. Uses the parent class rules()
* function but passes in the rules for updating rather than creating.
*/
public function rules(array $rules = null): array
{
/** @var Mount $mount */
$mount = $this->route()->parameter('mount');

return parent::rules(Mount::getRulesForUpdate($mount->id));
}
}
6 changes: 5 additions & 1 deletion app/Models/ApiKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* @property int $r_eggs
* @property int $r_database_hosts
* @property int $r_server_databases
* @property int $r_mounts
* @property \App\Models\User $tokenable
* @property \App\Models\User $user
*
Expand Down Expand Up @@ -83,7 +84,7 @@ class ApiKey extends Model
*/
public const KEY_LENGTH = 32;

public const RESOURCES = ['servers', 'nodes', 'allocations', 'users', 'eggs', 'database_hosts', 'server_databases'];
public const RESOURCES = ['servers', 'nodes', 'allocations', 'users', 'eggs', 'database_hosts', 'server_databases', 'mounts'];

/**
* The table associated with the model.
Expand All @@ -109,6 +110,7 @@ class ApiKey extends Model
'r_' . AdminAcl::RESOURCE_EGGS,
'r_' . AdminAcl::RESOURCE_NODES,
'r_' . AdminAcl::RESOURCE_SERVERS,
'r_' . AdminAcl::RESOURCE_MOUNTS,
];

/**
Expand Down Expand Up @@ -137,6 +139,7 @@ class ApiKey extends Model
'r_' . AdminAcl::RESOURCE_EGGS => 'integer|min:0|max:3',
'r_' . AdminAcl::RESOURCE_NODES => 'integer|min:0|max:3',
'r_' . AdminAcl::RESOURCE_SERVERS => 'integer|min:0|max:3',
'r_' . AdminAcl::RESOURCE_MOUNTS => 'integer|min:0|max:3',
];

protected function casts(): array
Expand All @@ -155,6 +158,7 @@ protected function casts(): array
'r_' . AdminAcl::RESOURCE_EGGS => 'int',
'r_' . AdminAcl::RESOURCE_NODES => 'int',
'r_' . AdminAcl::RESOURCE_SERVERS => 'int',
'r_' . AdminAcl::RESOURCE_MOUNTS => 'int',
];
}

Expand Down
1 change: 1 addition & 0 deletions app/Services/Acl/Api/AdminAcl.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class AdminAcl
public const RESOURCE_EGGS = 'eggs';
public const RESOURCE_DATABASE_HOSTS = 'database_hosts';
public const RESOURCE_SERVER_DATABASES = 'server_databases';
public const RESOURCE_MOUNTS = 'mounts';

/**
* Determine if an API key has permission to perform a specific read/write operation.
Expand Down
89 changes: 89 additions & 0 deletions app/Transformers/Api/Application/MountTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace App\Transformers\Api\Application;

use App\Models\Mount;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\NullResource;
use App\Services\Acl\Api\AdminAcl;

class MountTransformer extends BaseTransformer
{
/**
* List of resources that can be included.
*/
protected array $availableIncludes = ['eggs', 'nodes', 'servers'];

/**
* Return the resource name for the JSONAPI output.
*/
public function getResourceName(): string
{
return Mount::RESOURCE_NAME;
}

public function transform(Mount $model)
{
return $model->toArray();
}

/**
* Return the eggs associated with this mount.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeEggs(Mount $mount): Collection|NullResource
{
if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) {
return $this->null();
}

$mount->loadMissing('eggs');

return $this->collection(
$mount->getRelation('eggs'),
$this->makeTransformer(EggTransformer::class),
'egg'
);
}

/**
* Return the nodes associated with this mount.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeNodes(Mount $mount): Collection|NullResource
{
if (!$this->authorize(AdminAcl::RESOURCE_NODES)) {
return $this->null();
}

$mount->loadMissing('nodes');

return $this->collection(
$mount->getRelation('nodes'),
$this->makeTransformer(NodeTransformer::class),
'node'
);
}

/**
* Return the servers associated with this mount.
*
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
*/
public function includeServers(Mount $mount): Collection|NullResource
{
if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) {
return $this->null();
}

$mount->loadMissing('servers');

return $this->collection(
$mount->getRelation('servers'),
$this->makeTransformer(ServerTransformer::class),
'server'
);
}
}
28 changes: 28 additions & 0 deletions database/migrations/2024_04_28_184102_add_mounts_to_api_keys.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('api_keys', function (Blueprint $table) {
$table->unsignedTinyInteger('r_mounts')->default(0);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('api_keys', function (Blueprint $table) {
$table->dropColumn('r_mounts');
});
}
};
Loading

0 comments on commit 91a04c8

Please sign in to comment.