164 lines
6.3 KiB
PHP
164 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace Convoy\Http\Controllers\Admin;
|
|
|
|
use Convoy\Enums\Server\Status;
|
|
use Convoy\Enums\Server\SuspensionAction;
|
|
use Convoy\Exceptions\Repository\Proxmox\ProxmoxConnectionException;
|
|
use Convoy\Http\Controllers\ApiController;
|
|
use Convoy\Http\Requests\Admin\Servers\Settings\UpdateBuildRequest;
|
|
use Convoy\Http\Requests\Admin\Servers\Settings\UpdateGeneralInfoRequest;
|
|
use Convoy\Http\Requests\Admin\Servers\StoreServerRequest;
|
|
use Convoy\Models\Filters\FiltersServerByAddressPoolId;
|
|
use Convoy\Models\Filters\FiltersServerWildcard;
|
|
use Convoy\Models\Server;
|
|
use Convoy\Services\Servers\CloudinitService;
|
|
use Convoy\Services\Servers\NetworkService;
|
|
use Convoy\Services\Servers\ServerCreationService;
|
|
use Convoy\Services\Servers\ServerDeletionService;
|
|
use Convoy\Services\Servers\ServerSuspensionService;
|
|
use Convoy\Services\Servers\SyncBuildService;
|
|
use Convoy\Transformers\Admin\ServerBuildTransformer;
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Http\Request;
|
|
use Spatie\QueryBuilder\AllowedFilter;
|
|
use Spatie\QueryBuilder\QueryBuilder;
|
|
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
|
|
|
|
class ServerController extends ApiController
|
|
{
|
|
public function __construct(
|
|
private ConnectionInterface $connection,
|
|
private ServerDeletionService $deletionService,
|
|
private NetworkService $networkService,
|
|
private ServerSuspensionService $suspensionService,
|
|
private ServerCreationService $creationService,
|
|
private CloudinitService $cloudinitService,
|
|
private SyncBuildService $buildModificationService,
|
|
)
|
|
{
|
|
}
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$servers = QueryBuilder::for(Server::query())
|
|
->with(['addresses', 'user', 'node'])
|
|
->defaultSort('-id')
|
|
->allowedFilters(
|
|
[
|
|
AllowedFilter::custom(
|
|
'*', new FiltersServerWildcard(),
|
|
),
|
|
AllowedFilter::custom(
|
|
'address_pool_id',
|
|
new FiltersServerByAddressPoolId(),
|
|
),
|
|
AllowedFilter::exact('node_id'),
|
|
AllowedFilter::exact('user_id'),
|
|
'name',
|
|
],
|
|
)
|
|
->paginate(min($request->query('per_page', 50), 100))->appends(
|
|
$request->query(),
|
|
);
|
|
|
|
return fractal($servers, new ServerBuildTransformer())->parseIncludes($request->include)
|
|
->respond();
|
|
}
|
|
|
|
public function show(Request $request, Server $server)
|
|
{
|
|
$server->load(['addresses', 'user', 'node']);
|
|
|
|
return fractal($server, new ServerBuildTransformer())->parseIncludes($request->include)
|
|
->respond();
|
|
}
|
|
|
|
public function store(StoreServerRequest $request)
|
|
{
|
|
$server = $this->creationService->handle($request->validated());
|
|
|
|
$server->load(['addresses', 'user', 'node']);
|
|
|
|
return fractal($server, new ServerBuildTransformer())->parseIncludes(['user', 'node'])
|
|
->respond();
|
|
}
|
|
|
|
public function update(UpdateGeneralInfoRequest $request, Server $server)
|
|
{
|
|
$this->connection->transaction(function () use ($request, $server) {
|
|
if ($request->hostname !== $server->hostname && !empty($request->hostname)) {
|
|
try {
|
|
$this->cloudinitService->updateHostname($server, $request->hostname);
|
|
} catch (ProxmoxConnectionException) {
|
|
throw new ServiceUnavailableHttpException(
|
|
message: "Server {$server->uuid} failed to sync hostname.",
|
|
);
|
|
}
|
|
}
|
|
|
|
$server->update($request->validated());
|
|
});
|
|
|
|
$server->load(['addresses', 'user', 'node']);
|
|
|
|
return fractal($server, new ServerBuildTransformer())->parseIncludes(['user', 'node'])
|
|
->respond();
|
|
}
|
|
|
|
public function updateBuild(UpdateBuildRequest $request, Server $server)
|
|
{
|
|
$validated = $request->validated();
|
|
|
|
// Handle address_ids separately
|
|
$addressIds = $validated['address_ids'] ?? null;
|
|
unset($validated['address_ids']);
|
|
|
|
// Ensure rate_limit is always present in update data to avoid validation errors
|
|
if (!array_key_exists('rate_limit', $validated)) {
|
|
$validated['rate_limit'] = $server->rate_limit ?? null;
|
|
}
|
|
|
|
// Update server with validated data, skip model validation as request validation is already done
|
|
$server->skipValidation()->update($validated);
|
|
|
|
$this->networkService->updateAddresses($server, $addressIds ?? []);
|
|
|
|
try {
|
|
$this->buildModificationService->handle($server);
|
|
} catch (ProxmoxConnectionException $e) {
|
|
// do nothing
|
|
}
|
|
|
|
$server->load(['addresses', 'user', 'node']);
|
|
|
|
return fractal($server, new ServerBuildTransformer())->parseIncludes(['user', 'node'])
|
|
->respond();
|
|
}
|
|
|
|
public function suspend(Server $server)
|
|
{
|
|
$this->suspensionService->toggle($server);
|
|
|
|
return $this->returnNoContent();
|
|
}
|
|
|
|
public function unsuspend(Server $server)
|
|
{
|
|
$this->suspensionService->toggle($server, SuspensionAction::UNSUSPEND);
|
|
|
|
return $this->returnNoContent();
|
|
}
|
|
|
|
public function destroy(Request $request, Server $server)
|
|
{
|
|
$this->connection->transaction(function () use ($server, $request) {
|
|
$server->update(['status' => Status::DELETING->value]);
|
|
|
|
$this->deletionService->handle($server, $request->input('no_purge', false));
|
|
});
|
|
|
|
return $this->returnNoContent();
|
|
}
|
|
}
|