Improve logic for logging into the websocket of the target node

This commit is contained in:
Matthew Penner 2020-12-16 18:54:01 -07:00
parent 5c5e2e24f1
commit 01926e2896
6 changed files with 63 additions and 31 deletions

View file

@ -61,18 +61,21 @@ class WebsocketController extends ClientApiController
$permissions = $this->permissionsService->handle($server, $user); $permissions = $this->permissionsService->handle($server, $user);
$node = null; $node = null;
if ($server->transfer !== null) {
// Check if there is a transfer query param asking to connect to the target node's websocket.
if ($request->query('transfer', 'false') === 'true') {
// Check if the user has permissions to receive transfer logs. // Check if the user has permissions to receive transfer logs.
if (! in_array('admin.websocket.transfer', $permissions)) { if (! in_array('admin.websocket.transfer', $permissions)) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'You do not have permission to get transfer logs'); throw new HttpException(Response::HTTP_FORBIDDEN, 'You do not have permission to view transfer logs');
} }
// Redirect the websocket request to the new node if the server has been archived.
if ($server->transfer->archived) {
$node = $server->transfer->newNode; $node = $server->transfer->newNode;
} else { } else {
$node = $server->node; $node = $server->node;
} }
} else {
$node = $server->node;
}
$token = $this->jwtService $token = $this->jwtService
->setExpiresAt(CarbonImmutable::now()->addMinutes(10)) ->setExpiresAt(CarbonImmutable::now()->addMinutes(10))

View file

@ -112,7 +112,6 @@ class ServerTransferController extends Controller
// Unsuspend the server and don't continue the transfer. // Unsuspend the server and don't continue the transfer.
if (! $request->input('successful')) { if (! $request->input('successful')) {
//$this->suspensionService->toggle($server, 'unsuspend');
$server->transfer->forceFill([ $server->transfer->forceFill([
'successful' => false, 'successful' => false,
])->saveOrFail(); ])->saveOrFail();
@ -142,6 +141,12 @@ class ServerTransferController extends Controller
->relatedTo($server->uuid, true) ->relatedTo($server->uuid, true)
->getToken($signer, new Key($server->node->getDecryptedKey())); ->getToken($signer, new Key($server->node->getDecryptedKey()));
// Update the archived field on the transfer to make clients connect to the websocket
// on the new node to be able to receive transfer logs.
$server->transfer->forceFill([
'archived' => true,
])->saveOrFail();
// On the daemon transfer repository, make sure to set the node after the server // On the daemon transfer repository, make sure to set the node after the server
// because setServer() tells the repository to use the server's node and not the one // because setServer() tells the repository to use the server's node and not the one
// we want to specify. // we want to specify.

View file

@ -12,6 +12,7 @@ namespace Pterodactyl\Models;
* @property string $old_additional_allocations * @property string $old_additional_allocations
* @property string $new_additional_allocations * @property string $new_additional_allocations
* @property bool|null $successful * @property bool|null $successful
* @property bool $archived
* @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at * @property \Carbon\Carbon $updated_at
* *
@ -55,6 +56,7 @@ class ServerTransfer extends Model
'old_additional_allocations' => 'string', 'old_additional_allocations' => 'string',
'new_additional_allocations' => 'string', 'new_additional_allocations' => 'string',
'successful' => 'bool', 'successful' => 'bool',
'archived' => 'bool',
]; ];
/** /**

View file

@ -0,0 +1,38 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddArchivedFieldToServerTransfersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('server_transfers', function (Blueprint $table) {
$table->boolean('archived')->default(0)->after('new_additional_allocations');
});
// Update archived to all be true on existing transfers.
Schema::table('server_transfers', function (Blueprint $table) {
DB::statement('UPDATE `server_transfers` SET `archived` = 1 WHERE `successful` = 1');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('server_transfers', function (Blueprint $table) {
$table->dropColumn('archived');
});
}
}

View file

@ -5,13 +5,9 @@ interface Response {
socket: string; socket: string;
} }
export default (server: string, transfer: boolean): Promise<Response> => { export default (server: string): Promise<Response> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${server}/websocket`, { http.get(`/api/client/servers/${server}/websocket`)
params: {
transfer,
},
})
.then(({ data }) => resolve({ .then(({ data }) => resolve({
token: data.data.token, token: data.data.token,
socket: data.data.socket, socket: data.data.socket,

View file

@ -15,15 +15,12 @@ const reconnectErrors = [
export default () => { export default () => {
let updatingToken = false; let updatingToken = false;
const [ error, setError ] = useState<'connecting' | string>(''); const [ error, setError ] = useState<'connecting' | string>('');
const [ transfer, setTransfer ] = useState<boolean>(false);
const { connected, instance } = ServerContext.useStoreState(state => state.socket); const { connected, instance } = ServerContext.useStoreState(state => state.socket);
const uuid = ServerContext.useStoreState(state => state.server.data?.uuid); const uuid = ServerContext.useStoreState(state => state.server.data?.uuid);
const setServerStatus = ServerContext.useStoreActions(actions => actions.status.setServerStatus); const setServerStatus = ServerContext.useStoreActions(actions => actions.status.setServerStatus);
const { setInstance, setConnectionState } = ServerContext.useStoreActions(actions => actions.socket); const { setInstance, setConnectionState } = ServerContext.useStoreActions(actions => actions.socket);
const connect = (uuid: string, transfer = false) => { const connect = (uuid: string) => {
setTransfer(transfer);
const socket = new Websocket(); const socket = new Websocket();
socket.on('auth success', () => setConnectionState(true)); socket.on('auth success', () => setConnectionState(true));
@ -52,21 +49,12 @@ export default () => {
}); });
socket.on('transfer status', (status: string) => { socket.on('transfer status', (status: string) => {
if (status === 'success') { if (status === 'starting' || status === 'success') {
setTransfer(false);
return; return;
} }
if (status === 'starting') { // Force a reconnection to the websocket which will connect us
return; // to the target node instead of the source node.
}
// This doesn't use the `setTransfer` hook as it doesn't want to work properly in this context,
// and causes all kinds of fuckery with the websocket.
let transfer = false;
if (status === 'archived') {
transfer = true;
}
// Close the current websocket connection. // Close the current websocket connection.
socket.close(); socket.close();
@ -75,10 +63,10 @@ export default () => {
setConnectionState(false); setConnectionState(false);
setInstance(null); setInstance(null);
connect(uuid, transfer); connect(uuid);
}); });
getWebsocketToken(uuid, transfer) getWebsocketToken(uuid)
.then(data => { .then(data => {
// Connect and then set the authentication token. // Connect and then set the authentication token.
socket.setToken(data.token).connect(data.socket); socket.setToken(data.token).connect(data.socket);
@ -93,7 +81,7 @@ export default () => {
if (updatingToken) return; if (updatingToken) return;
updatingToken = true; updatingToken = true;
getWebsocketToken(uuid, transfer) getWebsocketToken(uuid)
.then(data => socket.setToken(data.token, true)) .then(data => socket.setToken(data.token, true))
.catch(error => console.error(error)) .catch(error => console.error(error))
.then(() => { .then(() => {