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,15 +61,18 @@ class WebsocketController extends ClientApiController
$permissions = $this->permissionsService->handle($server, $user);
$node = null;
// Check if there is a transfer query param asking to connect to the target node's websocket.
if ($request->query('transfer', 'false') === 'true') {
if ($server->transfer !== null) {
// Check if the user has permissions to receive transfer logs.
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');
}
$node = $server->transfer->newNode;
// Redirect the websocket request to the new node if the server has been archived.
if ($server->transfer->archived) {
$node = $server->transfer->newNode;
} else {
$node = $server->node;
}
} else {
$node = $server->node;
}

View file

@ -112,7 +112,6 @@ class ServerTransferController extends Controller
// Unsuspend the server and don't continue the transfer.
if (! $request->input('successful')) {
//$this->suspensionService->toggle($server, 'unsuspend');
$server->transfer->forceFill([
'successful' => false,
])->saveOrFail();
@ -142,6 +141,12 @@ class ServerTransferController extends Controller
->relatedTo($server->uuid, true)
->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
// because setServer() tells the repository to use the server's node and not the one
// we want to specify.

View file

@ -12,6 +12,7 @@ namespace Pterodactyl\Models;
* @property string $old_additional_allocations
* @property string $new_additional_allocations
* @property bool|null $successful
* @property bool $archived
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
*
@ -55,6 +56,7 @@ class ServerTransfer extends Model
'old_additional_allocations' => 'string',
'new_additional_allocations' => 'string',
'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;
}
export default (server: string, transfer: boolean): Promise<Response> => {
export default (server: string): Promise<Response> => {
return new Promise((resolve, reject) => {
http.get(`/api/client/servers/${server}/websocket`, {
params: {
transfer,
},
})
http.get(`/api/client/servers/${server}/websocket`)
.then(({ data }) => resolve({
token: data.data.token,
socket: data.data.socket,

View file

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