Merge branch 'develop' into enhancement/new-server-admin
This commit is contained in:
commit
ea8f7bb5b6
17 changed files with 178 additions and 799 deletions
1
.php_cs
1
.php_cs
|
@ -47,6 +47,7 @@ return PhpCsFixer\Config::create()
|
||||||
'psr4' => true,
|
'psr4' => true,
|
||||||
'random_api_migration' => true,
|
'random_api_migration' => true,
|
||||||
'single_line_throw' => false,
|
'single_line_throw' => false,
|
||||||
|
'single_trait_insert_per_statement' => false,
|
||||||
'standardize_not_equals' => true,
|
'standardize_not_equals' => true,
|
||||||
'ternary_to_null_coalescing' => true,
|
'ternary_to_null_coalescing' => true,
|
||||||
'yoda_style' => [
|
'yoda_style' => [
|
||||||
|
|
|
@ -5,14 +5,14 @@ namespace Pterodactyl\Console\Commands\Server;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use GuzzleHttp\Exception\RequestException;
|
use GuzzleHttp\Exception\RequestException;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use Pterodactyl\Repositories\Daemon\PowerRepository;
|
|
||||||
use Illuminate\Validation\Factory as ValidatorFactory;
|
use Illuminate\Validation\Factory as ValidatorFactory;
|
||||||
|
use Pterodactyl\Repositories\Wings\DaemonPowerRepository;
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||||
|
|
||||||
class BulkPowerActionCommand extends Command
|
class BulkPowerActionCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface
|
* @var \Pterodactyl\Repositories\Wings\DaemonPowerRepository
|
||||||
*/
|
*/
|
||||||
private $powerRepository;
|
private $powerRepository;
|
||||||
|
|
||||||
|
@ -42,27 +42,26 @@ class BulkPowerActionCommand extends Command
|
||||||
/**
|
/**
|
||||||
* BulkPowerActionCommand constructor.
|
* BulkPowerActionCommand constructor.
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Repositories\Daemon\PowerRepository $powerRepository
|
* @param \Pterodactyl\Repositories\Wings\DaemonPowerRepository $powerRepository
|
||||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||||
* @param \Illuminate\Validation\Factory $validator
|
* @param \Illuminate\Validation\Factory $validator
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
PowerRepository $powerRepository,
|
DaemonPowerRepository $powerRepository,
|
||||||
ServerRepositoryInterface $repository,
|
ServerRepositoryInterface $repository,
|
||||||
ValidatorFactory $validator
|
ValidatorFactory $validator
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->powerRepository = $powerRepository;
|
|
||||||
$this->repository = $repository;
|
$this->repository = $repository;
|
||||||
$this->validator = $validator;
|
$this->validator = $validator;
|
||||||
|
$this->powerRepository = $powerRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the bulk power request.
|
* Handle the bulk power request.
|
||||||
*
|
*
|
||||||
* @throws \Illuminate\Validation\ValidationException
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -105,7 +104,7 @@ class BulkPowerActionCommand extends Command
|
||||||
$this->powerRepository
|
$this->powerRepository
|
||||||
->setNode($server->node)
|
->setNode($server->node)
|
||||||
->setServer($server)
|
->setServer($server)
|
||||||
->sendSignal($action);
|
->send($action);
|
||||||
} catch (RequestException $exception) {
|
} catch (RequestException $exception) {
|
||||||
$this->output->error(trans('command/messages.server.power.action_failed', [
|
$this->output->error(trans('command/messages.server.power.action_failed', [
|
||||||
'name' => $server->name,
|
'name' => $server->name,
|
||||||
|
|
|
@ -12,8 +12,8 @@ namespace Pterodactyl\Console\Commands\Server;
|
||||||
use Webmozart\Assert\Assert;
|
use Webmozart\Assert\Assert;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use GuzzleHttp\Exception\RequestException;
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
|
||||||
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
|
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
|
||||||
|
|
||||||
class BulkReinstallActionCommand extends Command
|
class BulkReinstallActionCommand extends Command
|
||||||
|
@ -21,23 +21,23 @@ class BulkReinstallActionCommand extends Command
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
|
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
|
||||||
*/
|
*/
|
||||||
protected $configurationStructureService;
|
private $configurationStructureService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
|
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
|
||||||
*/
|
*/
|
||||||
protected $daemonRepository;
|
private $daemonRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
|
||||||
|
*/
|
||||||
|
private $repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $description = 'Reinstall a single server, all servers on a node, or all servers on the panel.';
|
protected $description = 'Reinstall a single server, all servers on a node, or all servers on the panel.';
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
|
|
||||||
*/
|
|
||||||
protected $repository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
@ -50,12 +50,12 @@ class BulkReinstallActionCommand extends Command
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonRepository
|
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonRepository
|
||||||
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService
|
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService
|
||||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
DaemonServerRepository $daemonRepository,
|
DaemonServerRepository $daemonRepository,
|
||||||
ServerConfigurationStructureService $configurationStructureService,
|
ServerConfigurationStructureService $configurationStructureService,
|
||||||
ServerRepositoryInterface $repository
|
ServerRepository $repository
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class BulkReinstallActionCommand extends Command
|
||||||
/**
|
/**
|
||||||
* Return the servers to be reinstalled.
|
* Return the servers to be reinstalled.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Database\Eloquent\Collection
|
* @return \Illuminate\Support\Collection
|
||||||
*/
|
*/
|
||||||
private function getServersToProcess()
|
private function getServersToProcess()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Contracts\Repository\Daemon;
|
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use Pterodactyl\Models\Node;
|
|
||||||
use Pterodactyl\Models\Server;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
interface BaseRepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Set the node model to be used for this daemon connection.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Models\Node $node
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setNode(Node $node);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the node model being used.
|
|
||||||
*
|
|
||||||
* @return \Pterodactyl\Models\Node|null
|
|
||||||
*/
|
|
||||||
public function getNode();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the Server model to use when requesting information from the Daemon.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Models\Server $server
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setServer(Server $server);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the Server model.
|
|
||||||
*
|
|
||||||
* @return \Pterodactyl\Models\Server|null
|
|
||||||
*/
|
|
||||||
public function getServer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the token to be used in the X-Access-Token header for requests to the daemon.
|
|
||||||
*
|
|
||||||
* @param string $token
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setToken(string $token);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the access token being used for requests.
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getToken();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an instance of the Guzzle HTTP Client to be used for requests.
|
|
||||||
*
|
|
||||||
* @param array $headers
|
|
||||||
* @return \GuzzleHttp\Client
|
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function getHttpClient(array $headers = []): Client;
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Contracts\Repository\Daemon;
|
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
interface CommandRepositoryInterface extends BaseRepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Send a command to a server.
|
|
||||||
*
|
|
||||||
* @param string $command
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function send(string $command): ResponseInterface;
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Contracts\Repository\Daemon;
|
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
interface ConfigurationRepositoryInterface extends BaseRepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Update the configuration details for the specified node using data from the database.
|
|
||||||
*
|
|
||||||
* @param array $overrides
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function update(array $overrides = []): ResponseInterface;
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Contracts\Repository\Daemon;
|
|
||||||
|
|
||||||
use stdClass;
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
interface FileRepositoryInterface extends BaseRepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return stat information for a given file.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @return \stdClass
|
|
||||||
*
|
|
||||||
* @throws \GuzzleHttp\Exception\TransferException
|
|
||||||
*/
|
|
||||||
public function getFileStat(string $path): stdClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the contents of a given file if it can be edited in the Panel.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @param int|null $notLargerThan
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getContent(string $path, int $notLargerThan = null): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save new contents to a given file.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @param string $content
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*
|
|
||||||
* @throws \GuzzleHttp\Exception\TransferException
|
|
||||||
*/
|
|
||||||
public function putContent(string $path, string $content): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a directory listing for a given path.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @throws \GuzzleHttp\Exception\TransferException
|
|
||||||
*/
|
|
||||||
public function getDirectory(string $path): array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new directory for the server in the given $path.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $path
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function createDirectory(string $name, string $path): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renames or moves a file on the remote machine.
|
|
||||||
*
|
|
||||||
* @param string $from
|
|
||||||
* @param string $to
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function renameFile(string $from, string $to): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy a given file and give it a unique name.
|
|
||||||
*
|
|
||||||
* @param string $location
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function copyFile(string $location): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a file or folder for the server.
|
|
||||||
*
|
|
||||||
* @param string $location
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function deleteFile(string $location): ResponseInterface;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Contracts\Repository\Daemon;
|
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
interface PowerRepositoryInterface extends BaseRepositoryInterface
|
|
||||||
{
|
|
||||||
const SIGNAL_START = 'start';
|
|
||||||
const SIGNAL_STOP = 'stop';
|
|
||||||
const SIGNAL_RESTART = 'restart';
|
|
||||||
const SIGNAL_KILL = 'kill';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a power signal to a server.
|
|
||||||
*
|
|
||||||
* @param string $signal
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException
|
|
||||||
*/
|
|
||||||
public function sendSignal(string $signal): ResponseInterface;
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Contracts\Repository\Daemon;
|
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
interface ServerRepositoryInterface extends BaseRepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a new server on the daemon for the panel.
|
|
||||||
*
|
|
||||||
* @param array $structure
|
|
||||||
* @param array $overrides
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*
|
|
||||||
* @throws \GuzzleHttp\Exception\RequestException
|
|
||||||
*/
|
|
||||||
public function create(array $structure, array $overrides = []): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update server details on the daemon.
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function update(array $data): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark a server to be reinstalled on the system.
|
|
||||||
*
|
|
||||||
* @param array|null $data
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function reinstall(array $data = null): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark a server as needing a container rebuild the next time the server is booted.
|
|
||||||
*
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function rebuild(): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Suspend a server on the daemon.
|
|
||||||
*
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function suspend(): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Un-suspend a server on the daemon.
|
|
||||||
*
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function unsuspend(): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a server on the daemon.
|
|
||||||
*
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function delete(): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return details on a specific server.
|
|
||||||
*
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*/
|
|
||||||
public function details(): ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Revoke an access key on the daemon before the time is expired.
|
|
||||||
*
|
|
||||||
* @param string|array $key
|
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
|
||||||
*
|
|
||||||
* @throws \GuzzleHttp\Exception\RequestException
|
|
||||||
*/
|
|
||||||
public function revokeAccessKey($key): ResponseInterface;
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
* Pterodactyl - Panel
|
|
||||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Controllers\Api\Remote;
|
|
||||||
|
|
||||||
use Spatie\Fractal\Fractal;
|
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use Pterodactyl\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
|
||||||
use Illuminate\Foundation\Testing\HttpException;
|
|
||||||
use League\Fractal\Serializer\JsonApiSerializer;
|
|
||||||
use Pterodactyl\Transformers\Daemon\ApiKeyTransformer;
|
|
||||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
||||||
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
|
|
||||||
|
|
||||||
class ValidateKeyController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Illuminate\Contracts\Foundation\Application
|
|
||||||
*/
|
|
||||||
protected $app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $daemonKeyRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Spatie\Fractal\Fractal
|
|
||||||
*/
|
|
||||||
protected $fractal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ValidateKeyController constructor.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $daemonKeyRepository
|
|
||||||
* @param \Spatie\Fractal\Fractal $fractal
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
Application $app,
|
|
||||||
DaemonKeyRepositoryInterface $daemonKeyRepository,
|
|
||||||
Fractal $fractal
|
|
||||||
) {
|
|
||||||
$this->app = $app;
|
|
||||||
$this->daemonKeyRepository = $daemonKeyRepository;
|
|
||||||
$this->fractal = $fractal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the server(s) and permissions associated with an API key.
|
|
||||||
*
|
|
||||||
* @param string $token
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Foundation\Testing\HttpException
|
|
||||||
*/
|
|
||||||
public function index($token)
|
|
||||||
{
|
|
||||||
if (! starts_with($token, DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER)) {
|
|
||||||
throw new HttpException(Response::HTTP_NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$key = $this->daemonKeyRepository->getKeyWithServer($token);
|
|
||||||
} catch (RecordNotFoundException $exception) {
|
|
||||||
throw new NotFoundHttpException;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($key->getRelation('server')->suspended || $key->getRelation('server')->installed !== 1) {
|
|
||||||
throw new NotFoundHttpException;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->fractal->item($key, $this->app->make(ApiKeyTransformer::class), 'server')
|
|
||||||
->serializeWith(JsonApiSerializer::class)
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,33 +3,25 @@
|
||||||
namespace Pterodactyl\Jobs\Schedule;
|
namespace Pterodactyl\Jobs\Schedule;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Cake\Chronos\Chronos;
|
use Carbon\Carbon;
|
||||||
use Pterodactyl\Jobs\Job;
|
use Pterodactyl\Jobs\Job;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
use Illuminate\Container\Container;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Pterodactyl\Repositories\Eloquent\TaskRepository;
|
||||||
|
use Pterodactyl\Repositories\Wings\DaemonPowerRepository;
|
||||||
|
use Pterodactyl\Repositories\Wings\DaemonCommandRepository;
|
||||||
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\TaskRepositoryInterface;
|
||||||
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
|
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
|
||||||
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface;
|
|
||||||
|
|
||||||
class RunTaskJob extends Job implements ShouldQueue
|
class RunTaskJob extends Job implements ShouldQueue
|
||||||
{
|
{
|
||||||
use DispatchesJobs, InteractsWithQueue, SerializesModels;
|
use DispatchesJobs, InteractsWithQueue, SerializesModels;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $commandRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $powerRepository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
|
@ -41,7 +33,7 @@ class RunTaskJob extends Job implements ShouldQueue
|
||||||
public $task;
|
public $task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface
|
* @var \Pterodactyl\Repositories\Eloquent\TaskRepository
|
||||||
*/
|
*/
|
||||||
protected $taskRepository;
|
protected $taskRepository;
|
||||||
|
|
||||||
|
@ -61,28 +53,24 @@ class RunTaskJob extends Job implements ShouldQueue
|
||||||
/**
|
/**
|
||||||
* Run the job and send actions to the daemon running the server.
|
* Run the job and send actions to the daemon running the server.
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface $commandRepository
|
* @param \Pterodactyl\Repositories\Wings\DaemonCommandRepository $commandRepository
|
||||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
|
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
|
||||||
* @param \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface $powerRepository
|
* @param \Pterodactyl\Repositories\Wings\DaemonPowerRepository $powerRepository
|
||||||
* @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $taskRepository
|
* @param \Pterodactyl\Repositories\Eloquent\TaskRepository $taskRepository
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||||
*/
|
*/
|
||||||
public function handle(
|
public function handle(
|
||||||
CommandRepositoryInterface $commandRepository,
|
DaemonCommandRepository $commandRepository,
|
||||||
DaemonKeyProviderService $keyProviderService,
|
DaemonKeyProviderService $keyProviderService,
|
||||||
PowerRepositoryInterface $powerRepository,
|
DaemonPowerRepository $powerRepository,
|
||||||
TaskRepositoryInterface $taskRepository
|
TaskRepository $taskRepository
|
||||||
) {
|
) {
|
||||||
$this->commandRepository = $commandRepository;
|
|
||||||
$this->powerRepository = $powerRepository;
|
|
||||||
$this->taskRepository = $taskRepository;
|
$this->taskRepository = $taskRepository;
|
||||||
|
|
||||||
$task = $this->taskRepository->getTaskForJobProcess($this->task);
|
$task = $this->taskRepository->getTaskForJobProcess($this->task);
|
||||||
$server = $task->getRelation('server');
|
$server = $task->getRelation('server');
|
||||||
$user = $server->getRelation('user');
|
|
||||||
|
|
||||||
// Do not process a task that is not set to active.
|
// Do not process a task that is not set to active.
|
||||||
if (! $task->getRelation('schedule')->is_active) {
|
if (! $task->getRelation('schedule')->is_active) {
|
||||||
|
@ -95,14 +83,10 @@ class RunTaskJob extends Job implements ShouldQueue
|
||||||
// Perform the provided task against the daemon.
|
// Perform the provided task against the daemon.
|
||||||
switch ($task->action) {
|
switch ($task->action) {
|
||||||
case 'power':
|
case 'power':
|
||||||
$this->powerRepository->setServer($server)
|
$powerRepository->setServer($server)->send($task->payload);
|
||||||
->setToken($keyProviderService->handle($server, $user))
|
|
||||||
->sendSignal($task->payload);
|
|
||||||
break;
|
break;
|
||||||
case 'command':
|
case 'command':
|
||||||
$this->commandRepository->setServer($server)
|
$commandRepository->setServer($server)->send($task->payload);
|
||||||
->setToken($keyProviderService->handle($server, $user))
|
|
||||||
->send($task->payload);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.');
|
throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.');
|
||||||
|
@ -115,10 +99,7 @@ class RunTaskJob extends Job implements ShouldQueue
|
||||||
/**
|
/**
|
||||||
* Handle a failure while sending the action to the daemon or otherwise processing the job.
|
* Handle a failure while sending the action to the daemon or otherwise processing the job.
|
||||||
*
|
*
|
||||||
* @param null|\Exception $exception
|
* @param \Exception|null $exception
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
*/
|
||||||
public function failed(Exception $exception = null)
|
public function failed(Exception $exception = null)
|
||||||
{
|
{
|
||||||
|
@ -149,28 +130,25 @@ class RunTaskJob extends Job implements ShouldQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the parent schedule as being complete.
|
* Marks the parent schedule as being complete.
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
*/
|
||||||
private function markScheduleComplete()
|
private function markScheduleComplete()
|
||||||
{
|
{
|
||||||
$repository = app()->make(ScheduleRepositoryInterface::class);
|
Container::getInstance()
|
||||||
$repository->withoutFreshModel()->update($this->schedule, [
|
->make(ScheduleRepositoryInterface::class)
|
||||||
|
->withoutFreshModel()
|
||||||
|
->update($this->schedule, [
|
||||||
'is_processing' => false,
|
'is_processing' => false,
|
||||||
'last_run_at' => Chronos::now()->toDateTimeString(),
|
'last_run_at' => Carbon::now()->toDateTimeString(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark a specific task as no longer being queued.
|
* Mark a specific task as no longer being queued.
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
*/
|
||||||
private function markTaskNotQueued()
|
private function markTaskNotQueued()
|
||||||
{
|
{
|
||||||
$repository = app()->make(TaskRepositoryInterface::class);
|
Container::getInstance()
|
||||||
$repository->update($this->task, ['is_queued' => false]);
|
->make(TaskRepositoryInterface::class)
|
||||||
|
->update($this->task, ['is_queued' => false]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,12 @@ use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\SessionRepository;
|
use Pterodactyl\Repositories\Eloquent\SessionRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
|
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
|
||||||
use Pterodactyl\Repositories\Wings\DaemonFileRepository;
|
|
||||||
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
|
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\LocationRepository;
|
use Pterodactyl\Repositories\Eloquent\LocationRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\ScheduleRepository;
|
use Pterodactyl\Repositories\Eloquent\ScheduleRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\SettingsRepository;
|
use Pterodactyl\Repositories\Eloquent\SettingsRepository;
|
||||||
use Pterodactyl\Repositories\Wings\DaemonPowerRepository;
|
|
||||||
use Pterodactyl\Repositories\Eloquent\DaemonKeyRepository;
|
use Pterodactyl\Repositories\Eloquent\DaemonKeyRepository;
|
||||||
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
||||||
use Pterodactyl\Repositories\Wings\DaemonCommandRepository;
|
|
||||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||||
use Pterodactyl\Repositories\Eloquent\EggVariableRepository;
|
use Pterodactyl\Repositories\Eloquent\EggVariableRepository;
|
||||||
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
|
||||||
|
@ -39,18 +36,11 @@ use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface;
|
||||||
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
|
||||||
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface;
|
|
||||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository as DaemonServerRepository;
|
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
|
|
||||||
|
|
||||||
class RepositoryServiceProvider extends ServiceProvider
|
class RepositoryServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
@ -79,12 +69,5 @@ class RepositoryServiceProvider extends ServiceProvider
|
||||||
$this->app->bind(SubuserRepositoryInterface::class, SubuserRepository::class);
|
$this->app->bind(SubuserRepositoryInterface::class, SubuserRepository::class);
|
||||||
$this->app->bind(TaskRepositoryInterface::class, TaskRepository::class);
|
$this->app->bind(TaskRepositoryInterface::class, TaskRepository::class);
|
||||||
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
|
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
|
||||||
|
|
||||||
// Daemon Repositories
|
|
||||||
$this->app->bind(ConfigurationRepositoryInterface::class, DaemonConfigurationRepository::class);
|
|
||||||
$this->app->bind(CommandRepositoryInterface::class, DaemonCommandRepository::class);
|
|
||||||
$this->app->bind(DaemonServerRepositoryInterface::class, DaemonServerRepository::class);
|
|
||||||
$this->app->bind(FileRepositoryInterface::class, DaemonFileRepository::class);
|
|
||||||
$this->app->bind(PowerRepositoryInterface::class, DaemonPowerRepository::class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Repositories\Wings;
|
namespace Pterodactyl\Repositories\Wings;
|
||||||
|
|
||||||
|
use Pterodactyl\Models\Node;
|
||||||
use GuzzleHttp\Exception\TransferException;
|
use GuzzleHttp\Exception\TransferException;
|
||||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||||
|
|
||||||
|
@ -25,17 +26,19 @@ class DaemonConfigurationRepository extends DaemonRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the configuration information for a daemon.
|
* Updates the configuration information for a daemon. Updates the information for
|
||||||
|
* this instance using a passed-in model. This allows us to change plenty of information
|
||||||
|
* in the model, and still use the old, pre-update model to actually make the HTTP request.
|
||||||
*
|
*
|
||||||
* @param array $attributes
|
* @param \Pterodactyl\Models\Node $node
|
||||||
* @return \Psr\Http\Message\ResponseInterface
|
* @return \Psr\Http\Message\ResponseInterface
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||||
*/
|
*/
|
||||||
public function update(array $attributes = [])
|
public function update(?Node $node)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->getHttpClient()->post(
|
return $this->getHttpClient()->post(
|
||||||
'/api/update', array_merge($this->node->getConfiguration(), $attributes)
|
'/api/update', ['json' => $node->getConfiguration()]
|
||||||
);
|
);
|
||||||
} catch (TransferException $exception) {
|
} catch (TransferException $exception) {
|
||||||
throw new DaemonConnectionException($exception);
|
throw new DaemonConnectionException($exception);
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Services\Allocations;
|
|
||||||
|
|
||||||
use Pterodactyl\Models\Server;
|
|
||||||
use Pterodactyl\Models\Allocation;
|
|
||||||
use GuzzleHttp\Exception\RequestException;
|
|
||||||
use Illuminate\Database\ConnectionInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
|
||||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
|
||||||
use Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException;
|
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonRepositoryInterface;
|
|
||||||
|
|
||||||
class SetDefaultAllocationService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Illuminate\Database\ConnectionInterface
|
|
||||||
*/
|
|
||||||
private $connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $daemonRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $serverRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SetDefaultAllocationService constructor.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
|
|
||||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
AllocationRepositoryInterface $repository,
|
|
||||||
ConnectionInterface $connection,
|
|
||||||
DaemonRepositoryInterface $daemonRepository,
|
|
||||||
ServerRepositoryInterface $serverRepository
|
|
||||||
) {
|
|
||||||
$this->connection = $connection;
|
|
||||||
$this->daemonRepository = $daemonRepository;
|
|
||||||
$this->repository = $repository;
|
|
||||||
$this->serverRepository = $serverRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the default allocation for a server only if that allocation is currently
|
|
||||||
* assigned to the specified server.
|
|
||||||
*
|
|
||||||
* @param int|\Pterodactyl\Models\Server $server
|
|
||||||
* @param int $allocation
|
|
||||||
* @return \Pterodactyl\Models\Allocation
|
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException
|
|
||||||
*/
|
|
||||||
public function handle($server, int $allocation): Allocation
|
|
||||||
{
|
|
||||||
if (! $server instanceof Server) {
|
|
||||||
$server = $this->serverRepository->find($server);
|
|
||||||
}
|
|
||||||
|
|
||||||
$allocations = $this->repository->findWhere([['server_id', '=', $server->id]]);
|
|
||||||
$model = $allocations->filter(function ($model) use ($allocation) {
|
|
||||||
return $model->id === $allocation;
|
|
||||||
})->first();
|
|
||||||
|
|
||||||
if (! $model instanceof Allocation) {
|
|
||||||
throw new AllocationDoesNotBelongToServerException;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->connection->beginTransaction();
|
|
||||||
$this->serverRepository->withoutFreshModel()->update($server->id, ['allocation_id' => $model->id]);
|
|
||||||
|
|
||||||
// Update on the daemon.
|
|
||||||
try {
|
|
||||||
$this->daemonRepository->setServer($server)->update([
|
|
||||||
'build' => [
|
|
||||||
'default' => [
|
|
||||||
'ip' => $model->ip,
|
|
||||||
'port' => $model->port,
|
|
||||||
],
|
|
||||||
'ports|overwrite' => $allocations->groupBy('ip')->map(function ($item) {
|
|
||||||
return $item->pluck('port');
|
|
||||||
})->toArray(),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->connection->commit();
|
|
||||||
} catch (RequestException $exception) {
|
|
||||||
$this->connection->rollBack();
|
|
||||||
throw new DaemonConnectionException($exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $model;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Services\DaemonKeys;
|
|
||||||
|
|
||||||
use Pterodactyl\Models\User;
|
|
||||||
use GuzzleHttp\Exception\RequestException;
|
|
||||||
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
|
|
||||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepository;
|
|
||||||
|
|
||||||
class RevokeMultipleDaemonKeysService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $exceptions = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $daemonRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RevokeMultipleDaemonKeysService constructor.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
DaemonKeyRepositoryInterface $repository,
|
|
||||||
DaemonServerRepository $daemonRepository
|
|
||||||
) {
|
|
||||||
$this->daemonRepository = $daemonRepository;
|
|
||||||
$this->repository = $repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grab all of the keys that exist for a single user and delete them from all
|
|
||||||
* daemon's that they are assigned to. If connection fails, this function will
|
|
||||||
* return an error.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Models\User $user
|
|
||||||
* @param bool $ignoreConnectionErrors
|
|
||||||
*/
|
|
||||||
public function handle(User $user, bool $ignoreConnectionErrors = false)
|
|
||||||
{
|
|
||||||
$keys = $this->repository->getKeysForRevocation($user);
|
|
||||||
|
|
||||||
$keys->groupBy('node.id')->each(function ($group, $nodeId) use ($ignoreConnectionErrors) {
|
|
||||||
try {
|
|
||||||
$this->daemonRepository->setNode(collect($group)->first()->getRelation('node'))->revokeAccessKey(collect($group)->pluck('secret')->toArray());
|
|
||||||
} catch (RequestException $exception) {
|
|
||||||
if (! $ignoreConnectionErrors) {
|
|
||||||
throw new DaemonConnectionException($exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->setConnectionException($nodeId, $exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->repository->deleteKeys(collect($group)->pluck('id')->toArray());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of exceptions that were returned by the handle function.
|
|
||||||
*
|
|
||||||
* @return RequestException[]
|
|
||||||
*/
|
|
||||||
public function getExceptions()
|
|
||||||
{
|
|
||||||
return $this->exceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an exception for a node to the array.
|
|
||||||
*
|
|
||||||
* @param int $node
|
|
||||||
* @param \GuzzleHttp\Exception\RequestException $exception
|
|
||||||
*/
|
|
||||||
protected function setConnectionException(int $node, RequestException $exception)
|
|
||||||
{
|
|
||||||
$this->exceptions[$node] = $exception;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +1,17 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
|
||||||
* Pterodactyl - Panel
|
|
||||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
|
||||||
*
|
|
||||||
* This software is licensed under the terms of the MIT license.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Pterodactyl\Services\Eggs;
|
namespace Pterodactyl\Services\Eggs;
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Pterodactyl\Models\Egg;
|
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||||
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
|
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
|
||||||
|
|
||||||
class EggConfigurationService
|
class EggConfigurationService
|
||||||
{
|
{
|
||||||
|
private const NOT_MATCHED = '__no_match';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
|
||||||
*/
|
*/
|
||||||
|
@ -109,63 +103,8 @@ class EggConfigurationService
|
||||||
// can property map the egg placeholders to values.
|
// can property map the egg placeholders to values.
|
||||||
$structure = $this->configurationStructureService->handle($server, true);
|
$structure = $this->configurationStructureService->handle($server, true);
|
||||||
|
|
||||||
foreach ($configs as $file => $data) {
|
foreach ($configs as $file => &$data) {
|
||||||
foreach ($data->find ?? [] as &$value) {
|
$this->iterate($data->find, $structure);
|
||||||
preg_match('/^{{(?<key>.*)}}$/', $value, $matches);
|
|
||||||
|
|
||||||
if (! $key = $matches['key'] ?? null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matched something in {{server.X}} format, now replace that with the actual
|
|
||||||
// value from the server properties.
|
|
||||||
//
|
|
||||||
// The Daemon supports server.X, env.X, and config.X placeholders.
|
|
||||||
if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want to do anything with config keys since the Daemon will need to handle
|
|
||||||
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
|
|
||||||
// interface to proxy through, but the Panel would be unaware of that.
|
|
||||||
if (Str::startsWith($key, 'config.')) {
|
|
||||||
$value = "{{{$key}}}";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their
|
|
||||||
// respective values on the Daemon side. Ensure that anything referencing those properly
|
|
||||||
// replaces them with the matching config value.
|
|
||||||
switch ($key) {
|
|
||||||
case 'server.build.env.SERVER_MEMORY':
|
|
||||||
case 'env.SERVER_MEMORY':
|
|
||||||
$key = 'server.build.memory';
|
|
||||||
break;
|
|
||||||
case 'server.build.env.SERVER_IP':
|
|
||||||
case 'env.SERVER_IP':
|
|
||||||
$key = 'server.build.default.ip';
|
|
||||||
break;
|
|
||||||
case 'server.build.env.SERVER_PORT':
|
|
||||||
case 'env.SERVER_PORT':
|
|
||||||
$key = 'server.build.default.port';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace anything starting with "server." with the value out of the server configuration
|
|
||||||
// array that used to be created for the old daemon.
|
|
||||||
if (Str::startsWith($key, 'server.')) {
|
|
||||||
$value = Arr::get(
|
|
||||||
$structure, preg_replace('/^server\./', '', $key), ''
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, replace anything starting with env. with the expected environment
|
|
||||||
// variable from the server configuration.
|
|
||||||
$value = Arr::get(
|
|
||||||
$structure, preg_replace('/^env\./', 'build.env.', $key), ''
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = [];
|
$response = [];
|
||||||
|
@ -194,4 +133,99 @@ class EggConfigurationService
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $value
|
||||||
|
* @param array $structure
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function matchAndReplaceKeys(string $value, array $structure): ?string
|
||||||
|
{
|
||||||
|
preg_match('/{{(?<key>.*)}}/', $value, $matches);
|
||||||
|
|
||||||
|
if (! $key = $matches['key'] ?? null) {
|
||||||
|
return self::NOT_MATCHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matched something in {{server.X}} format, now replace that with the actual
|
||||||
|
// value from the server properties.
|
||||||
|
//
|
||||||
|
// The Daemon supports server.X, env.X, and config.X placeholders.
|
||||||
|
if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) {
|
||||||
|
return self::NOT_MATCHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want to do anything with config keys since the Daemon will need to handle
|
||||||
|
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
|
||||||
|
// interface to proxy through, but the Panel would be unaware of that.
|
||||||
|
if (Str::startsWith($key, 'config.')) {
|
||||||
|
return "{{{$key}}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their
|
||||||
|
// respective values on the Daemon side. Ensure that anything referencing those properly
|
||||||
|
// replaces them with the matching config value.
|
||||||
|
switch ($key) {
|
||||||
|
case 'server.build.env.SERVER_MEMORY':
|
||||||
|
case 'env.SERVER_MEMORY':
|
||||||
|
$key = 'server.build.memory';
|
||||||
|
break;
|
||||||
|
case 'server.build.env.SERVER_IP':
|
||||||
|
case 'env.SERVER_IP':
|
||||||
|
$key = 'server.build.default.ip';
|
||||||
|
break;
|
||||||
|
case 'server.build.env.SERVER_PORT':
|
||||||
|
case 'env.SERVER_PORT':
|
||||||
|
$key = 'server.build.default.port';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace anything starting with "server." with the value out of the server configuration
|
||||||
|
// array that used to be created for the old daemon.
|
||||||
|
if (Str::startsWith($key, 'server.')) {
|
||||||
|
$plucked = Arr::get(
|
||||||
|
$structure, preg_replace('/^server\./', '', $key), ''
|
||||||
|
);
|
||||||
|
|
||||||
|
return preg_replace('/{{(.*)}}/', $plucked, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, replace anything starting with env. with the expected environment
|
||||||
|
// variable from the server configuration.
|
||||||
|
$plucked = Arr::get(
|
||||||
|
$structure, preg_replace('/^env\./', 'build.env.', $key), ''
|
||||||
|
);
|
||||||
|
|
||||||
|
return preg_replace('/{{(.*)}}/', $plucked, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over a set of "find" values for a given file in the parser configuration. If
|
||||||
|
* the value of the line match is something iterable, continue iterating, otherwise perform
|
||||||
|
* a match & replace.
|
||||||
|
*
|
||||||
|
* @param mixed $data
|
||||||
|
* @param array $structure
|
||||||
|
*/
|
||||||
|
private function iterate(&$data, array $structure)
|
||||||
|
{
|
||||||
|
if (! is_iterable($data) && ! is_object($data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($data as &$value) {
|
||||||
|
if (is_iterable($value) || is_object($value)) {
|
||||||
|
$this->iterate($value, $structure);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->matchAndReplaceKeys($value, $structure);
|
||||||
|
if ($response === self::NOT_MATCHED) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ namespace Pterodactyl\Services\Nodes;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Pterodactyl\Models\Node;
|
use Pterodactyl\Models\Node;
|
||||||
use GuzzleHttp\Exception\ConnectException;
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
use GuzzleHttp\Exception\RequestException;
|
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
||||||
|
use Pterodactyl\Repositories\Daemon\ConfigurationRepository;
|
||||||
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
||||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||||
use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
||||||
|
@ -20,11 +20,6 @@ class NodeUpdateService
|
||||||
*/
|
*/
|
||||||
private $connection;
|
private $connection;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository
|
* @var \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository
|
||||||
*/
|
*/
|
||||||
|
@ -35,24 +30,29 @@ class NodeUpdateService
|
||||||
*/
|
*/
|
||||||
private $encrypter;
|
private $encrypter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
|
||||||
|
*/
|
||||||
|
private $repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateService constructor.
|
* UpdateService constructor.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||||
* @param \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository $configurationRepository
|
* @param \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository $configurationRepository
|
||||||
* @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository
|
* @param \Pterodactyl\Repositories\Eloquent\NodeRepository $repository
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ConnectionInterface $connection,
|
ConnectionInterface $connection,
|
||||||
Encrypter $encrypter,
|
Encrypter $encrypter,
|
||||||
DaemonConfigurationRepository $configurationRepository,
|
DaemonConfigurationRepository $configurationRepository,
|
||||||
NodeRepositoryInterface $repository
|
NodeRepository $repository
|
||||||
) {
|
) {
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
$this->repository = $repository;
|
|
||||||
$this->configurationRepository = $configurationRepository;
|
$this->configurationRepository = $configurationRepository;
|
||||||
$this->encrypter = $encrypter;
|
$this->encrypter = $encrypter;
|
||||||
|
$this->repository = $repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,55 +63,36 @@ class NodeUpdateService
|
||||||
* @param bool $resetToken
|
* @param bool $resetToken
|
||||||
*
|
*
|
||||||
* @return \Pterodactyl\Models\Node
|
* @return \Pterodactyl\Models\Node
|
||||||
*
|
* @throws \Throwable
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException
|
|
||||||
*/
|
*/
|
||||||
public function handle(Node $node, array $data, bool $resetToken = false)
|
public function handle(Node $node, array $data, bool $resetToken = false)
|
||||||
{
|
{
|
||||||
if ($resetToken) {
|
if ($resetToken) {
|
||||||
$data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH);
|
$data['daemon_token'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH));
|
||||||
$data['daemon_token_id'] = $this->encrypter->encrypt(
|
$data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH);
|
||||||
Str::random(Node::DAEMON_TOKEN_ID_LENGTH)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->connection->beginTransaction();
|
[$updated, $exception] = $this->connection->transaction(function () use ($data, $node) {
|
||||||
|
/** @var \Pterodactyl\Models\Node $updated */
|
||||||
/** @var \Pterodactyl\Models\Node $updatedModel */
|
$updated = $this->repository->withFreshModel()->update($node->id, $data, true, true);
|
||||||
$updatedModel = $this->repository->update($node->id, $data);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($resetToken) {
|
$this->configurationRepository->setNode($node)->update($updated);
|
||||||
// We need to clone the new model and set it's authentication token to be the
|
} catch (DaemonConnectionException $exception) {
|
||||||
// old one so we can connect. Then we will pass the new token through as an
|
if (! is_null($exception->getPrevious()) && $exception->getPrevious() instanceof ConnectException) {
|
||||||
// override on the call.
|
return [$updated, true];
|
||||||
$cloned = $updatedModel->replicate(['daemon_token']);
|
|
||||||
$cloned->setAttribute('daemon_token', $node->getAttribute('daemon_token'));
|
|
||||||
|
|
||||||
$this->configurationRepository->setNode($cloned)->update([
|
|
||||||
'daemon_token_id' => $updatedModel->daemon_token_id,
|
|
||||||
'daemon_token' => $updatedModel->getDecryptedKey(),
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$this->configurationRepository->setNode($updatedModel)->update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->connection->commit();
|
throw $exception;
|
||||||
} catch (RequestException $exception) {
|
}
|
||||||
// Failed to connect to the Daemon. Let's go ahead and save the configuration
|
|
||||||
// and let the user know they'll need to manually update.
|
|
||||||
if ($exception instanceof ConnectException) {
|
|
||||||
$this->connection->commit();
|
|
||||||
|
|
||||||
|
return [$updated, false];
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($exception) {
|
||||||
throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated'));
|
throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated'));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new DaemonConnectionException($exception);
|
return $updated;
|
||||||
}
|
|
||||||
|
|
||||||
return $updatedModel;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue