2020-04-04 00:50:06 -06:00
< ? php
2024-03-12 22:39:16 -04:00
namespace App\Http\Controllers\Api\Remote\Servers ;
2020-04-04 00:50:06 -06:00
2026-04-20 17:25:54 +02:00
use App\Exceptions\Http\HttpForbiddenException ;
2025-09-24 13:34:19 +02:00
use App\Http\Controllers\Controller ;
use App\Models\Allocation ;
2026-04-20 17:25:54 +02:00
use App\Models\Node ;
2024-03-16 14:35:13 -04:00
use App\Models\Server ;
2024-03-16 15:01:21 -04:00
use App\Repositories\Daemon\DaemonServerRepository ;
2020-04-04 16:24:58 -06:00
use Illuminate\Database\ConnectionInterface ;
2025-01-07 22:58:04 +01:00
use Illuminate\Http\Client\ConnectionException ;
2025-09-24 13:34:19 +02:00
use Illuminate\Http\JsonResponse ;
2026-04-20 17:25:54 +02:00
use Illuminate\Http\Request ;
2025-09-24 13:34:19 +02:00
use Illuminate\Http\Response ;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException ;
2025-09-08 13:12:33 -04:00
use Throwable ;
2026-04-20 17:25:54 +02:00
use Webmozart\Assert\Assert ;
2020-04-04 00:50:06 -06:00
class ServerTransferController extends Controller
{
/**
* ServerTransferController constructor .
*/
public function __construct (
2022-10-14 10:59:20 -06:00
private ConnectionInterface $connection ,
2024-03-16 15:01:21 -04:00
private DaemonServerRepository $daemonServerRepository ,
2024-11-22 09:27:57 +01:00
) {}
2020-04-04 00:50:06 -06:00
2020-04-04 16:16:18 -06:00
/**
* The daemon notifies us about a transfer failure .
*
2025-09-08 13:12:33 -04:00
* @ throws Throwable
2020-04-04 16:16:18 -06:00
*/
2026-04-20 17:25:54 +02:00
public function failure ( Request $request , Server $server ) : JsonResponse
2020-04-04 16:16:18 -06:00
{
2022-12-01 11:50:28 -07:00
$transfer = $server -> transfer ;
2026-07-09 09:55:11 -04:00
throw_if ( is_null ( $transfer ), new ConflictHttpException ( 'Server is not being transferred.' ));
2020-04-04 16:16:18 -06:00
2026-04-20 17:25:54 +02:00
/* @var Node $node */
Assert :: isInstanceOf ( $node = $request -> attributes -> get ( 'node' ), Node :: class );
// Either node can tell the panel that the transfer has failed. Only the new node
// can tell the panel that it was successful.
2026-07-09 09:55:11 -04:00
throw_if ( ! $node -> is ( $transfer -> newNode ) && ! $node -> is ( $transfer -> oldNode ), new HttpForbiddenException ( 'Requesting node does not have permission to access this server.' ));
2026-04-20 17:25:54 +02:00
2025-08-09 23:53:45 +02:00
$this -> connection -> transaction ( function () use ( $transfer ) {
$transfer -> forceFill ([ 'successful' => false ]) -> saveOrFail ();
if ( $transfer -> new_allocation || $transfer -> new_additional_allocations ) {
$allocations = array_merge ([ $transfer -> new_allocation ], $transfer -> new_additional_allocations );
Allocation :: query () -> whereIn ( 'id' , $allocations ) -> update ([ 'server_id' => null ]);
}
});
return new JsonResponse ([], Response :: HTTP_NO_CONTENT );
2020-04-04 16:16:18 -06:00
}
/**
* The daemon notifies us about a transfer success .
*
2025-09-08 13:12:33 -04:00
* @ throws Throwable
2020-04-04 16:16:18 -06:00
*/
2026-04-20 17:25:54 +02:00
public function success ( Request $request , Server $server ) : JsonResponse
2020-04-04 16:16:18 -06:00
{
$transfer = $server -> transfer ;
2026-07-09 09:55:11 -04:00
throw_if ( is_null ( $transfer ), new ConflictHttpException ( 'Server is not being transferred.' ));
2020-04-04 16:16:18 -06:00
2026-04-20 17:25:54 +02:00
/* @var Node $node */
Assert :: isInstanceOf ( $node = $request -> attributes -> get ( 'node' ), Node :: class );
// Only the new node communicates a successful state to the panel, so we should
// not allow the old node to hit this endpoint.
2026-07-09 09:55:11 -04:00
throw_unless ( $node -> is ( $transfer -> newNode ), new HttpForbiddenException ( 'Requesting node does not have permission to access this server.' ));
2026-04-20 17:25:54 +02:00
2025-08-09 23:53:45 +02:00
/** @var Server $server */
$server = $this -> connection -> transaction ( function () use ( $server , $transfer ) {
$data = [];
2025-06-26 01:49:43 +02:00
if ( $transfer -> old_allocation || $transfer -> old_additional_allocations ) {
$allocations = array_merge ([ $transfer -> old_allocation ], $transfer -> old_additional_allocations );
// Remove the old allocations for the server and re-assign the server to the new
// primary allocation and node.
Allocation :: query () -> whereIn ( 'id' , $allocations ) -> update ([ 'server_id' => null ]);
$data [ 'allocation_id' ] = $transfer -> new_allocation ;
}
2025-08-09 23:53:45 +02:00
2025-06-26 01:49:43 +02:00
$data [ 'node_id' ] = $transfer -> new_node ;
$server -> update ( $data );
2020-12-24 10:10:40 -08:00
$server = $server -> fresh ();
$server -> transfer -> update ([ 'successful' => true ]);
return $server ;
});
// Delete the server from the old node making sure to point it to the old node so
2021-08-18 11:58:41 -07:00
// that we do not delete it from the new node the server was transferred to.
2020-12-24 10:10:40 -08:00
try {
$this -> daemonServerRepository
-> setServer ( $server )
-> setNode ( $transfer -> oldNode )
-> delete ();
2025-01-07 22:58:04 +01:00
} catch ( ConnectionException $exception ) {
2024-03-16 23:27:24 -04:00
logger () -> warning ( $exception , [ 'transfer_id' => $server -> transfer -> id ]);
2020-12-24 10:10:40 -08:00
}
2020-04-04 16:16:18 -06:00
2020-12-24 10:10:40 -08:00
return new JsonResponse ([], Response :: HTTP_NO_CONTENT );
}
2020-04-04 00:50:06 -06:00
}