Path: blob/1.0-develop/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php
14056 views
<?php12namespace Pterodactyl\Http\Controllers\Api\Remote\Servers;34use Illuminate\Http\Request;5use Pterodactyl\Models\Node;6use Webmozart\Assert\Assert;7use Pterodactyl\Models\Server;8use Illuminate\Http\JsonResponse;9use Pterodactyl\Facades\Activity;10use Illuminate\Database\ConnectionInterface;11use Pterodactyl\Http\Controllers\Controller;12use Pterodactyl\Services\Eggs\EggConfigurationService;13use Pterodactyl\Exceptions\Http\HttpForbiddenException;14use Pterodactyl\Repositories\Eloquent\ServerRepository;15use Pterodactyl\Http\Resources\Wings\ServerConfigurationCollection;16use Pterodactyl\Services\Servers\ServerConfigurationStructureService;1718class ServerDetailsController extends Controller19{20/**21* ServerConfigurationController constructor.22*/23public function __construct(24protected ConnectionInterface $connection,25private ServerRepository $repository,26private ServerConfigurationStructureService $configurationStructureService,27private EggConfigurationService $eggConfigurationService,28) {29}3031/**32* Returns details about the server that allows Wings to self-recover and ensure33* that the state of the server matches the Panel at all times.34*35* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException36*/37public function __invoke(Request $request, string $uuid): JsonResponse38{39Assert::isInstanceOf($node = $request->attributes->get('node'), Node::class);4041$server = $this->repository->getByUuid($uuid);42$transfer = $server->transfer;4344// If the server is being transferred allow either node to request information about45// the server. If the server is not being transferred only the target node is allowed46// to fetch these details.47$valid = $transfer48? $node->id === $transfer->old_node || $node->id === $transfer->new_node49: $node->id === $server->node_id;5051if (! $valid) {52throw new HttpForbiddenException('Requesting node does not have permission to access this server.');53}5455return new JsonResponse([56'settings' => $this->configurationStructureService->handle($server),57'process_configuration' => $this->eggConfigurationService->handle($server),58]);59}6061/**62* Lists all servers with their configurations that are assigned to the requesting node.63*/64public function list(Request $request): ServerConfigurationCollection65{66/** @var Node $node */67$node = $request->attributes->get('node');6869// Avoid run-away N+1 SQL queries by preloading the relationships that are used70// within each of the services called below.71$servers = Server::query()->with('allocations', 'egg', 'mounts', 'variables', 'location')72->where('node_id', $node->id)73// If you don't cast this to a string you'll end up with a stringified per_page returned in74// the metadata, and then Wings will panic crash as a result.75->paginate((int) $request->input('per_page', 50));7677return new ServerConfigurationCollection($servers);78}7980/**81* Resets the state of all servers on the node to be normal. This is triggered82* when Wings restarts and is useful for ensuring that any servers on the node83* do not get incorrectly stuck in installing/restoring from backup states since84* a Wings reboot would completely stop those processes.85*86* @throws \Throwable87*/88public function resetState(Request $request): JsonResponse89{90$node = $request->attributes->get('node');9192// Get all the servers that are currently marked as restoring from a backup93// on this node that do not have a failed backup tracked in the audit logs table94// as well.95//96// For each of those servers we'll track a new audit log entry to mark them as97// failed and then update them all to be in a valid state.98$servers = Server::query()99->with([100'activity' => fn ($builder) => $builder101->where('activity_logs.event', 'server:backup.restore-started')102->latest('timestamp'),103])104->where('node_id', $node->id)105->where('status', Server::STATUS_RESTORING_BACKUP)106->get();107108$this->connection->transaction(function () use ($node, $servers) {109/** @var Server $server */110foreach ($servers as $server) {111/** @var \Pterodactyl\Models\ActivityLog|null $activity */112$activity = $server->activity->first();113if (!is_null($activity)) {114if ($subject = $activity->subjects->where('subject_type', 'backup')->first()) {115// Just create a new audit entry for this event and update the server state116// so that power actions, file management, and backups can resume as normal.117Activity::event('server:backup.restore-failed')118->subject($server, $subject->subject)119->property('name', $subject->subject->name) // @phpstan-ignore property.notFound120->log();121}122}123}124125// Update any server marked as installing or restoring as being in a normal state126// at this point in the process.127Server::query()->where('node_id', $node->id)128->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP])129->update(['status' => null]);130});131132return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);133}134}135136137