Skip to content

Commit 5c5e2e2

Browse files
committed
📯 tRaNsFeR lOgS 📯
1 parent e6c4a68 commit 5c5e2e2

File tree

12 files changed

+149
-65
lines changed

12 files changed

+149
-65
lines changed

app/Http/Controllers/Admin/Servers/ServerTransferController.php

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Pterodactyl\Models\ServerTransfer;
99
use Pterodactyl\Http\Controllers\Controller;
1010
use Pterodactyl\Services\Servers\TransferService;
11-
use Pterodactyl\Services\Servers\SuspensionService;
1211
use Pterodactyl\Repositories\Eloquent\NodeRepository;
1312
use Pterodactyl\Repositories\Eloquent\ServerRepository;
1413
use Pterodactyl\Repositories\Eloquent\LocationRepository;
@@ -42,11 +41,6 @@ class ServerTransferController extends Controller
4241
*/
4342
private $nodeRepository;
4443

45-
/**
46-
* @var \Pterodactyl\Services\Servers\SuspensionService
47-
*/
48-
private $suspensionService;
49-
5044
/**
5145
* @var \Pterodactyl\Services\Servers\TransferService
5246
*/
@@ -65,7 +59,6 @@ class ServerTransferController extends Controller
6559
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
6660
* @param \Pterodactyl\Repositories\Eloquent\LocationRepository $locationRepository
6761
* @param \Pterodactyl\Repositories\Eloquent\NodeRepository $nodeRepository
68-
* @param \Pterodactyl\Services\Servers\SuspensionService $suspensionService
6962
* @param \Pterodactyl\Services\Servers\TransferService $transferService
7063
* @param \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository $daemonConfigurationRepository
7164
*/
@@ -75,7 +68,6 @@ public function __construct(
7568
ServerRepository $repository,
7669
LocationRepository $locationRepository,
7770
NodeRepository $nodeRepository,
78-
SuspensionService $suspensionService,
7971
TransferService $transferService,
8072
DaemonConfigurationRepository $daemonConfigurationRepository
8173
) {
@@ -84,7 +76,6 @@ public function __construct(
8476
$this->repository = $repository;
8577
$this->locationRepository = $locationRepository;
8678
$this->nodeRepository = $nodeRepository;
87-
$this->suspensionService = $suspensionService;
8879
$this->transferService = $transferService;
8980
$this->daemonConfigurationRepository = $daemonConfigurationRepository;
9081
}
@@ -98,8 +89,7 @@ public function __construct(
9889
*
9990
* @throws \Throwable
10091
*/
101-
public function transfer(Request $request, Server $server)
102-
{
92+
public function transfer(Request $request, Server $server) {
10393
$validatedData = $request->validate([
10494
'node_id' => 'required|exists:nodes,id',
10595
'allocation_id' => 'required|bail|unique:servers|exists:allocations,id',
@@ -116,9 +106,6 @@ public function transfer(Request $request, Server $server)
116106
// Check if the selected daemon is online.
117107
$this->daemonConfigurationRepository->setNode($node)->getSystemInformation();
118108

119-
// Suspend the server and request an archive to be created.
120-
//$this->suspensionService->toggle($server, 'suspend');
121-
122109
// Create a new ServerTransfer entry.
123110
$transfer = new ServerTransfer;
124111

app/Http/Controllers/Api/Client/Servers/WebsocketController.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,32 @@ public function __invoke(ClientApiRequest $request, Server $server)
5858
throw new HttpException(Response::HTTP_FORBIDDEN, 'You do not have permission to connect to this server\'s websocket.');
5959
}
6060

61+
$permissions = $this->permissionsService->handle($server, $user);
62+
63+
$node = null;
64+
65+
// Check if there is a transfer query param asking to connect to the target node's websocket.
66+
if ($request->query('transfer', 'false') === 'true') {
67+
// Check if the user has permissions to receive transfer logs.
68+
if (! in_array('admin.websocket.transfer', $permissions)) {
69+
throw new HttpException(Response::HTTP_FORBIDDEN, 'You do not have permission to get transfer logs');
70+
}
71+
72+
$node = $server->transfer->newNode;
73+
} else {
74+
$node = $server->node;
75+
}
76+
6177
$token = $this->jwtService
6278
->setExpiresAt(CarbonImmutable::now()->addMinutes(10))
6379
->setClaims([
6480
'user_id' => $request->user()->id,
6581
'server_uuid' => $server->uuid,
66-
'permissions' => $this->permissionsService->handle($server, $user),
82+
'permissions' => $permissions,
6783
])
68-
->handle($server->node, $user->id . $server->uuid);
84+
->handle($node, $user->id . $server->uuid);
6985

70-
$socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $server->node->getConnectionAddress());
86+
$socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $node->getConnectionAddress());
7187

7288
return new JsonResponse([
7389
'data' => [

app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,6 @@ class ServerTransferController extends Controller
5858
*/
5959
private $configurationStructureService;
6060

61-
/**
62-
* @var \Pterodactyl\Services\Servers\SuspensionService
63-
*/
64-
private $suspensionService;
65-
6661
/**
6762
* @var \Psr\Log\LoggerInterface
6863
*/
@@ -78,7 +73,6 @@ class ServerTransferController extends Controller
7873
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
7974
* @param \Pterodactyl\Repositories\Wings\DaemonTransferRepository $daemonTransferRepository
8075
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService
81-
* @param \Pterodactyl\Services\Servers\SuspensionService $suspensionService
8276
* @param \Psr\Log\LoggerInterface $writer
8377
*/
8478
public function __construct(
@@ -89,7 +83,6 @@ public function __construct(
8983
DaemonServerRepository $daemonServerRepository,
9084
DaemonTransferRepository $daemonTransferRepository,
9185
ServerConfigurationStructureService $configurationStructureService,
92-
SuspensionService $suspensionService,
9386
LoggerInterface $writer
9487
) {
9588
$this->connection = $connection;
@@ -99,7 +92,6 @@ public function __construct(
9992
$this->daemonServerRepository = $daemonServerRepository;
10093
$this->daemonTransferRepository = $daemonTransferRepository;
10194
$this->configurationStructureService = $configurationStructureService;
102-
$this->suspensionService = $suspensionService;
10395
$this->writer = $writer;
10496
}
10597

@@ -187,9 +179,6 @@ public function failure(string $uuid)
187179
// Remove the new allocations.
188180
$this->allocationRepository->updateWhereIn('id', $allocationIds, ['server_id' => null]);
189181

190-
// Unsuspend the server.
191-
//$this->suspensionService->toggle($server, 'unsuspend');
192-
193182
return new JsonResponse([], Response::HTTP_NO_CONTENT);
194183
}
195184

@@ -236,7 +225,6 @@ public function success(string $uuid)
236225

237226
// Unsuspend the server
238227
$server->load('node');
239-
//$this->suspensionService->toggle($server, $this->suspensionService::ACTION_UNSUSPEND);
240228

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

app/Models/ServerTransfer.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
* @property \Carbon\Carbon $updated_at
1717
*
1818
* @property \Pterodactyl\Models\Server $server
19+
* @property \Pterodactyl\Models\Node $oldNode
20+
* @property \Pterodactyl\Models\Node $newNode
1921
*/
2022
class ServerTransfer extends Model
2123
{
@@ -78,4 +80,24 @@ public function server()
7880
{
7981
return $this->belongsTo(Server::class);
8082
}
83+
84+
/**
85+
* Gets the source node associated with a server transfer.
86+
*
87+
* @return \Illuminate\Database\Eloquent\Relations\HasOne
88+
*/
89+
public function oldNode()
90+
{
91+
return $this->hasOne(Node::class, 'id', 'old_node');
92+
}
93+
94+
/**
95+
* Gets the target node associated with a server transfer.
96+
*
97+
* @return \Illuminate\Database\Eloquent\Relations\HasOne
98+
*/
99+
public function newNode()
100+
{
101+
return $this->hasOne(Node::class, 'id', 'new_node');
102+
}
81103
}

app/Services/Servers/SuspensionService.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Pterodactyl\Models\Server;
77
use Illuminate\Database\ConnectionInterface;
88
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
9+
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
910

1011
class SuspensionService
1112
{
@@ -56,6 +57,11 @@ public function toggle(Server $server, $action = self::ACTION_SUSPEND)
5657
return;
5758
}
5859

60+
// Check if the server is currently being transferred.
61+
if ($server->transfer !== null) {
62+
throw new ConflictHttpException('Server is currently being transferred');
63+
}
64+
5965
$this->connection->transaction(function () use ($action, $server) {
6066
$server->update([
6167
'suspended' => $action === self::ACTION_SUSPEND,

resources/scripts/api/server/getWebsocketToken.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ interface Response {
55
socket: string;
66
}
77

8-
export default (server: string): Promise<Response> => {
8+
export default (server: string, transfer: boolean): Promise<Response> => {
99
return new Promise((resolve, reject) => {
10-
http.get(`/api/client/servers/${server}/websocket`)
10+
http.get(`/api/client/servers/${server}/websocket`, {
11+
params: {
12+
transfer,
13+
},
14+
})
1115
.then(({ data }) => resolve({
1216
token: data.data.token,
1317
socket: data.data.socket,

resources/scripts/components/server/Console.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ export default () => {
8484
// Sent by the source node whenever the server was archived successfully.
8585
case 'archive':
8686
terminal.writeln(TERMINAL_PRELUDE + 'Server has been archived successfully, attempting connection to target node..\u001b[0m');
87-
// TODO: Get WebSocket information for the target node.
88-
return;
8987
}
9088
};
9189

@@ -167,7 +165,7 @@ export default () => {
167165

168166
useEffect(() => {
169167
if (connected && instance) {
170-
terminal.clear();
168+
// terminal.clear();
171169

172170
instance.addListener('status', handlePowerChangeEvent);
173171
instance.addListener('console output', handleConsoleOutput);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
2+
import { ServerContext } from '@/state/server';
3+
4+
const TransferListener = () => {
5+
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
6+
const getServer = ServerContext.useStoreActions(actions => actions.server.getServer);
7+
const setServerFromState = ServerContext.useStoreActions(actions => actions.server.setServerFromState);
8+
9+
// Listen for the installation completion event and then fire off a request to fetch the updated
10+
// server information. This allows the server to automatically become available to the user if they
11+
// just sit on the page.
12+
useWebsocketEvent('transfer status', (status: string) => {
13+
if (status === 'starting') {
14+
setServerFromState(s => ({ ...s, isTransferring: true }));
15+
return;
16+
}
17+
18+
if (status !== 'success') {
19+
return;
20+
}
21+
22+
getServer(uuid).catch(error => console.error(error));
23+
});
24+
25+
return null;
26+
};
27+
28+
export default TransferListener;

resources/scripts/components/server/WebsocketHandler.tsx

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,14 @@ const reconnectErrors = [
1515
export default () => {
1616
let updatingToken = false;
1717
const [ error, setError ] = useState<'connecting' | string>('');
18+
const [ transfer, setTransfer ] = useState<boolean>(false);
1819
const { connected, instance } = ServerContext.useStoreState(state => state.socket);
1920
const uuid = ServerContext.useStoreState(state => state.server.data?.uuid);
2021
const setServerStatus = ServerContext.useStoreActions(actions => actions.status.setServerStatus);
2122
const { setInstance, setConnectionState } = ServerContext.useStoreActions(actions => actions.socket);
2223

23-
const updateToken = (uuid: string, socket: Websocket) => {
24-
if (updatingToken) return;
25-
26-
updatingToken = true;
27-
getWebsocketToken(uuid)
28-
.then(data => socket.setToken(data.token, true))
29-
.catch(error => console.error(error))
30-
.then(() => {
31-
updatingToken = false;
32-
});
33-
};
34-
35-
useEffect(() => {
36-
connected && setError('');
37-
}, [ connected ]);
38-
39-
useEffect(() => {
40-
return () => {
41-
instance && instance.close();
42-
};
43-
}, [ instance ]);
44-
45-
useEffect(() => {
46-
// If there is already an instance or there is no server, just exit out of this process
47-
// since we don't need to make a new connection.
48-
if (instance || !uuid) {
49-
return;
50-
}
24+
const connect = (uuid: string, transfer = false) => {
25+
setTransfer(transfer);
5126

5227
const socket = new Websocket();
5328

@@ -76,7 +51,34 @@ export default () => {
7651
}
7752
});
7853

79-
getWebsocketToken(uuid)
54+
socket.on('transfer status', (status: string) => {
55+
if (status === 'success') {
56+
setTransfer(false);
57+
return;
58+
}
59+
60+
if (status === 'starting') {
61+
return;
62+
}
63+
64+
// This doesn't use the `setTransfer` hook as it doesn't want to work properly in this context,
65+
// and causes all kinds of fuckery with the websocket.
66+
let transfer = false;
67+
if (status === 'archived') {
68+
transfer = true;
69+
}
70+
71+
// Close the current websocket connection.
72+
socket.close();
73+
74+
setError('connecting');
75+
setConnectionState(false);
76+
setInstance(null);
77+
78+
connect(uuid, transfer);
79+
});
80+
81+
getWebsocketToken(uuid, transfer)
8082
.then(data => {
8183
// Connect and then set the authentication token.
8284
socket.setToken(data.token).connect(data.socket);
@@ -85,6 +87,38 @@ export default () => {
8587
setInstance(socket);
8688
})
8789
.catch(error => console.error(error));
90+
};
91+
92+
const updateToken = (uuid: string, socket: Websocket) => {
93+
if (updatingToken) return;
94+
95+
updatingToken = true;
96+
getWebsocketToken(uuid, transfer)
97+
.then(data => socket.setToken(data.token, true))
98+
.catch(error => console.error(error))
99+
.then(() => {
100+
updatingToken = false;
101+
});
102+
};
103+
104+
useEffect(() => {
105+
connected && setError('');
106+
}, [ connected ]);
107+
108+
useEffect(() => {
109+
return () => {
110+
instance && instance.close();
111+
};
112+
}, [ instance ]);
113+
114+
useEffect(() => {
115+
// If there is already an instance or there is no server, just exit out of this process
116+
// since we don't need to make a new connection.
117+
if (instance || !uuid) {
118+
return;
119+
}
120+
121+
connect(uuid);
88122
}, [ uuid ]);
89123

90124
return (

resources/scripts/components/server/databases/CreateDatabaseButton.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export default () => {
4242
setVisible(false);
4343
})
4444
.catch(error => {
45-
console.log(error);
4645
addError({ key: 'database:create', message: httpErrorToHuman(error) });
4746
setSubmitting(false);
4847
});

0 commit comments

Comments
 (0)