Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php
14056 views
1
<?php
2
3
namespace Pterodactyl\Http\Controllers\Api\Remote\Servers;
4
5
use Illuminate\Http\Request;
6
use Pterodactyl\Models\Node;
7
use Webmozart\Assert\Assert;
8
use Pterodactyl\Models\Server;
9
use Illuminate\Http\JsonResponse;
10
use Pterodactyl\Facades\Activity;
11
use Illuminate\Database\ConnectionInterface;
12
use Pterodactyl\Http\Controllers\Controller;
13
use Pterodactyl\Services\Eggs\EggConfigurationService;
14
use Pterodactyl\Exceptions\Http\HttpForbiddenException;
15
use Pterodactyl\Repositories\Eloquent\ServerRepository;
16
use Pterodactyl\Http\Resources\Wings\ServerConfigurationCollection;
17
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
18
19
class ServerDetailsController extends Controller
20
{
21
/**
22
* ServerConfigurationController constructor.
23
*/
24
public function __construct(
25
protected ConnectionInterface $connection,
26
private ServerRepository $repository,
27
private ServerConfigurationStructureService $configurationStructureService,
28
private EggConfigurationService $eggConfigurationService,
29
) {
30
}
31
32
/**
33
* Returns details about the server that allows Wings to self-recover and ensure
34
* that the state of the server matches the Panel at all times.
35
*
36
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
37
*/
38
public function __invoke(Request $request, string $uuid): JsonResponse
39
{
40
Assert::isInstanceOf($node = $request->attributes->get('node'), Node::class);
41
42
$server = $this->repository->getByUuid($uuid);
43
$transfer = $server->transfer;
44
45
// If the server is being transferred allow either node to request information about
46
// the server. If the server is not being transferred only the target node is allowed
47
// to fetch these details.
48
$valid = $transfer
49
? $node->id === $transfer->old_node || $node->id === $transfer->new_node
50
: $node->id === $server->node_id;
51
52
if (! $valid) {
53
throw new HttpForbiddenException('Requesting node does not have permission to access this server.');
54
}
55
56
return new JsonResponse([
57
'settings' => $this->configurationStructureService->handle($server),
58
'process_configuration' => $this->eggConfigurationService->handle($server),
59
]);
60
}
61
62
/**
63
* Lists all servers with their configurations that are assigned to the requesting node.
64
*/
65
public function list(Request $request): ServerConfigurationCollection
66
{
67
/** @var Node $node */
68
$node = $request->attributes->get('node');
69
70
// Avoid run-away N+1 SQL queries by preloading the relationships that are used
71
// within each of the services called below.
72
$servers = Server::query()->with('allocations', 'egg', 'mounts', 'variables', 'location')
73
->where('node_id', $node->id)
74
// If you don't cast this to a string you'll end up with a stringified per_page returned in
75
// the metadata, and then Wings will panic crash as a result.
76
->paginate((int) $request->input('per_page', 50));
77
78
return new ServerConfigurationCollection($servers);
79
}
80
81
/**
82
* Resets the state of all servers on the node to be normal. This is triggered
83
* when Wings restarts and is useful for ensuring that any servers on the node
84
* do not get incorrectly stuck in installing/restoring from backup states since
85
* a Wings reboot would completely stop those processes.
86
*
87
* @throws \Throwable
88
*/
89
public function resetState(Request $request): JsonResponse
90
{
91
$node = $request->attributes->get('node');
92
93
// Get all the servers that are currently marked as restoring from a backup
94
// on this node that do not have a failed backup tracked in the audit logs table
95
// as well.
96
//
97
// For each of those servers we'll track a new audit log entry to mark them as
98
// failed and then update them all to be in a valid state.
99
$servers = Server::query()
100
->with([
101
'activity' => fn ($builder) => $builder
102
->where('activity_logs.event', 'server:backup.restore-started')
103
->latest('timestamp'),
104
])
105
->where('node_id', $node->id)
106
->where('status', Server::STATUS_RESTORING_BACKUP)
107
->get();
108
109
$this->connection->transaction(function () use ($node, $servers) {
110
/** @var Server $server */
111
foreach ($servers as $server) {
112
/** @var \Pterodactyl\Models\ActivityLog|null $activity */
113
$activity = $server->activity->first();
114
if (!is_null($activity)) {
115
if ($subject = $activity->subjects->where('subject_type', 'backup')->first()) {
116
// Just create a new audit entry for this event and update the server state
117
// so that power actions, file management, and backups can resume as normal.
118
Activity::event('server:backup.restore-failed')
119
->subject($server, $subject->subject)
120
->property('name', $subject->subject->name) // @phpstan-ignore property.notFound
121
->log();
122
}
123
}
124
}
125
126
// Update any server marked as installing or restoring as being in a normal state
127
// at this point in the process.
128
Server::query()->where('node_id', $node->id)
129
->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP])
130
->update(['status' => null]);
131
});
132
133
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
134
}
135
}
136
137