Merge branch 'develop' into feature/api-integration-testing
This commit is contained in:
commit
bde4d4187f
61 changed files with 1017 additions and 453 deletions
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1,6 +1,8 @@
|
|||
<!---
|
||||
Please take a little time to submit a good issue. It makes our life easier and the issue will be resolved quicker.
|
||||
|
||||
!!! GitHub is NOT the place for difficulties setting up this software. Please use it for bugs and feature requests only. If you have issues setting up the panel or the daemon visit our Discord server: https://pterodactyl.io/discord
|
||||
|
||||
If you are submitting a feature request please remove everything in here. Then give a detailed explanation what you want to have implemented and why that would be a good addition.
|
||||
|
||||
Please also try to give the issue a good title: It should summarize your issue in a few words and help us see what the issue is about in a glance. Things like "Panel is not working" do not help.
|
||||
|
|
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -3,6 +3,36 @@ This file is a running track of new features and fixes to each version of the pa
|
|||
|
||||
This project follows [Semantic Versioning](http://semver.org) guidelines.
|
||||
|
||||
## v0.7.7 (Derelict Dermodactylus)
|
||||
### Fixed
|
||||
* Fixes an issue with the sidebar logo not working correctly in some browsers due to the CSS being assigned.
|
||||
|
||||
### Added
|
||||
* Added a new client API endpoint for gathering the utilization stats for servers including disk, cpu, and memory. `GET /api/client/servers/<id>/utilization`
|
||||
* Added validation to variable validation rules to validate that the validation rules are valid because we heard you like validating your validation.
|
||||
|
||||
## v0.7.6 (Derelict Dermodactylus)
|
||||
### Fixed
|
||||
* Fixes a UI error when attempting to change the default Nest and Egg for an existing server.
|
||||
* Correct permissions check in UI to allow subusers with permission to `view-allocations` the ability to actually see the sidebar link.
|
||||
* Fixes improper behavior when marking an egg as copying the configuration from another.
|
||||
* Debug bar is only checked when the app is set to debug mode in the API session handler, rather than when it is in local mode to match the plugin settings.
|
||||
* Added validation to port allocations to prevent allocation of restricted or invalid ports.
|
||||
* Fix data integrity exception thrown when attempting to store updated server egg variables.
|
||||
* Added missing permissions check on 'SFTP Configuration' page to ensure user has permission to access a server's SFTP server before showing a user credentials.
|
||||
|
||||
### Added
|
||||
* Added ability for end users to change the name of their server through the UI. This option is only open to the server owner or an admin.
|
||||
* Added giant warning message if you attempt to change an encryption key once one has been set.
|
||||
|
||||
### Changed
|
||||
* Panel now throws proper 504: Gateway Timeout errors on server listing when daemon is offline.
|
||||
* Sessions handled through redis now use a seperate database (default `1`) to store session database to avoid logging users out when flushing the cache.
|
||||
* File manager UI improved to be clearer with buttons and cleaner on mobile.
|
||||
* reCAPTCHA's secret key position swapped with website key in advanced panel settings to be consistent with Google's reCAPTCHA dashboard.
|
||||
* Changed DisplayException to handle its own logging correctly and check if the previous exception is marked as one that should not be logged.
|
||||
* Changed 'New Folder' modal in file manager to include a trailing slash.
|
||||
|
||||
## v0.7.5 (Derelict Dermodactylus)
|
||||
### Fixed
|
||||
* Fixes application API keys being created as a client API key.
|
||||
|
|
10
Vagrantfile
vendored
10
Vagrantfile
vendored
|
@ -1,13 +1,9 @@
|
|||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "ubuntu/xenial64"
|
||||
config.vm.box = "bento/ubuntu-16.04"
|
||||
|
||||
config.vm.synced_folder "./", "/var/www/html/pterodactyl",
|
||||
owner: "www-data", group: "www-data"
|
||||
|
||||
#config.vm.provision :file, source: ".dev/vagrant/pterdactyl.conf", destination: "/etc/nginx/sites-available/pterodactyl.conf"
|
||||
#config.vm.provision :file, source: ".dev/vagrant/pteroq.service", destination: "/etc/systemd/system/pteroq.service"
|
||||
#config.vm.provision :file, source: ".dev/vagrant/mailhog.service", destination: "/etc/systemd/system/mailhog.service"
|
||||
#config.vm.provision :file, source: ".dev/vagrant/.env", destination: "/var/www/html/pterodactyl/.env"
|
||||
config.vm.provision :shell, path: ".dev/vagrant/provision.sh"
|
||||
|
||||
config.vm.network :private_network, ip: "192.168.50.2"
|
||||
|
@ -16,6 +12,6 @@ Vagrant.configure("2") do |config|
|
|||
config.vm.network :forwarded_port, guest: 3306, host: 53306
|
||||
|
||||
# Config for the vagrant-dns plugin (https://github.com/BerlinVagrant/vagrant-dns)
|
||||
config.dns.tld = "app"
|
||||
config.dns.patterns = [/^pterodactyl.app$/]
|
||||
config.dns.tld = "test"
|
||||
config.dns.patterns = [/^pterodactyl.test$/]
|
||||
end
|
||||
|
|
28
app/Console/Commands/Overrides/KeyGenerateCommand.php
Normal file
28
app/Console/Commands/Overrides/KeyGenerateCommand.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Console\Commands\Overrides;
|
||||
|
||||
use Illuminate\Foundation\Console\KeyGenerateCommand as BaseKeyGenerateCommand;
|
||||
|
||||
class KeyGenerateCommand extends BaseKeyGenerateCommand
|
||||
{
|
||||
/**
|
||||
* Override the default Laravel key generation command to throw a warning to the user
|
||||
* if it appears that they have already generated an application encryption key.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! empty(config('app.key')) && $this->input->isInteractive()) {
|
||||
$this->output->warning(trans('command/messages.key.warning'));
|
||||
if (! $this->confirm(trans('command/messages.key.confirm'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->confirm(trans('command/messages.key.final_confirm'))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
parent::handle();
|
||||
}
|
||||
}
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
namespace Pterodactyl\Exceptions;
|
||||
|
||||
use Log;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Container\Container;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
|
||||
class DisplayException extends PterodactylException
|
||||
|
@ -31,10 +33,6 @@ class DisplayException extends PterodactylException
|
|||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
if (! is_null($previous)) {
|
||||
Log::{$level}($previous);
|
||||
}
|
||||
|
||||
$this->level = $level;
|
||||
}
|
||||
|
||||
|
@ -70,8 +68,31 @@ class DisplayException extends PterodactylException
|
|||
]), method_exists($this, 'getStatusCode') ? $this->getStatusCode() : Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
app()->make(AlertsMessageBag::class)->danger($this->getMessage())->flash();
|
||||
Container::getInstance()->make(AlertsMessageBag::class)->danger($this->getMessage())->flash();
|
||||
|
||||
return redirect()->back()->withInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the exception to the logs using the defined error level only if the previous
|
||||
* exception is set.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function report()
|
||||
{
|
||||
if (! $this->getPrevious() instanceof Exception || ! Handler::isReportable($this->getPrevious())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$logger = Container::getInstance()->make(LoggerInterface::class);
|
||||
} catch (Exception $ex) {
|
||||
throw $this->getPrevious();
|
||||
}
|
||||
|
||||
return $logger->{$this->getErrorLevel()}($this->getPrevious());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Pterodactyl\Exceptions;
|
|||
use Exception;
|
||||
use PDOException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Session\TokenMismatchException;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
@ -31,7 +32,6 @@ class Handler extends ExceptionHandler
|
|||
protected $dontReport = [
|
||||
AuthenticationException::class,
|
||||
AuthorizationException::class,
|
||||
DisplayException::class,
|
||||
HttpException::class,
|
||||
ModelNotFoundException::class,
|
||||
RecordNotFoundException::class,
|
||||
|
@ -210,6 +210,17 @@ class Handler extends ExceptionHandler
|
|||
return ['errors' => [array_merge($error, $override)]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of exceptions that should not be reported.
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* @return bool
|
||||
*/
|
||||
public static function isReportable(Exception $exception): bool
|
||||
{
|
||||
return (new static(Container::getInstance()))->shouldReport($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an authentication exception into an unauthenticated response.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions\Service\Allocation;
|
||||
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
|
||||
class CidrOutOfRangeException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* CidrOutOfRangeException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.cidr_out_of_range'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions\Service\Allocation;
|
||||
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
|
||||
class InvalidPortMappingException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* InvalidPortMappingException constructor.
|
||||
*
|
||||
* @param mixed $port
|
||||
*/
|
||||
public function __construct($port)
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.invalid_mapping', ['port' => $port]));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions\Service\Allocation;
|
||||
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
|
||||
class PortOutOfRangeException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* PortOutOfRangeException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.port_out_of_range'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions\Service\Allocation;
|
||||
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
|
||||
class TooManyPortsInRangeException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* TooManyPortsInRangeException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.too_many_ports'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions\Service\Egg\Variable;
|
||||
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
|
||||
class BadValidationRuleException extends DisplayException
|
||||
{
|
||||
}
|
|
@ -331,7 +331,10 @@ class NodesController extends Controller
|
|||
* @param int|\Pterodactyl\Models\Node $node
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
|
||||
*/
|
||||
public function createAllocation(AllocationFormRequest $request, Node $node)
|
||||
{
|
||||
|
|
|
@ -73,7 +73,10 @@ class AllocationController extends ApplicationApiController
|
|||
* @param \Pterodactyl\Http\Requests\Api\Application\Allocations\StoreAllocationRequest $request
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
|
||||
*/
|
||||
public function store(StoreAllocationRequest $request): array
|
||||
{
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Transformers\Api\Client\StatsTransformer;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest;
|
||||
|
||||
class ResourceUtilizationController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* Return the current resource utilization for a server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function index(GetServerRequest $request): array
|
||||
{
|
||||
return $this->fractal->item($request->getModel(Server::class))
|
||||
->transformWith($this->getTransformer(StatsTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ namespace Pterodactyl\Http\Controllers\Base;
|
|||
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Http\Response;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
@ -81,6 +83,8 @@ class IndexController extends Controller
|
|||
|
||||
try {
|
||||
$response = $this->daemonRepository->setServer($server)->setToken($token)->details();
|
||||
} catch (ConnectException $exception) {
|
||||
throw new HttpException(Response::HTTP_GATEWAY_TIMEOUT, $exception->getMessage());
|
||||
} catch (RequestException $exception) {
|
||||
throw new HttpException(500, $exception->getMessage());
|
||||
}
|
||||
|
|
59
app/Http/Controllers/Server/Settings/NameController.php
Normal file
59
app/Http/Controllers/Server/Settings/NameController.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Settings;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Server\Settings\ChangeServerNameRequest;
|
||||
|
||||
class NameController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* NameController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(ServerRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view-name', $request->attributes->get('server'));
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.settings.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the stored name for a specific server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\Settings\ChangeServerNameRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(ChangeServerNameRequest $request): RedirectResponse
|
||||
{
|
||||
$this->repository->update($request->getServer()->id, $request->validated());
|
||||
|
||||
return redirect()->route('server.settings.name', $request->getServer()->uuidShort);
|
||||
}
|
||||
}
|
|
@ -16,9 +16,12 @@ class SftpController extends Controller
|
|||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$this->authorize('access-sftp', $request->attributes->get('server'));
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.settings.sftp');
|
||||
|
|
|
@ -41,7 +41,7 @@ class SetSessionDriver
|
|||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if ($this->app->environment() !== 'production') {
|
||||
if ($this->config->get('app.debug')) {
|
||||
$this->app->make(LaravelDebugbar::class)->disable();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Server\Settings;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Http\Requests\Server\ServerFormRequest;
|
||||
|
||||
class ChangeServerNameRequest extends ServerFormRequest
|
||||
{
|
||||
/**
|
||||
* Permission to use when checking if a user can access this resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function permission(): string
|
||||
{
|
||||
return 'edit-name';
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules to use when validating the submitted data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => Server::getCreateRules()['name'],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -113,7 +113,11 @@ class Egg extends Model implements CleansAttributes, ValidableContract
|
|||
*/
|
||||
public function getCopyScriptInstallAttribute()
|
||||
{
|
||||
return (is_null($this->copy_script_from)) ? $this->script_install : $this->scriptFrom->script_install;
|
||||
if (! is_null($this->script_install) || is_null($this->copy_script_from)) {
|
||||
return $this->script_install;
|
||||
}
|
||||
|
||||
return $this->scriptFrom->script_install;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,7 +128,11 @@ class Egg extends Model implements CleansAttributes, ValidableContract
|
|||
*/
|
||||
public function getCopyScriptEntryAttribute()
|
||||
{
|
||||
return (is_null($this->copy_script_from)) ? $this->script_entry : $this->scriptFrom->script_entry;
|
||||
if (! is_null($this->script_entry) || is_null($this->copy_script_from)) {
|
||||
return $this->script_entry;
|
||||
}
|
||||
|
||||
return $this->scriptFrom->script_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,7 +143,11 @@ class Egg extends Model implements CleansAttributes, ValidableContract
|
|||
*/
|
||||
public function getCopyScriptContainerAttribute()
|
||||
{
|
||||
return (is_null($this->copy_script_from)) ? $this->script_container : $this->scriptFrom->script_container;
|
||||
if (! is_null($this->script_container) || is_null($this->copy_script_from)) {
|
||||
return $this->script_container;
|
||||
}
|
||||
|
||||
return $this->scriptFrom->script_container;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,7 +157,11 @@ class Egg extends Model implements CleansAttributes, ValidableContract
|
|||
*/
|
||||
public function getInheritConfigFilesAttribute()
|
||||
{
|
||||
return is_null($this->config_from) ? $this->config_files : $this->configFrom->config_files;
|
||||
if (! is_null($this->config_files) || is_null($this->config_from)) {
|
||||
return $this->config_files;
|
||||
}
|
||||
|
||||
return $this->configFrom->config_files;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,7 +171,11 @@ class Egg extends Model implements CleansAttributes, ValidableContract
|
|||
*/
|
||||
public function getInheritConfigStartupAttribute()
|
||||
{
|
||||
return is_null($this->config_from) ? $this->config_startup : $this->configFrom->config_startup;
|
||||
if (! is_null($this->config_startup) || is_null($this->config_from)) {
|
||||
return $this->config_startup;
|
||||
}
|
||||
|
||||
return $this->configFrom->config_startup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,7 +185,11 @@ class Egg extends Model implements CleansAttributes, ValidableContract
|
|||
*/
|
||||
public function getInheritConfigLogsAttribute()
|
||||
{
|
||||
return is_null($this->config_from) ? $this->config_logs : $this->configFrom->config_logs;
|
||||
if (! is_null($this->config_logs) || is_null($this->config_from)) {
|
||||
return $this->config_logs;
|
||||
}
|
||||
|
||||
return $this->configFrom->config_logs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +199,11 @@ class Egg extends Model implements CleansAttributes, ValidableContract
|
|||
*/
|
||||
public function getInheritConfigStopAttribute()
|
||||
{
|
||||
return is_null($this->config_from) ? $this->config_stop : $this->configFrom->config_stop;
|
||||
if (! is_null($this->config_stop) || is_null($this->config_from)) {
|
||||
return $this->config_stop;
|
||||
}
|
||||
|
||||
return $this->configFrom->config_stop;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -164,7 +164,7 @@ class Node extends Model implements CleansAttributes, ValidableContract
|
|||
'enabled' => true,
|
||||
'kill_at_count' => 5,
|
||||
'decay' => 10,
|
||||
'bytes' => 30720,
|
||||
'lines' => 1000,
|
||||
'check_interval_ms' => 100,
|
||||
],
|
||||
],
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
<?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\Allocations;
|
||||
|
||||
use IPTools\Network;
|
||||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException;
|
||||
use Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException;
|
||||
use Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException;
|
||||
use Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException;
|
||||
|
||||
class AssignmentService
|
||||
{
|
||||
const CIDR_MAX_BITS = 27;
|
||||
const CIDR_MIN_BITS = 32;
|
||||
const PORT_FLOOR = 1024;
|
||||
const PORT_CEIL = 65535;
|
||||
const PORT_RANGE_LIMIT = 1000;
|
||||
const PORT_RANGE_REGEX = '/^(\d{1,5})-(\d{1,5})$/';
|
||||
const PORT_RANGE_REGEX = '/^(\d{4,5})-(\d{4,5})$/';
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
|
@ -38,10 +36,8 @@ class AssignmentService
|
|||
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
*/
|
||||
public function __construct(
|
||||
AllocationRepositoryInterface $repository,
|
||||
ConnectionInterface $connection
|
||||
) {
|
||||
public function __construct(AllocationRepositoryInterface $repository, ConnectionInterface $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
@ -49,21 +45,20 @@ class AssignmentService
|
|||
/**
|
||||
* Insert allocations into the database and link them to a specific node.
|
||||
*
|
||||
* @param int|\Pterodactyl\Models\Node $node
|
||||
* @param array $data
|
||||
* @param \Pterodactyl\Models\Node $node
|
||||
* @param array $data
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
|
||||
*/
|
||||
public function handle($node, array $data)
|
||||
public function handle(Node $node, array $data)
|
||||
{
|
||||
if ($node instanceof Node) {
|
||||
$node = $node->id;
|
||||
}
|
||||
|
||||
$explode = explode('/', $data['allocation_ip']);
|
||||
if (count($explode) !== 1) {
|
||||
if (! ctype_digit($explode[1]) || ($explode[1] > self::CIDR_MIN_BITS || $explode[1] < self::CIDR_MAX_BITS)) {
|
||||
throw new DisplayException(trans('exceptions.allocations.cidr_out_of_range'));
|
||||
throw new CidrOutOfRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +66,7 @@ class AssignmentService
|
|||
foreach (Network::parse(gethostbyname($data['allocation_ip'])) as $ip) {
|
||||
foreach ($data['allocation_ports'] as $port) {
|
||||
if (! is_digit($port) && ! preg_match(self::PORT_RANGE_REGEX, $port)) {
|
||||
throw new DisplayException(trans('exceptions.allocations.invalid_mapping', ['port' => $port]));
|
||||
throw new InvalidPortMappingException($port);
|
||||
}
|
||||
|
||||
$insertData = [];
|
||||
|
@ -79,12 +74,16 @@ class AssignmentService
|
|||
$block = range($matches[1], $matches[2]);
|
||||
|
||||
if (count($block) > self::PORT_RANGE_LIMIT) {
|
||||
throw new DisplayException(trans('exceptions.allocations.too_many_ports'));
|
||||
throw new TooManyPortsInRangeException;
|
||||
}
|
||||
|
||||
if ((int) $matches[1] <= self::PORT_FLOOR || (int) $matches[2] > self::PORT_CEIL) {
|
||||
throw new PortOutOfRangeException;
|
||||
}
|
||||
|
||||
foreach ($block as $unit) {
|
||||
$insertData[] = [
|
||||
'node_id' => $node,
|
||||
'node_id' => $node->id,
|
||||
'ip' => $ip->__toString(),
|
||||
'port' => (int) $unit,
|
||||
'ip_alias' => array_get($data, 'allocation_alias'),
|
||||
|
@ -92,8 +91,12 @@ class AssignmentService
|
|||
];
|
||||
}
|
||||
} else {
|
||||
if ((int) $port <= self::PORT_FLOOR || (int) $port > self::PORT_CEIL) {
|
||||
throw new PortOutOfRangeException;
|
||||
}
|
||||
|
||||
$insertData[] = [
|
||||
'node_id' => $node,
|
||||
'node_id' => $node->id,
|
||||
'ip' => $ip->__toString(),
|
||||
'port' => (int) $port,
|
||||
'ip_alias' => array_get($data, 'allocation_alias'),
|
||||
|
|
|
@ -3,24 +3,46 @@
|
|||
namespace Pterodactyl\Services\Eggs\Variables;
|
||||
|
||||
use Pterodactyl\Models\EggVariable;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Pterodactyl\Traits\Services\ValidatesValidationRules;
|
||||
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException;
|
||||
|
||||
class VariableCreationService
|
||||
{
|
||||
use ValidatesValidationRules;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* VariableCreationService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $repository
|
||||
* @param \Illuminate\Contracts\Validation\Factory $validator
|
||||
*/
|
||||
public function __construct(EggVariableRepositoryInterface $repository)
|
||||
public function __construct(EggVariableRepositoryInterface $repository, Factory $validator)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the validation factory instance to be used by rule validation
|
||||
* checking in the trait.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
protected function getValidator(): Factory
|
||||
{
|
||||
return $this->validator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,6 +53,7 @@ class VariableCreationService
|
|||
* @return \Pterodactyl\Models\EggVariable
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException
|
||||
*/
|
||||
public function handle(int $egg, array $data): EggVariable
|
||||
|
@ -42,6 +65,10 @@ class VariableCreationService
|
|||
));
|
||||
}
|
||||
|
||||
if (! empty($data['rules'] ?? '')) {
|
||||
$this->validateRules($data['rules']);
|
||||
}
|
||||
|
||||
$options = array_get($data, 'options') ?? [];
|
||||
|
||||
return $this->repository->create([
|
||||
|
|
|
@ -3,25 +3,47 @@
|
|||
namespace Pterodactyl\Services\Eggs\Variables;
|
||||
|
||||
use Pterodactyl\Models\EggVariable;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Traits\Services\ValidatesValidationRules;
|
||||
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException;
|
||||
|
||||
class VariableUpdateService
|
||||
{
|
||||
use ValidatesValidationRules;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* VariableUpdateService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $repository
|
||||
* @param \Illuminate\Contracts\Validation\Factory $validator
|
||||
*/
|
||||
public function __construct(EggVariableRepositoryInterface $repository)
|
||||
public function __construct(EggVariableRepositoryInterface $repository, Factory $validator)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the validation factory instance to be used by rule validation
|
||||
* checking in the trait.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
protected function getValidator(): Factory
|
||||
{
|
||||
return $this->validator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,6 +80,10 @@ class VariableUpdateService
|
|||
}
|
||||
}
|
||||
|
||||
if (! empty($data['rules'] ?? '')) {
|
||||
$this->validateRules($data['rules']);
|
||||
}
|
||||
|
||||
$options = array_get($data, 'options') ?? [];
|
||||
|
||||
return $this->repository->withoutFreshModel()->update($variable->id, [
|
||||
|
|
|
@ -105,7 +105,7 @@ class StartupModificationService
|
|||
'server_id' => $server->id,
|
||||
'variable_id' => $result->id,
|
||||
], [
|
||||
'variable_value' => $result->value,
|
||||
'variable_value' => $result->value ?? '',
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
40
app/Traits/Services/ValidatesValidationRules.php
Normal file
40
app/Traits/Services/ValidatesValidationRules.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Traits\Services;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException;
|
||||
|
||||
trait ValidatesValidationRules
|
||||
{
|
||||
/**
|
||||
* @return \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
abstract protected function getValidator(): Factory;
|
||||
|
||||
/**
|
||||
* Validate that the rules being provided are valid for Laravel and can
|
||||
* be resolved.
|
||||
*
|
||||
* @param array|string $rules
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException
|
||||
*/
|
||||
public function validateRules($rules)
|
||||
{
|
||||
try {
|
||||
$this->getValidator()->make(['__TEST' => 'test'], ['__TEST' => $rules])->fails();
|
||||
} catch (BadMethodCallException $exception) {
|
||||
$matches = [];
|
||||
if (preg_match('/Method \[(.+)\] does not exist\./', $exception->getMessage(), $matches)) {
|
||||
throw new BadValidationRuleException(trans('exceptions.nest.variables.bad_validation_rule', [
|
||||
'rule' => Str::snake(str_replace('validate', '', array_get($matches, 1, 'unknownRule'))),
|
||||
]), $exception);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
88
app/Transformers/Api/Client/StatsTransformer.php
Normal file
88
app/Transformers/Api/Client/StatsTransformer.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Transformers\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface;
|
||||
|
||||
class StatsTransformer extends BaseClientTransformer
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* Perform dependency injection.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $repository
|
||||
*/
|
||||
public function handle(ServerRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getResourceName(): string
|
||||
{
|
||||
return 'stats';
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform stats from the daemon into a result set that can be used in
|
||||
* the client API.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Server $model
|
||||
* @return array
|
||||
*/
|
||||
public function transform(Server $model)
|
||||
{
|
||||
try {
|
||||
$stats = $this->repository->setServer($model)->details();
|
||||
} catch (RequestException $exception) {
|
||||
throw new DaemonConnectionException($exception);
|
||||
}
|
||||
|
||||
$object = json_decode($stats->getBody()->getContents());
|
||||
|
||||
return [
|
||||
'state' => $this->transformState(object_get($object, 'status', 0)),
|
||||
'memory' => [
|
||||
'current' => round(object_get($object, 'proc.memory.total', 0) / 1024 / 1024),
|
||||
'limit' => floatval($model->memory),
|
||||
],
|
||||
'cpu' => [
|
||||
'current' => object_get($object, 'proc.cpu.total', 0),
|
||||
'cores' => object_get($object, 'proc.cpu.cores', []),
|
||||
'limit' => floatval($model->cpu),
|
||||
],
|
||||
'disk' => [
|
||||
'current' => round(object_get($object, 'proc.disk.used', 0)),
|
||||
'limit' => floatval($model->disk),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the state returned by the daemon into a human readable string.
|
||||
*
|
||||
* @param int $state
|
||||
* @return string
|
||||
*/
|
||||
private function transformState(int $state): string
|
||||
{
|
||||
switch ($state) {
|
||||
case 1:
|
||||
return 'on';
|
||||
case 2:
|
||||
return 'starting';
|
||||
case 3:
|
||||
return 'stopping';
|
||||
case 0:
|
||||
default:
|
||||
return 'off';
|
||||
}
|
||||
}
|
||||
}
|
6
artisan
6
artisan
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|
@ -13,7 +15,7 @@
|
|||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/bootstrap/autoload.php';
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
|
@ -40,7 +42,7 @@ $status = $kernel->handle(
|
|||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running. We will fire off the shutdown events
|
||||
| Once Artisan has finished running, we will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Composer Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
|
@ -70,6 +70,12 @@ return [
|
|||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
],
|
||||
|
||||
'sessions' => [
|
||||
'driver' => env('SESSION_DRIVER', 'database'),
|
||||
'table' => 'sessions',
|
||||
'connection' => env('SESSION_DRIVER') === 'redis' ? 'sessions' : null,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
@ -93,11 +93,19 @@ return [
|
|||
|
||||
'redis' => [
|
||||
'client' => 'predis',
|
||||
|
||||
'default' => [
|
||||
'host' => env('REDIS_HOST', 'localhost'),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DATBASE', 0),
|
||||
],
|
||||
|
||||
'sessions' => [
|
||||
'host' => env('REDIS_HOST', 'localhost'),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DATBASE_SESSIONS', 1),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
@ -69,7 +69,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'connection' => null,
|
||||
'connection' => env('SESSION_DRIVER') === 'redis' ? 'sessions' : null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"meta": {
|
||||
"version": "PTDL_v1"
|
||||
},
|
||||
"exported_at": "2017-11-03T22:15:10-05:00",
|
||||
"exported_at": "2018-02-27T00:57:04-06:00",
|
||||
"name": "Forge Minecraft",
|
||||
"author": "support@pterodactyl.io",
|
||||
"description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.",
|
||||
|
@ -17,7 +17,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"installation": {
|
||||
"script": "#!\/bin\/ash\n# Forge Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add curl\n\nGET_VERSIONS=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/ | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\.[0-9][0-9][0-9][0-9]')\nLATEST_VERSION=$(echo $GET_VERSIONS | sed 's\/ \/\/g')\n\ncd \/mnt\/server\n\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-installer.jar -o installer.jar\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-universal.jar -o server.jar\n\njava -jar installer.jar --installServer\nrm -rf installer.jar",
|
||||
"script": "#!\/bin\/ash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk update\r\napk add curl\r\n\r\nGET_VERSIONS=$(curl -sl http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/ | grep -A1 Latest | grep -o -e '[1]\\.[0-9][0-9]]\\?\\.\\?[0-9]\\?[0-9] - [0-9][0-9]\\.[0-9][0-9]\\.[0-9]\\?[0-9]\\.[0-9][0-9][0-9][0-9]')\r\nLATEST_VERSION=$(echo $GET_VERSIONS | sed 's\/ \/\/g')\r\n\r\ncd \/mnt\/server\r\n\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-installer.jar -o installer.jar\r\ncurl -sS http:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/$LATEST_VERSION\/forge-$LATEST_VERSION-universal.jar -o server.jar\r\n\r\njava -jar installer.jar --installServer\r\nrm -rf installer.jar",
|
||||
"container": "frolvlad\/alpine-oraclejdk8:cleaned",
|
||||
"entrypoint": "ash"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"installation": {
|
||||
"script": "#!\/bin\/ash\n# TS3 Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add tar curl\n\ncd \/tmp\n\ncurl -sSLO http:\/\/dl.4players.de\/ts\/releases\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\n\ntar -xjvf teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\ncp -r teamspeak3-server_linux_amd64\/* \/mnt\/server\n\necho \"machine_id=\ndefault_voice_port=${SERVER_PORT}\nvoice_ip=0.0.0.0\nlicensepath=\nfiletransfer_port=30033\nfiletransfer_ip=\nquery_port=${SERVER_PORT}\nquery_ip=0.0.0.0\nquery_ip_whitelist=query_ip_whitelist.txt\nquery_ip_blacklist=query_ip_blacklist.txt\ndbplugin=ts3db_sqlite3\ndbpluginparameter=\ndbsqlpath=sql\/\ndbsqlcreatepath=create_sqlite\/\ndbconnections=10\nlogpath=logs\nlogquerycommands=0\ndbclientkeepdays=30\nlogappend=0\nquery_skipbruteforcecheck=0\" > \/mnt\/server\/ts3server.ini",
|
||||
"script": "#!\/bin\/ash\n# TS3 Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add tar curl\n\ncd \/tmp\n\ncurl -sSLO http:\/\/dl.4players.de\/ts\/releases\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\n\ntar -xjvf teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\ncp -r teamspeak3-server_linux_amd64\/* \/mnt\/server\n\necho \"machine_id=\ndefault_voice_port=${SERVER_PORT}\nvoice_ip=0.0.0.0\nlicensepath=\nfiletransfer_port=30033\nfiletransfer_ip=\nquery_port=${SERVER_PORT}\nquery_ip=0.0.0.0\nquery_ip_whitelist=query_ip_whitelist.txt\nquery_ip_blacklist=query_ip_blacklist.txt\ndbplugin=ts3db_sqlite3\ndbpluginparameter=\ndbsqlpath=sql\/\ndbsqlcreatepath=create_sqlite\/\ndbconnections=10\nlogpath=logs\nlogquerycommands=0\ndbclientkeepdays=30\nlogappend=0\nquery_skipbruteforcecheck=0\" > \/mnt\/server\/ts3server.ini\n\ntouch \/mnt\/server\/.ts3server_license_accepted",
|
||||
"container": "alpine:3.4",
|
||||
"entrypoint": "ash"
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
|||
"name": "Server Version",
|
||||
"description": "The version of Teamspeak 3 to use when running the server.",
|
||||
"env_variable": "TS_VERSION",
|
||||
"default_value": "3.0.13.8",
|
||||
"default_value": "3.1.1",
|
||||
"user_viewable": 1,
|
||||
"user_editable": 1,
|
||||
"rules": "required|regex:\/^([0-9_\\.-]{5,10})$\/"
|
||||
|
|
|
@ -145,6 +145,18 @@ p.small {
|
|||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.table .min-size {
|
||||
width:1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width:767px) {
|
||||
.box-header > .box-tools {
|
||||
position: relative !important;
|
||||
padding: 0px 10px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.middle, .align-middle {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
@ -457,10 +469,7 @@ label.control-label > span.field-optional:before {
|
|||
padding: 3px 10px !important;
|
||||
}
|
||||
|
||||
body.sidebar-collapse .main-header .logo {
|
||||
overflow: hidden;
|
||||
text-indent: 100%;
|
||||
background-image: url('/favicons/favicon-32x32.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
.logo-mini > img {
|
||||
height: 42px;
|
||||
width: auto;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -284,7 +284,7 @@ class ActionsClass {
|
|||
swal({
|
||||
type: 'warning',
|
||||
title: '',
|
||||
text: 'Are you sure you want to delete <code>' + delName + '</code>? There is <strong>no</strong> reversing this action.',
|
||||
text: 'Are you sure you want to delete <code>' + delName + '</code>?',
|
||||
html: true,
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
|
@ -380,16 +380,22 @@ class ActionsClass {
|
|||
if (selectedItems.length != 0)
|
||||
{
|
||||
let formattedItems = "";
|
||||
let i = 0;
|
||||
$.each(selectedItems, function(key, value) {
|
||||
formattedItems += ("<code>" + value + "</code>, ");
|
||||
})
|
||||
formattedItems += ("<code>" + value + "</code>, ");
|
||||
i++;
|
||||
return i < 5;
|
||||
});
|
||||
|
||||
formattedItems = formattedItems.slice(0, -2);
|
||||
if (selectedItems.length > 5) {
|
||||
formattedItems += ', and ' + (selectedItems.length - 5) + ' other(s)';
|
||||
}
|
||||
|
||||
swal({
|
||||
type: 'warning',
|
||||
title: '',
|
||||
text: 'Are you sure you want to delete:' + formattedItems + '? There is <strong>no</strong> reversing this action.',
|
||||
text: 'Are you sure you want to delete the following files: ' + formattedItems + '?',
|
||||
html: true,
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
|
|
|
@ -80,8 +80,11 @@
|
|||
}
|
||||
}
|
||||
}).fail(function (jqXHR) {
|
||||
console.error(jqXHR);
|
||||
element.find('[data-action="status"]').html('<span class="label label-default">Error</span>');
|
||||
if (jqXHR.status === 504) {
|
||||
element.find('[data-action="status"]').html('<span class="label label-default">Gateway Timeout</span>');
|
||||
} else {
|
||||
element.find('[data-action="status"]').html('<span class="label label-default">Error</span>');
|
||||
}
|
||||
});
|
||||
}).promise().done(function () {
|
||||
setTimeout(updateServerStatus, 10000);
|
||||
|
|
|
@ -30,7 +30,7 @@ return [
|
|||
'api' => [
|
||||
'index' => [
|
||||
'list' => 'Your Keys',
|
||||
'header' => 'Accout API',
|
||||
'header' => 'Account API',
|
||||
'header_sub' => 'Manage access keys that allow you to perform actions aganist the panel.',
|
||||
'create_new' => 'Create New API key',
|
||||
'keypair_created' => 'An API key has been successfully generated and is listed below.',
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'key' => [
|
||||
'warning' => 'It appears you have already configured an application encryption key. Continuing with this process with overwrite that key and cause data corruption for any existing encrypted data. DO NOT CONTINUE UNLESS YOU KNOW WHAT YOU ARE DOING.',
|
||||
'confirm' => 'I understand the consequences of performing this command and accept all responsibility for the loss of encrypted data.',
|
||||
'final_confirm' => 'Are you sure you wish to continue? Changing the application encryption key WILL CAUSE DATA LOSS.',
|
||||
],
|
||||
'location' => [
|
||||
'no_location_found' => 'Could not locate a record matching the provided short code.',
|
||||
'ask_short' => 'Location Short Code',
|
||||
|
|
|
@ -8,9 +8,10 @@ return [
|
|||
],
|
||||
'allocations' => [
|
||||
'server_using' => 'A server is currently assigned to this allocation. An allocation can only be deleted if no server is currently assigned.',
|
||||
'too_many_ports' => 'Adding more than 1000 ports at a single time is not supported. Please use a smaller range.',
|
||||
'too_many_ports' => 'Adding more than 1000 ports in a single range at once is not supported.',
|
||||
'invalid_mapping' => 'The mapping provided for :port was invalid and could not be processed.',
|
||||
'cidr_out_of_range' => 'CIDR notation only allows masks between /25 and /32.',
|
||||
'port_out_of_range' => 'Ports in an allocation must be greater than 1024 and less than or equal to 65535.',
|
||||
],
|
||||
'nest' => [
|
||||
'delete_has_servers' => 'A Nest with active servers attached to it cannot be deleted from the Panel.',
|
||||
|
@ -23,6 +24,7 @@ return [
|
|||
'variables' => [
|
||||
'env_not_unique' => 'The environment variable :name must be unique to this Egg.',
|
||||
'reserved_name' => 'The environment variable :name is protected and cannot be assigned to a variable.',
|
||||
'bad_validation_rule' => 'The validation rule ":rule" is not a valid rule for this application.',
|
||||
],
|
||||
'importer' => [
|
||||
'json_error' => 'There was an error while attempting to parse the JSON file: :error.',
|
||||
|
|
|
@ -27,5 +27,6 @@ return [
|
|||
'edit_file' => 'Edit File',
|
||||
'admin_header' => 'ADMINISTRATIVE',
|
||||
'admin' => 'Server Configuration',
|
||||
'server_name' => 'Server Name',
|
||||
],
|
||||
];
|
||||
|
|
|
@ -273,8 +273,8 @@ return [
|
|||
'last_modified' => 'Last Modified',
|
||||
'add_new' => 'Add New File',
|
||||
'add_folder' => 'Add New Folder',
|
||||
'mass_actions' => 'Mass actions',
|
||||
'delete' => 'Delete',
|
||||
'mass_actions' => 'Mass Actions',
|
||||
'delete' => 'Delete Files',
|
||||
'edit' => [
|
||||
'header' => 'Edit File',
|
||||
'header_sub' => 'Make modifications to a file from the web.',
|
||||
|
@ -289,6 +289,11 @@ return [
|
|||
],
|
||||
],
|
||||
'config' => [
|
||||
'name' => [
|
||||
'header' => 'Server Name',
|
||||
'header_sub' => 'Change this server\'s name.',
|
||||
'details' => 'The server name is only a reference to this server on the panel, and will not affect any server specific configurations that may display to users in games.',
|
||||
],
|
||||
'startup' => [
|
||||
'header' => 'Start Configuration',
|
||||
'header_sub' => 'Control server startup arguments.',
|
||||
|
|
|
@ -105,20 +105,20 @@
|
|||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Name <span class="field-required"></span></label>
|
||||
<input type="text" name="name" class="form-control" />
|
||||
<input type="text" name="name" class="form-control" value="{{ old('name') }}"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">Description</label>
|
||||
<textarea name="description" class="form-control" rows="3"></textarea>
|
||||
<textarea name="description" class="form-control" rows="3">{{ old('description') }}</textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<label class="control-label">Environment Variable <span class="field-required"></span></label>
|
||||
<input type="text" name="env_variable" class="form-control" />
|
||||
<input type="text" name="env_variable" class="form-control" value="{{ old('env_variable') }}" />
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label class="control-label">Default Value</label>
|
||||
<input type="text" name="default_value" class="form-control" />
|
||||
<input type="text" name="default_value" class="form-control" value="{{ old('default_value') }}" />
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<p class="text-muted small">This variable can be accessed in the statup command by entering <code>@{{environment variable value}}</code>.</p>
|
||||
|
@ -133,7 +133,7 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">Input Rules <span class="field-required"></span></label>
|
||||
<input type="text" name="rules" class="form-control" value="required|string|max:20" placeholder="required|string|max:20" />
|
||||
<input type="text" name="rules" class="form-control" value="{{ old('rules', 'required|string|max:20') }}" placeholder="required|string|max:20" />
|
||||
<p class="text-muted small">These rules are defined using standard Laravel Framework validation rules.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
<div>
|
||||
<input type="text" name="database_limit" class="form-control" value="{{ old('database_limit', 0) }}"/>
|
||||
</div>
|
||||
<p class="text-muted small">The total number of databases a user is allowed to create for this server. Leave blank to allow unlimmited.</p>
|
||||
<p class="text-muted small">The total number of databases a user is allowed to create for this server. Leave blank to allow unlimited.</p>
|
||||
</div>
|
||||
<div class="form-group col-xs-6">
|
||||
<label for="cpu" class="control-label">Allocation Limit</label>
|
||||
|
@ -143,7 +143,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="box-footer no-border no-pad-top no-pad-bottom">
|
||||
<p class="text-muted small">If you do not want to assign swap space to a server simply put <code>0</code> for the value, or <code>-1</code> to allow unlimited swap space. If you want to disable memory limiting on a server simply enter <code>0</code> into the memory field.<p>
|
||||
<p class="text-muted small">If you do not want to assign swap space to a server, simply put <code>0</code> for the value, or <code>-1</code> to allow unlimited swap space. If you want to disable memory limiting on a server, simply enter <code>0</code> into the memory field.<p>
|
||||
</div>
|
||||
<div class="box-body row">
|
||||
<div class="form-group col-sm-4">
|
||||
|
@ -169,7 +169,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="box-footer no-border no-pad-top no-pad-bottom">
|
||||
<p class="text-muted small">If you do not want to limit CPU usage set the value to <code>0</code>. To determine a value, take the number <em>physical</em> cores and multiply it by 100. For example, on a quad core system <code>(4 * 100 = 400)</code> there is <code>400%</code> available. To limit a server to using half of a single core, you would set the value to <code>50</code>. To allow a server to use up to two physical cores, set the value to <code>200</code>. BlockIO should be a value between <code>10</code> and <code>1000</code>. Please see <a href="https://docs.docker.com/engine/reference/run/#/block-io-bandwidth-blkio-constraint" target="_blank">this documentation</a> for more information about it.<p>
|
||||
<p class="text-muted small">If you do not want to limit CPU usage, set the value to <code>0</code>. To determine a value, take the number of <em>physical</em> cores and multiply it by 100. For example, on a quad core system <code>(4 * 100 = 400)</code> there is <code>400%</code> available. To limit a server to using half of a single core, you would set the value to <code>50</code>. To allow a server to use up to two physical cores, set the value to <code>200</code>. BlockIO should be a value between <code>10</code> and <code>1000</code>. Please see <a href="https://docs.docker.com/engine/reference/run/#/block-io-bandwidth-blkio-constraint" target="_blank">this documentation</a> for more information about it.<p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -239,7 +239,7 @@
|
|||
<div class="form-group col-xs-12">
|
||||
<label for="pStartup">Startup Command</label>
|
||||
<input type="text" id="pStartup" value="{{ old('startup') }}" class="form-control" name="startup" />
|
||||
<p class="small text-muted no-margin">The following data replacers are avaliable for the startup command: <code>@{{SERVER_MEMORY}}</code>, <code>@{{SERVER_IP}}</code>, and <code>@{{SERVER_PORT}}</code>. They will be replaced with the allocated memory, server ip, and server port respectively.</p>
|
||||
<p class="small text-muted no-margin">The following data replacers are avaliable for the startup command: <code>@{{SERVER_MEMORY}}</code>, <code>@{{SERVER_IP}}</code>, and <code>@{{SERVER_PORT}}</code>. They will be replaced with the allocated memory, server IP, and server port respectively.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-header with-border" style="margin-top:-10px;">
|
||||
|
|
|
@ -134,26 +134,11 @@
|
|||
{!! Theme::js('vendor/lodash/lodash.js') !!}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#pNestId').select2({placeholder: 'Select a Nest'}).change();
|
||||
$('#pEggId').select2({placeholder: 'Select a Nest Egg'});
|
||||
$('#pPackId').select2({placeholder: 'Select a Service Pack'});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$('#pNestId').on('change', function (event) {
|
||||
$('#pEggId').html('').select2({
|
||||
data: $.map(_.get(Pterodactyl.nests, $(this).val() + '.eggs', []), function (item) {
|
||||
return {
|
||||
id: item.id,
|
||||
text: item.name,
|
||||
};
|
||||
}),
|
||||
}).val(Pterodactyl.server.egg_id).change();
|
||||
});
|
||||
|
||||
$('#pEggId').on('change', function (event) {
|
||||
var parentChain = _.get(Pterodactyl.nests, $('#pNestId').val(), null);
|
||||
var objectChain = _.get(parentChain, 'eggs.' + $(this).val(), null);
|
||||
$('#pEggId').select2({placeholder: 'Select a Nest Egg'}).on('change', function () {
|
||||
var selectedEgg = _.isNull($(this).val()) ? $(this).find('option').first().val() : $(this).val();
|
||||
var parentChain = _.get(Pterodactyl.nests, $("#pNestId").val());
|
||||
var objectChain = _.get(parentChain, 'eggs.' + selectedEgg);
|
||||
|
||||
$('#setDefaultImage').html(_.get(objectChain, 'docker_image', 'undefined'));
|
||||
$('#pDockerImage').val(_.get(objectChain, 'docker_image', 'undefined'));
|
||||
|
@ -168,7 +153,7 @@
|
|||
}
|
||||
|
||||
$('#pPackId').html('').select2({
|
||||
data: [{ id: '0', text: 'No Service Pack' }].concat(
|
||||
data: [{id: '0', text: 'No Service Pack'}].concat(
|
||||
$.map(_.get(objectChain, 'packs', []), function (item, i) {
|
||||
return {
|
||||
id: item.id,
|
||||
|
@ -202,9 +187,26 @@
|
|||
</div> \
|
||||
</div> \
|
||||
</div>';
|
||||
$('#appendVariablesTo').append(dataAppend);
|
||||
$('#appendVariablesTo').find('#egg_variable_' + item.env_variable).val(setValue);
|
||||
$('#appendVariablesTo').append(dataAppend).find('#egg_variable_' + item.env_variable).val(setValue);
|
||||
});
|
||||
});
|
||||
|
||||
$('#pNestId').select2({placeholder: 'Select a Nest'}).on('change', function () {
|
||||
$('#pEggId').html('').select2({
|
||||
data: $.map(_.get(Pterodactyl.nests, $(this).val() + '.eggs', []), function (item) {
|
||||
return {
|
||||
id: item.id,
|
||||
text: item.name,
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
if (_.isObject(_.get(Pterodactyl.nests, $(this).val() + '.eggs.' + Pterodactyl.server.egg_id))) {
|
||||
$('#pEggId').val(Pterodactyl.server.egg_id);
|
||||
}
|
||||
|
||||
$('#pEggId').change();
|
||||
}).change();
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
|
|
@ -34,6 +34,12 @@
|
|||
<p class="text-muted small">If enabled, login forms and password reset forms will do a silent captcha check and display a visible captcha if needed.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label class="control-label">Site Key</label>
|
||||
<div>
|
||||
<input type="text" required class="form-control" name="recaptcha:website_key" value="{{ old('recaptcha:website_key', config('recaptcha.website_key')) }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label class="control-label">Secret Key</label>
|
||||
<div>
|
||||
|
@ -41,12 +47,6 @@
|
|||
<p class="text-muted small">Used for communication between your site and Google. Be sure to keep it a secret.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label class="control-label">Website Key</label>
|
||||
<div>
|
||||
<input type="text" required class="form-control" name="recaptcha:website_key" value="{{ old('recaptcha:website_key', config('recaptcha.website_key')) }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if($showRecaptchaWarning)
|
||||
<div class="row">
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#bc6e3c">
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico">
|
||||
<meta name="msapplication-config" content="/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#367fa9">
|
||||
<meta name="theme-color" content="#0e4688">
|
||||
|
||||
@include('layouts.scripts')
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#bc6e3c">
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico">
|
||||
<meta name="msapplication-config" content="/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#367fa9">
|
||||
<meta name="theme-color" content="#0e4688">
|
||||
|
||||
@section('scripts')
|
||||
{!! Theme::css('vendor/bootstrap/bootstrap.min.css?t={cache-version}') !!}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#bc6e3c">
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico">
|
||||
<meta name="msapplication-config" content="/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#367fa9">
|
||||
<meta name="theme-color" content="#0e4688">
|
||||
|
||||
@section('scripts')
|
||||
{!! Theme::css('vendor/bootstrap/bootstrap.min.css?t={cache-version}') !!}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#bc6e3c">
|
||||
<link rel="shortcut icon" href="/favicons/favicon.ico">
|
||||
<meta name="msapplication-config" content="/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#367fa9">
|
||||
<meta name="theme-color" content="#0e4688">
|
||||
|
||||
@include('layouts.scripts')
|
||||
|
||||
|
@ -43,7 +43,8 @@
|
|||
<div class="wrapper">
|
||||
<header class="main-header">
|
||||
<a href="{{ route('index') }}" class="logo">
|
||||
<span>{{ config('app.name', 'Pterodactyl') }}</span>
|
||||
<span class="logo-lg">{{ config('app.name', 'Pterodactyl') }}</span>
|
||||
<span class="logo-mini"><img src="favicons/android-chrome-192x192.png"></span>
|
||||
</a>
|
||||
<nav class="navbar navbar-static-top">
|
||||
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
|
||||
|
@ -170,7 +171,7 @@
|
|||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
@if(Gate::allows('view-startup', $server) || Gate::allows('access-sftp', $server) || Gate::allows('view-allocation', $server))
|
||||
@if(Gate::allows('view-startup', $server) || Gate::allows('access-sftp', $server) || Gate::allows('view-allocations', $server))
|
||||
<li class="treeview
|
||||
@if(starts_with(Route::currentRouteName(), 'server.settings'))
|
||||
active
|
||||
|
@ -184,7 +185,10 @@
|
|||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
@can('view-allocation', $server)
|
||||
@can('view-name', $server)
|
||||
<li class="{{ Route::currentRouteName() !== 'server.settings.name' ?: 'active' }}"><a href="{{ route('server.settings.name', $server->uuidShort) }}"><i class="fa fa-angle-right"></i> @lang('navigation.server.server_name')</a></li>
|
||||
@endcan
|
||||
@can('view-allocations', $server)
|
||||
<li class="{{ Route::currentRouteName() !== 'server.settings.allocation' ?: 'active' }}"><a href="{{ route('server.settings.allocation', $server->uuidShort) }}"><i class="fa fa-angle-right"></i> @lang('navigation.server.port_allocations')</a></li>
|
||||
@endcan
|
||||
@can('access-sftp', $server)
|
||||
|
|
|
@ -6,45 +6,45 @@
|
|||
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">/home/container{{ $directory['header'] }}</h3>
|
||||
<div class="box-tools pull-right">
|
||||
<div class="btn-group">
|
||||
<button type="button" id="mass_actions" class="btn btn-sm btn-info dropdown-toggle disabled" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@lang('server.files.mass_actions') <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-massactions">
|
||||
<li><a href="#" id="selective-deletion" data-action="selective-deletion">@lang('server.files.delete') <i class="fa fa-fw fa-trash-o"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-success btn-icon" data-action="add-folder">
|
||||
<i class="fa fa-fw fa-folder-open-o"></i>
|
||||
</button>
|
||||
<div class="box-tools">
|
||||
<a href="/server/{{ $server->uuidShort }}/files/add/@if($directory['header'] !== '')?dir={{ $directory['header'] }}@endif">
|
||||
<button class="btn btn-success btn-sm btn-icon">
|
||||
<i class="fa fa-fw fa-file-text-o"></i>
|
||||
New File <i class="fa fa-fw fa-file-text-o"></i>
|
||||
</button>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-success btn-icon" data-action="add-folder">
|
||||
New Folder <i class="fa fa-fw fa-folder-open-o"></i>
|
||||
</button>
|
||||
<label class="btn btn-primary btn-sm btn-icon">
|
||||
<i class="fa fa-fw fa-upload"></i><input type="file" id="files_touch_target" class="hidden">
|
||||
Upload <i class="fa fa-fw fa-upload"></i><input type="file" id="files_touch_target" class="hidden">
|
||||
</label>
|
||||
<div class="btn-group hidden-xs">
|
||||
<button type="button" id="mass_actions" class="btn btn-sm btn-default dropdown-toggle disabled" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@lang('server.files.mass_actions') <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-massactions">
|
||||
<li><a href="#" id="selective-deletion" data-action="selective-deletion">@lang('server.files.delete') <i class="fa fa-fw fa-trash-o"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body table-responsive no-padding">
|
||||
<table class="table table-hover" id="file_listing" data-current-dir="{{ $directory['header'] }}">
|
||||
<table class="table table-hover" id="file_listing" data-current-dir="{{ rtrim($directory['header'], '/') . '/' }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:4%;" class="middle">
|
||||
<input type="checkbox" class="select-all-files" data-action="selectAll"><i class="fa fa-refresh muted muted-hover use-pointer" data-action="reload-files" style="font-size:14px;"></i>
|
||||
<th class="middle min-size">
|
||||
<input type="checkbox" class="select-all-files hidden-xs" data-action="selectAll"><i class="fa fa-refresh muted muted-hover use-pointer" data-action="reload-files" style="font-size:14px;"></i>
|
||||
</th>
|
||||
<th style="width:55%">@lang('server.files.file_name')</th>
|
||||
<th style="width:15%" class="hidden-xs">@lang('server.files.size')</th>
|
||||
<th style="width:20%" class="hidden-xs">@lang('server.files.last_modified')</th>
|
||||
<th style="width:6%"></th>
|
||||
<th>@lang('server.files.file_name')</th>
|
||||
<th class="hidden-xs">@lang('server.files.size')</th>
|
||||
<th class="hidden-xs">@lang('server.files.last_modified')</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="append_files_to">
|
||||
@if (isset($directory['first']) && $directory['first'] === true)
|
||||
<tr data-type="disabled">
|
||||
<td><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td class="middle min-size"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td><a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">←</a></a></td>
|
||||
<td class="hidden-xs"></td>
|
||||
<td class="hidden-xs"></td>
|
||||
|
@ -53,7 +53,7 @@
|
|||
@endif
|
||||
@if (isset($directory['show']) && $directory['show'] === true)
|
||||
<tr data-type="disabled">
|
||||
<td><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td class="middle min-size"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td data-name="{{ rawurlencode($directory['link']) }}">
|
||||
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">← {{ $directory['link_show'] }}</a>
|
||||
</td>
|
||||
|
@ -64,7 +64,9 @@
|
|||
@endif
|
||||
@foreach ($folders as $folder)
|
||||
<tr data-type="folder">
|
||||
<td data-identifier="type" class="middle"><input type="checkbox" class="select-folder" data-action="addSelection"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td class="middle min-size" data-identifier="type">
|
||||
<input type="checkbox" class="select-folder hidden-xs" data-action="addSelection"><i class="fa fa-folder" style="margin-left: 0.859px;"></i>
|
||||
</td>
|
||||
<td data-identifier="name" data-name="{{ rawurlencode($folder['entry']) }}" data-path="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}@endif/">
|
||||
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">{{ $folder['entry'] }}</a>
|
||||
</td>
|
||||
|
@ -79,12 +81,14 @@
|
|||
{{ $carbon->diffForHumans() }}
|
||||
@endif
|
||||
</td>
|
||||
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></td>
|
||||
<td class="min-size">
|
||||
<button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@foreach ($files as $file)
|
||||
<tr data-type="file" data-mime="{{ $file['mime'] }}">
|
||||
<td data-identifier="type" class="middle"><input type="checkbox" class="select-file" data-action="addSelection">
|
||||
<td class="middle min-size" data-identifier="type"><input type="checkbox" class="select-file hidden-xs" data-action="addSelection">
|
||||
{{-- oh boy --}}
|
||||
@if(in_array($file['mime'], [
|
||||
'application/x-7z-compressed',
|
||||
|
@ -156,7 +160,9 @@
|
|||
{{ $carbon->diffForHumans() }}
|
||||
@endif
|
||||
</td>
|
||||
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></td>
|
||||
<td class="min-size">
|
||||
<button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
|
50
resources/themes/pterodactyl/server/settings/name.blade.php
Normal file
50
resources/themes/pterodactyl/server/settings/name.blade.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
{{-- 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 --}}
|
||||
@extends('layouts.master')
|
||||
|
||||
@section('title')
|
||||
@lang('server.config.name.header')
|
||||
@endsection
|
||||
|
||||
@section('content-header')
|
||||
<h1>@lang('server.config.name.header')<small>@lang('server.config.name.header_sub')</small></h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ route('index') }}">@lang('strings.home')</a></li>
|
||||
<li><a href="{{ route('server.index', $server->uuidShort) }}">{{ $server->name }}</a></li>
|
||||
<li>@lang('navigation.server.configuration')</li>
|
||||
<li class="active">@lang('navigation.server.server_name')</li>
|
||||
</ol>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<form action="{{ route('server.settings.name', $server->uuidShort) }}" method="POST">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div class="form-group no-margin-bottom">
|
||||
<label class="control-label" for="pServerName">@lang('server.config.name.header')</label>
|
||||
<div>
|
||||
<input type="text" name="name" id="pServerName" class="form-control" value="{{ $server->name }}" />
|
||||
<p class="small text-muted no-margin-bottom">@lang('server.config.name.details')</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ method_field('PATCH') }}
|
||||
{{ csrf_field() }}
|
||||
<input type="submit" class="btn btn-sm btn-primary pull-right" value="@lang('strings.submit')" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('footer-scripts')
|
||||
@parent
|
||||
{!! Theme::js('js/frontend/server.socket.js') !!}
|
||||
@endsection
|
|
@ -22,6 +22,9 @@ Route::get('/', 'ClientController@index')->name('api.client.index');
|
|||
*/
|
||||
Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateClientAccess::class]], function () {
|
||||
Route::get('/', 'Servers\ServerController@index')->name('api.client.servers.view');
|
||||
Route::get('/utilization', 'Servers\ResourceUtilizationController@index')
|
||||
->middleware(['throttle:15,1'])
|
||||
->name('api.client.servers.resources');
|
||||
|
||||
Route::post('/command', 'Servers\CommandController@index')->name('api.client.servers.command');
|
||||
Route::post('/power', 'Servers\PowerController@index')->name('api.client.servers.power');
|
||||
|
|
|
@ -19,11 +19,12 @@ Route::get('/console', 'ConsoleController@console')->name('server.console');
|
|||
*/
|
||||
Route::group(['prefix' => 'settings'], function () {
|
||||
Route::get('/allocation', 'Settings\AllocationController@index')->name('server.settings.allocation');
|
||||
Route::patch('/allocation', 'Settings\AllocationController@update');
|
||||
|
||||
Route::get('/name', 'Settings\NameController@index')->name('server.settings.name');
|
||||
Route::get('/sftp', 'Settings\SftpController@index')->name('server.settings.sftp');
|
||||
|
||||
Route::get('/startup', 'Settings\StartupController@index')->name('server.settings.startup');
|
||||
|
||||
Route::patch('/allocation', 'Settings\AllocationController@update');
|
||||
Route::patch('/name', 'Settings\NameController@update');
|
||||
Route::patch('/startup', 'Settings\StartupController@update');
|
||||
});
|
||||
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Http\Controllers\Base;
|
||||
|
||||
use Mockery as m;
|
||||
use Pterodactyl\Models\ApiKey;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Services\Api\KeyCreationService;
|
||||
use Tests\Unit\Http\Controllers\ControllerTestCase;
|
||||
use Pterodactyl\Http\Requests\Base\StoreAccountKeyRequest;
|
||||
use Pterodactyl\Http\Controllers\Base\AccountKeyController;
|
||||
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
|
||||
|
||||
class AccountKeyControllerTest extends ControllerTestCase
|
||||
{
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag|\Mockery\Mock
|
||||
*/
|
||||
protected $alert;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Api\KeyCreationService|\Mockery\Mock
|
||||
*/
|
||||
protected $keyService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface|\Mockery\Mock
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->markTestSkipped('Not implemented');
|
||||
|
||||
$this->alert = m::mock(AlertsMessageBag::class);
|
||||
$this->keyService = m::mock(KeyCreationService::class);
|
||||
$this->repository = m::mock(ApiKeyRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the index controller.
|
||||
*/
|
||||
public function testIndexController()
|
||||
{
|
||||
$model = $this->generateRequestUserModel();
|
||||
|
||||
$this->repository->shouldReceive('getAccountKeys')->with($model)->once()->andReturn(collect(['testkeys']));
|
||||
|
||||
$response = $this->getController()->index($this->request);
|
||||
$this->assertIsViewResponse($response);
|
||||
$this->assertViewNameEquals('base.api.index', $response);
|
||||
$this->assertViewHasKey('keys', $response);
|
||||
$this->assertViewKeyEquals('keys', collect(['testkeys']), $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the create API view controller.
|
||||
*/
|
||||
public function testCreateController()
|
||||
{
|
||||
$this->generateRequestUserModel();
|
||||
|
||||
$response = $this->getController()->create($this->request);
|
||||
$this->assertIsViewResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the store functionality for a user.
|
||||
*/
|
||||
public function testStoreController()
|
||||
{
|
||||
$this->setRequestMockClass(StoreAccountKeyRequest::class);
|
||||
$model = $this->generateRequestUserModel();
|
||||
$keyModel = factory(ApiKey::class)->make();
|
||||
|
||||
$this->request->shouldReceive('user')->withNoArgs()->andReturn($model);
|
||||
$this->request->shouldReceive('input')->with('allowed_ips')->once()->andReturnNull();
|
||||
$this->request->shouldReceive('input')->with('memo')->once()->andReturnNull();
|
||||
|
||||
$this->keyService->shouldReceive('setKeyType')->with(ApiKey::TYPE_ACCOUNT)->once()->andReturnSelf();
|
||||
$this->keyService->shouldReceive('handle')->with([
|
||||
'user_id' => $model->id,
|
||||
'allowed_ips' => null,
|
||||
'memo' => null,
|
||||
])->once()->andReturn($keyModel);
|
||||
|
||||
$this->alert->shouldReceive('success')->with(trans('base.api.index.keypair_created'))->once()->andReturnSelf();
|
||||
$this->alert->shouldReceive('flash')->withNoArgs()->once()->andReturnNull();
|
||||
|
||||
$response = $this->getController()->store($this->request);
|
||||
$this->assertIsRedirectResponse($response);
|
||||
$this->assertRedirectRouteEquals('account.api', $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the API key revocation controller.
|
||||
*/
|
||||
public function testRevokeController()
|
||||
{
|
||||
$model = $this->generateRequestUserModel();
|
||||
|
||||
$this->repository->shouldReceive('deleteAccountKey')->with($model, 'testIdentifier')->once()->andReturn(1);
|
||||
|
||||
$response = $this->getController()->revoke($this->request, 'testIdentifier');
|
||||
$this->assertIsResponse($response);
|
||||
$this->assertEmpty($response->getContent());
|
||||
$this->assertResponseCodeEquals(204, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of the controller with mocked dependencies for testing.
|
||||
*
|
||||
* @return \Pterodactyl\Http\Controllers\Base\AccountKeyController
|
||||
*/
|
||||
private function getController(): AccountKeyController
|
||||
{
|
||||
return new AccountKeyController($this->alert, $this->repository, $this->keyService);
|
||||
}
|
||||
}
|
|
@ -37,8 +37,8 @@ class SetSessionDriverTest extends MiddlewareTestCase
|
|||
*/
|
||||
public function testProductionEnvironment()
|
||||
{
|
||||
$this->appMock->shouldReceive('environment')->withNoArgs()->once()->andReturn('production');
|
||||
$this->config->shouldReceive('set')->with('session.driver', 'array')->once()->andReturnNull();
|
||||
$this->config->shouldReceive('get')->once()->with('app.debug')->andReturn(false);
|
||||
$this->config->shouldReceive('set')->once()->with('session.driver', 'array')->andReturnNull();
|
||||
|
||||
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
||||
}
|
||||
|
@ -48,11 +48,10 @@ class SetSessionDriverTest extends MiddlewareTestCase
|
|||
*/
|
||||
public function testLocalEnvironment()
|
||||
{
|
||||
$this->appMock->shouldReceive('environment')->withNoArgs()->once()->andReturn('local');
|
||||
$this->appMock->shouldReceive('make')->with(LaravelDebugbar::class)->once()->andReturnSelf();
|
||||
$this->appMock->shouldReceive('disable')->withNoArgs()->once()->andReturnNull();
|
||||
|
||||
$this->config->shouldReceive('set')->with('session.driver', 'array')->once()->andReturnNull();
|
||||
$this->config->shouldReceive('get')->once()->with('app.debug')->andReturn(true);
|
||||
$this->appMock->shouldReceive('make')->once()->with(LaravelDebugbar::class)->andReturnSelf();
|
||||
$this->appMock->shouldReceive('disable')->once()->withNoArgs()->andReturnNull();
|
||||
$this->config->shouldReceive('set')->once()->with('session.driver', 'array')->andReturnNull();
|
||||
|
||||
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
||||
}
|
||||
|
|
|
@ -1,30 +1,19 @@
|
|||
<?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 Tests\Unit\Services\Allocations;
|
||||
|
||||
use Exception;
|
||||
use Mockery as m;
|
||||
use Tests\TestCase;
|
||||
use phpmock\phpunit\PHPMock;
|
||||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Services\Allocations\AssignmentService;
|
||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||
|
||||
class AssignmentServiceTest extends TestCase
|
||||
{
|
||||
use PHPMock;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
* @var \Illuminate\Database\ConnectionInterface|\Mockery\Mock
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
|
@ -34,15 +23,10 @@ class AssignmentServiceTest extends TestCase
|
|||
protected $node;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
||||
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface|\Mockery\Mock
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Allocations\AssignmentService
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
*/
|
||||
|
@ -50,19 +34,9 @@ class AssignmentServiceTest extends TestCase
|
|||
{
|
||||
parent::setUp();
|
||||
|
||||
// Due to a bug in PHP, this is necessary since we only have a single test
|
||||
// that relies on this mock. If this does not exist the test will fail to register
|
||||
// correctly.
|
||||
//
|
||||
// This can also be avoided if tests were run in isolated processes, or if that test
|
||||
// came first, but neither of those are good solutions, so this is the next best option.
|
||||
PHPMock::defineFunctionMock('\\Pterodactyl\\Services\\Allocations', 'gethostbyname');
|
||||
|
||||
$this->node = factory(Node::class)->make();
|
||||
$this->connection = m::mock(ConnectionInterface::class);
|
||||
$this->repository = m::mock(AllocationRepositoryInterface::class);
|
||||
|
||||
$this->service = new AssignmentService($this->repository, $this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,22 +46,22 @@ class AssignmentServiceTest extends TestCase
|
|||
{
|
||||
$data = [
|
||||
'allocation_ip' => '192.168.1.1',
|
||||
'allocation_ports' => ['1024'],
|
||||
'allocation_ports' => ['2222'],
|
||||
];
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.1',
|
||||
'port' => 1024,
|
||||
'port' => 2222,
|
||||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
$this->service->handle($this->node->id, $data);
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,18 +71,11 @@ class AssignmentServiceTest extends TestCase
|
|||
{
|
||||
$data = [
|
||||
'allocation_ip' => '192.168.1.1',
|
||||
'allocation_ports' => ['1024-1026'],
|
||||
'allocation_ports' => ['1025-1027'],
|
||||
];
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.1',
|
||||
'port' => 1024,
|
||||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->once()->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.1',
|
||||
|
@ -123,10 +90,17 @@ class AssignmentServiceTest extends TestCase
|
|||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.1',
|
||||
'port' => 1027,
|
||||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
])->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
$this->service->handle($this->node->id, $data);
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,23 +110,23 @@ class AssignmentServiceTest extends TestCase
|
|||
{
|
||||
$data = [
|
||||
'allocation_ip' => '192.168.1.1',
|
||||
'allocation_ports' => ['1024'],
|
||||
'allocation_ports' => ['2222'],
|
||||
'allocation_alias' => 'my.alias.net',
|
||||
];
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->with([
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->once()->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.1',
|
||||
'port' => 1024,
|
||||
'port' => 2222,
|
||||
'ip_alias' => 'my.alias.net',
|
||||
'server_id' => null,
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||
])->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
$this->service->handle($this->node->id, $data);
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,26 +135,23 @@ class AssignmentServiceTest extends TestCase
|
|||
public function testDomainNamePassedInPlaceOfIPAddress()
|
||||
{
|
||||
$data = [
|
||||
'allocation_ip' => 'test-domain.com',
|
||||
'allocation_ports' => ['1024'],
|
||||
'allocation_ip' => 'unit-test-static.pterodactyl.io',
|
||||
'allocation_ports' => ['2222'],
|
||||
];
|
||||
|
||||
$this->getFunctionMock('\\Pterodactyl\\Services\\Allocations', 'gethostbyname')
|
||||
->expects($this->once())->willReturn('192.168.1.1');
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->with([
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->once()->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.1',
|
||||
'port' => 1024,
|
||||
'ip' => '127.0.0.1',
|
||||
'port' => 2222,
|
||||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||
])->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
$this->service->handle($this->node->id, $data);
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,54 +161,55 @@ class AssignmentServiceTest extends TestCase
|
|||
{
|
||||
$data = [
|
||||
'allocation_ip' => '192.168.1.100/31',
|
||||
'allocation_ports' => ['1024'],
|
||||
'allocation_ports' => ['2222'],
|
||||
];
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->with([
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->once()->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.100',
|
||||
'port' => 1024,
|
||||
'port' => 2222,
|
||||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
])->andReturn(true);
|
||||
|
||||
$this->repository->shouldReceive('insertIgnore')->with([
|
||||
$this->repository->shouldReceive('insertIgnore')->once()->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.101',
|
||||
'port' => 1024,
|
||||
'port' => 2222,
|
||||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||
])->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
$this->service->handle($this->node->id, $data);
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a CIDR IP address with a range works properly.
|
||||
*
|
||||
* @expectedException \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
|
||||
* @expectedExceptionMessage CIDR notation only allows masks between /25 and /32.
|
||||
*/
|
||||
public function testCIDRNotatedIPAddressOutsideRangeLimit()
|
||||
{
|
||||
$data = [
|
||||
'allocation_ip' => '192.168.1.100/20',
|
||||
'allocation_ports' => ['1024'],
|
||||
'allocation_ports' => ['2222'],
|
||||
];
|
||||
|
||||
try {
|
||||
$this->service->handle($this->node->id, $data);
|
||||
} catch (Exception $exception) {
|
||||
$this->assertInstanceOf(DisplayException::class, $exception);
|
||||
$this->assertEquals(trans('exceptions.allocations.cidr_out_of_range'), $exception->getMessage());
|
||||
}
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an exception is thrown if there are too many ports.
|
||||
*
|
||||
* @expectedException \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
|
||||
* @expectedExceptionMessage Adding more than 1000 ports in a single range at once is not supported.
|
||||
*/
|
||||
public function testAllocationWithPortsExceedingLimit()
|
||||
{
|
||||
|
@ -246,22 +218,16 @@ class AssignmentServiceTest extends TestCase
|
|||
'allocation_ports' => ['5000-7000'],
|
||||
];
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
try {
|
||||
$this->service->handle($this->node->id, $data);
|
||||
} catch (Exception $exception) {
|
||||
if (! $exception instanceof DisplayException) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$this->assertInstanceOf(DisplayException::class, $exception);
|
||||
$this->assertEquals(trans('exceptions.allocations.too_many_ports'), $exception->getMessage());
|
||||
}
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an exception is thrown if an invalid port is provided.
|
||||
*
|
||||
* @expectedException \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
|
||||
* @expectedExceptionMessage The mapping provided for test123 was invalid and could not be processed.
|
||||
*/
|
||||
public function testInvalidPortProvided()
|
||||
{
|
||||
|
@ -270,42 +236,52 @@ class AssignmentServiceTest extends TestCase
|
|||
'allocation_ports' => ['test123'],
|
||||
];
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
|
||||
try {
|
||||
$this->service->handle($this->node->id, $data);
|
||||
} catch (Exception $exception) {
|
||||
if (! $exception instanceof DisplayException) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$this->assertInstanceOf(DisplayException::class, $exception);
|
||||
$this->assertEquals(trans('exceptions.allocations.invalid_mapping', ['port' => 'test123']), $exception->getMessage());
|
||||
}
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a model can be passed in place of an ID.
|
||||
* Test that ports outside of defined limits throw an error.
|
||||
*
|
||||
* @param array $ports
|
||||
*
|
||||
* @dataProvider invalidPortsDataProvider
|
||||
* @expectedException \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
|
||||
* @expectedExceptionMessage Ports in an allocation must be greater than 1024 and less than or equal to 65535.
|
||||
*/
|
||||
public function testModelCanBePassedInPlaceOfNodeModel()
|
||||
public function testPortRangeOutsideOfRangeLimits(array $ports)
|
||||
{
|
||||
$data = [
|
||||
'allocation_ip' => '192.168.1.1',
|
||||
'allocation_ports' => ['1024'],
|
||||
$data = ['allocation_ip' => '192.168.1.1', 'allocation_ports' => $ports];
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs()->andReturnNull();
|
||||
$this->getService()->handle($this->node, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide ports and ranges of ports that exceed the viable port limits for the software.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalidPortsDataProvider(): array
|
||||
{
|
||||
return [
|
||||
[['65536']],
|
||||
[['1024']],
|
||||
[['1000']],
|
||||
[['0']],
|
||||
[['65530-65540']],
|
||||
[['65540-65560']],
|
||||
[[PHP_INT_MAX]],
|
||||
];
|
||||
}
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->repository->shouldReceive('insertIgnore')->with([
|
||||
[
|
||||
'node_id' => $this->node->id,
|
||||
'ip' => '192.168.1.1',
|
||||
'port' => 1024,
|
||||
'ip_alias' => null,
|
||||
'server_id' => null,
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||
|
||||
$this->service->handle($this->node, $data);
|
||||
/**
|
||||
* Returns an instance of the service with mocked dependencies for testing.
|
||||
*
|
||||
* @return \Pterodactyl\Services\Allocations\AssignmentService
|
||||
*/
|
||||
private function getService(): AssignmentService
|
||||
{
|
||||
return new AssignmentService($this->repository, $this->connection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ namespace Tests\Unit\Services\Eggs\Variables;
|
|||
|
||||
use Mockery as m;
|
||||
use Tests\TestCase;
|
||||
use BadMethodCallException;
|
||||
use Pterodactyl\Models\EggVariable;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Pterodactyl\Services\Eggs\Variables\VariableCreationService;
|
||||
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||
|
||||
|
@ -13,12 +15,12 @@ class VariableCreationServiceTest extends TestCase
|
|||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface|\Mockery\Mock
|
||||
*/
|
||||
protected $repository;
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Eggs\Variables\VariableCreationService
|
||||
* @var \Illuminate\Contracts\Validation\Factory|\Mockery\Mock
|
||||
*/
|
||||
protected $service;
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
|
@ -28,8 +30,7 @@ class VariableCreationServiceTest extends TestCase
|
|||
parent::setUp();
|
||||
|
||||
$this->repository = m::mock(EggVariableRepositoryInterface::class);
|
||||
|
||||
$this->service = new VariableCreationService($this->repository);
|
||||
$this->validator = m::mock(Factory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,7 +47,7 @@ class VariableCreationServiceTest extends TestCase
|
|||
'env_variable' => 'TEST_VAR_123',
|
||||
]))->once()->andReturn(new EggVariable);
|
||||
|
||||
$this->assertInstanceOf(EggVariable::class, $this->service->handle(1, $data));
|
||||
$this->assertInstanceOf(EggVariable::class, $this->getService()->handle(1, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +63,7 @@ class VariableCreationServiceTest extends TestCase
|
|||
'env_variable' => 'TEST_VAR_123',
|
||||
]))->once()->andReturn(new EggVariable);
|
||||
|
||||
$this->assertInstanceOf(EggVariable::class, $this->service->handle(1, $data));
|
||||
$this->assertInstanceOf(EggVariable::class, $this->getService()->handle(1, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,18 +82,20 @@ class VariableCreationServiceTest extends TestCase
|
|||
'user_editable' => false,
|
||||
]))->once()->andReturn(new EggVariable);
|
||||
|
||||
$this->assertInstanceOf(EggVariable::class, $this->service->handle(1, $data));
|
||||
$this->assertInstanceOf(EggVariable::class, $this->getService()->handle(1, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that all of the reserved variables defined in the model trigger an exception.
|
||||
*
|
||||
* @param string $variable
|
||||
*
|
||||
* @dataProvider reservedNamesProvider
|
||||
* @expectedException \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException
|
||||
*/
|
||||
public function testExceptionIsThrownIfEnvironmentVariableIsInListOfReservedNames(string $variable)
|
||||
{
|
||||
$this->service->handle(1, ['env_variable' => $variable]);
|
||||
$this->getService()->handle(1, ['env_variable' => $variable]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,7 +109,49 @@ class VariableCreationServiceTest extends TestCase
|
|||
'egg_id' => 1,
|
||||
]))->once()->andReturn(new EggVariable);
|
||||
|
||||
$this->assertInstanceOf(EggVariable::class, $this->service->handle(1, $data));
|
||||
$this->assertInstanceOf(EggVariable::class, $this->getService()->handle(1, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that validation errors due to invalid rules are caught and handled properly.
|
||||
*
|
||||
* @expectedException \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException
|
||||
* @expectedExceptionMessage The validation rule "hodor_door" is not a valid rule for this application.
|
||||
*/
|
||||
public function testInvalidValidationRulesResultInException()
|
||||
{
|
||||
$data = ['env_variable' => 'TEST_VAR_123', 'rules' => 'string|hodorDoor'];
|
||||
|
||||
$this->validator->shouldReceive('make')->once()
|
||||
->with(['__TEST' => 'test'], ['__TEST' => 'string|hodorDoor'])
|
||||
->andReturnSelf();
|
||||
|
||||
$this->validator->shouldReceive('fails')->once()
|
||||
->withNoArgs()
|
||||
->andThrow(new BadMethodCallException('Method [validateHodorDoor] does not exist.'));
|
||||
|
||||
$this->getService()->handle(1, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an exception not stemming from a bad rule is not caught.
|
||||
*
|
||||
* @expectedException \BadMethodCallException
|
||||
* @expectedExceptionMessage Received something, but no expectations were specified.
|
||||
*/
|
||||
public function testExceptionNotCausedByBadRuleIsNotCaught()
|
||||
{
|
||||
$data = ['env_variable' => 'TEST_VAR_123', 'rules' => 'string'];
|
||||
|
||||
$this->validator->shouldReceive('make')->once()
|
||||
->with(['__TEST' => 'test'], ['__TEST' => 'string'])
|
||||
->andReturnSelf();
|
||||
|
||||
$this->validator->shouldReceive('fails')->once()
|
||||
->withNoArgs()
|
||||
->andThrow(new BadMethodCallException('Received something, but no expectations were specified.'));
|
||||
|
||||
$this->getService()->handle(1, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,4 +169,14 @@ class VariableCreationServiceTest extends TestCase
|
|||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of the service with mocked dependencies for testing.
|
||||
*
|
||||
* @return \Pterodactyl\Services\Eggs\Variables\VariableCreationService
|
||||
*/
|
||||
private function getService(): VariableCreationService
|
||||
{
|
||||
return new VariableCreationService($this->repository, $this->validator);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ namespace Tests\Unit\Services\Eggs\Variables;
|
|||
use Exception;
|
||||
use Mockery as m;
|
||||
use Tests\TestCase;
|
||||
use BadMethodCallException;
|
||||
use Pterodactyl\Models\EggVariable;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Services\Eggs\Variables\VariableUpdateService;
|
||||
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||
|
@ -15,17 +17,17 @@ class VariableUpdateServiceTest extends TestCase
|
|||
/**
|
||||
* @var \Pterodactyl\Models\EggVariable|\Mockery\Mock
|
||||
*/
|
||||
protected $model;
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface|\Mockery\Mock
|
||||
*/
|
||||
protected $repository;
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Eggs\Variables\VariableUpdateService
|
||||
* @var \Illuminate\Contracts\Validation\Factory|\Mockery\Mock
|
||||
*/
|
||||
protected $service;
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
|
@ -36,8 +38,7 @@ class VariableUpdateServiceTest extends TestCase
|
|||
|
||||
$this->model = factory(EggVariable::class)->make();
|
||||
$this->repository = m::mock(EggVariableRepositoryInterface::class);
|
||||
|
||||
$this->service = new VariableUpdateService($this->repository);
|
||||
$this->validator = m::mock(Factory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +52,7 @@ class VariableUpdateServiceTest extends TestCase
|
|||
'user_editable' => false,
|
||||
]))->once()->andReturn(true);
|
||||
|
||||
$this->assertTrue($this->service->handle($this->model, []));
|
||||
$this->assertTrue($this->getService()->handle($this->model, []));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,7 +68,7 @@ class VariableUpdateServiceTest extends TestCase
|
|||
'default_value' => '',
|
||||
]))->once()->andReturn(true);
|
||||
|
||||
$this->assertTrue($this->service->handle($this->model, ['default_value' => null]));
|
||||
$this->assertTrue($this->getService()->handle($this->model, ['default_value' => null]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +90,7 @@ class VariableUpdateServiceTest extends TestCase
|
|||
'env_variable' => 'TEST_VAR_123',
|
||||
]))->once()->andReturn(true);
|
||||
|
||||
$this->assertTrue($this->service->handle($this->model, ['env_variable' => 'TEST_VAR_123']));
|
||||
$this->assertTrue($this->getService()->handle($this->model, ['env_variable' => 'TEST_VAR_123']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,7 +108,7 @@ class VariableUpdateServiceTest extends TestCase
|
|||
'description' => '',
|
||||
]))->once()->andReturn(true);
|
||||
|
||||
$this->assertTrue($this->service->handle($this->model, ['options' => null, 'description' => null]));
|
||||
$this->assertTrue($this->getService()->handle($this->model, ['options' => null, 'description' => null]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,7 +130,7 @@ class VariableUpdateServiceTest extends TestCase
|
|||
'env_variable' => 'TEST_VAR_123',
|
||||
]))->once()->andReturn(true);
|
||||
|
||||
$this->assertTrue($this->service->handle($this->model, ['user_viewable' => 123456, 'env_variable' => 'TEST_VAR_123']));
|
||||
$this->assertTrue($this->getService()->handle($this->model, ['user_viewable' => 123456, 'env_variable' => 'TEST_VAR_123']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,7 +146,7 @@ class VariableUpdateServiceTest extends TestCase
|
|||
])->once()->andReturn(1);
|
||||
|
||||
try {
|
||||
$this->service->handle($this->model, ['env_variable' => 'TEST_VAR_123']);
|
||||
$this->getService()->handle($this->model, ['env_variable' => 'TEST_VAR_123']);
|
||||
} catch (Exception $exception) {
|
||||
$this->assertInstanceOf(DisplayException::class, $exception);
|
||||
$this->assertEquals(trans('exceptions.service.variables.env_not_unique', [
|
||||
|
@ -162,7 +163,51 @@ class VariableUpdateServiceTest extends TestCase
|
|||
*/
|
||||
public function testExceptionIsThrownIfEnvironmentVariableIsInListOfReservedNames(string $variable)
|
||||
{
|
||||
$this->service->handle($this->model, ['env_variable' => $variable]);
|
||||
$this->getService()->handle($this->model, ['env_variable' => $variable]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that validation errors due to invalid rules are caught and handled properly.
|
||||
*
|
||||
* @expectedException \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException
|
||||
* @expectedExceptionMessage The validation rule "hodor_door" is not a valid rule for this application.
|
||||
*/
|
||||
public function testInvalidValidationRulesResultInException()
|
||||
{
|
||||
$data = ['env_variable' => 'TEST_VAR_123', 'rules' => 'string|hodorDoor'];
|
||||
|
||||
$this->repository->shouldReceive('setColumns->findCountWhere')->once()->andReturn(0);
|
||||
|
||||
$this->validator->shouldReceive('make')->once()
|
||||
->with(['__TEST' => 'test'], ['__TEST' => 'string|hodorDoor'])
|
||||
->andReturnSelf();
|
||||
|
||||
$this->validator->shouldReceive('fails')->once()
|
||||
->withNoArgs()
|
||||
->andThrow(new BadMethodCallException('Method [validateHodorDoor] does not exist.'));
|
||||
|
||||
$this->getService()->handle($this->model, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an exception not stemming from a bad rule is not caught.
|
||||
*
|
||||
* @expectedException \BadMethodCallException
|
||||
* @expectedExceptionMessage Received something, but no expectations were specified.
|
||||
*/
|
||||
public function testExceptionNotCausedByBadRuleIsNotCaught()
|
||||
{
|
||||
$data = ['rules' => 'string'];
|
||||
|
||||
$this->validator->shouldReceive('make')->once()
|
||||
->with(['__TEST' => 'test'], ['__TEST' => 'string'])
|
||||
->andReturnSelf();
|
||||
|
||||
$this->validator->shouldReceive('fails')->once()
|
||||
->withNoArgs()
|
||||
->andThrow(new BadMethodCallException('Received something, but no expectations were specified.'));
|
||||
|
||||
$this->getService()->handle($this->model, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,4 +225,14 @@ class VariableUpdateServiceTest extends TestCase
|
|||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of the service with mocked dependencies for testing.
|
||||
*
|
||||
* @return \Pterodactyl\Services\Eggs\Variables\VariableUpdateService
|
||||
*/
|
||||
private function getService(): VariableUpdateService
|
||||
{
|
||||
return new VariableUpdateService($this->repository, $this->validator);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,14 +121,18 @@ class StartupModificationServiceTest extends TestCase
|
|||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->validatorService->shouldReceive('setUserLevel')->with(User::USER_LEVEL_ADMIN)->once()->andReturnNull();
|
||||
$this->validatorService->shouldReceive('handle')->with(456, ['test' => 'abcd1234'])->once()->andReturn(
|
||||
collect([(object) ['id' => 1, 'value' => 'stored-value']])
|
||||
collect([(object) ['id' => 1, 'value' => 'stored-value'], (object) ['id' => 2, 'value' => null]])
|
||||
);
|
||||
|
||||
$this->serverVariableRepository->shouldReceive('withoutFreshModel')->withNoArgs()->once()->andReturnSelf();
|
||||
$this->serverVariableRepository->shouldReceive('updateOrCreate')->with([
|
||||
$this->serverVariableRepository->shouldReceive('withoutFreshModel->updateOrCreate')->once()->with([
|
||||
'server_id' => $model->id,
|
||||
'variable_id' => 1,
|
||||
], ['variable_value' => 'stored-value'])->once()->andReturnNull();
|
||||
], ['variable_value' => 'stored-value'])->andReturnNull();
|
||||
|
||||
$this->serverVariableRepository->shouldReceive('withoutFreshModel->updateOrCreate')->once()->with([
|
||||
'server_id' => $model->id,
|
||||
'variable_id' => 2,
|
||||
], ['variable_value' => ''])->andReturnNull();
|
||||
|
||||
$this->eggRepository->shouldReceive('setColumns->find')->once()->with($eggModel->id)->andReturn($eggModel);
|
||||
|
||||
|
|
Loading…
Reference in a new issue