Merge branch 'release/v0.7.15'

This commit is contained in:
Dane Everitt 2019-08-03 15:07:18 -07:00
commit bd5875084b
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
42 changed files with 307 additions and 193 deletions

View file

@ -3,6 +3,35 @@ 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. This project follows [Semantic Versioning](http://semver.org) guidelines.
## v0.7.15 (Derelict Dermodactylus)
### Fixed
* Fixes support for PHP 7.3 when running `composer install` commands due to a dependency that needed updating.
* Automatic allocation field when creating a new node (or updating one) should now properly remeber its old
value when showing an error state.
* Mass deleting files now executes properly and doesn't result in a JS console error.
* Scrolling on email settings page now works.
* Database host management will now properly display an error message to the user when there is any type of MySQL related
error encountered during creation or update.
* Two-factor tokens generated when a company name has a space in it will now properly be parsed on iOS authenticator devices.
* Fixed 500 error when trying to request subuser's from a server in the application API.
* Creating a node allocation via the API no longer requires an alias field be passed through in the request.
* Bulk power management for servers via the CLI no longer fails when servers span multiple nodes.
### Added
* Server listing view now displays the total used disk space for each server.
* Client API endpoint to list all servers now supports an additional `?filter=subuser-of|all|admin|owner` parameter to
return different groupings of servers. The default value is `subuser-of` which will include all of the user's servers
that they are the owner of, as well as all servers they're a subuser of.
* Added back ability to toggle OOM killer status on a per-server basis.
* Added `LOCK TABLES` permission for generated database users.
### Changed
* Updated Paper egg to not download `server.properties` each time. [parkervcp/eggs#260](https://github.com/parkervcp/eggs/issues/260)
* Insurgency egg now uses the proper dedicated server ID.
* Teamspeak egg updated with improved installation process and grabbing latest versions.
* OOM killer disabled by default on all new servers.
* Passwords generated for MySQL now include special characters and are 24 characters in length.
## v0.7.14 (Derelict Dermodactylus) ## v0.7.14 (Derelict Dermodactylus)
### Fixed ### Fixed
* **[SECURITY]** Fixes an XSS vulnerability when performing certain actions in the file manager. * **[SECURITY]** Fixes an XSS vulnerability when performing certain actions in the file manager.

View file

@ -102,7 +102,10 @@ class BulkPowerActionCommand extends Command
$bar->clear(); $bar->clear();
try { try {
$this->powerRepository->setServer($server)->sendSignal($action); $this->powerRepository
->setNode($server->node)
->setServer($server)
->sendSignal($action);
} catch (RequestException $exception) { } catch (RequestException $exception) {
$this->output->error(trans('command/messages.server.power.action_failed', [ $this->output->error(trans('command/messages.server.power.action_failed', [
'name' => $server->name, 'name' => $server->name,

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Http\Controllers\Admin; namespace Pterodactyl\Http\Controllers\Admin;
use Exception;
use PDOException; use PDOException;
use Illuminate\View\View; use Illuminate\View\View;
use Pterodactyl\Models\DatabaseHost; use Pterodactyl\Models\DatabaseHost;
@ -118,17 +119,22 @@ class DatabaseController extends Controller
* @param \Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest $request * @param \Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest $request
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function create(DatabaseHostFormRequest $request): RedirectResponse public function create(DatabaseHostFormRequest $request): RedirectResponse
{ {
try { try {
$host = $this->creationService->handle($request->normalize()); $host = $this->creationService->handle($request->normalize());
} catch (PDOException $ex) { } catch (Exception $exception) {
$this->alert->danger($ex->getMessage())->flash(); if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
return redirect()->route('admin.databases'); redirect()->route('admin.databases')->withInput($request->validated());
} else {
throw $exception;
}
} }
$this->alert->success('Successfully created a new database host on the system.')->flash(); $this->alert->success('Successfully created a new database host on the system.')->flash();
@ -143,8 +149,7 @@ class DatabaseController extends Controller
* @param \Pterodactyl\Models\DatabaseHost $host * @param \Pterodactyl\Models\DatabaseHost $host
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function update(DatabaseHostFormRequest $request, DatabaseHost $host): RedirectResponse public function update(DatabaseHostFormRequest $request, DatabaseHost $host): RedirectResponse
{ {
@ -153,9 +158,17 @@ class DatabaseController extends Controller
try { try {
$this->updateService->handle($host->id, $request->normalize()); $this->updateService->handle($host->id, $request->normalize());
$this->alert->success('Database host was updated successfully.')->flash(); $this->alert->success('Database host was updated successfully.')->flash();
} catch (PDOException $ex) { } catch (Exception $exception) {
$this->alert->danger($ex->getMessage())->flash(); // Catch any SQL related exceptions and display them back to the user, otherwise just
// throw the exception like normal and move on with it.
if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
$redirect->withInput($request->normalize()); $redirect->withInput($request->normalize());
} else {
throw $exception;
}
} }
return $redirect; return $redirect;

View file

@ -516,7 +516,7 @@ class ServersController extends Controller
$this->buildModificationService->handle($server, $request->only([ $this->buildModificationService->handle($server, $request->only([
'allocation_id', 'add_allocations', 'remove_allocations', 'allocation_id', 'add_allocations', 'remove_allocations',
'memory', 'swap', 'io', 'cpu', 'disk', 'memory', 'swap', 'io', 'cpu', 'disk',
'database_limit', 'allocation_limit', 'database_limit', 'allocation_limit', 'oom_disabled',
])); ]));
$this->alert->success(trans('admin/server.alerts.build_updated'))->flash(); $this->alert->success(trans('admin/server.alerts.build_updated'))->flash();
@ -589,8 +589,7 @@ class ServersController extends Controller
* @param int $server * @param int $server
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
* *
* @throws \Exception * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/ */
public function resetDatabasePassword(Request $request, $server) public function resetDatabasePassword(Request $request, $server)
{ {
@ -599,7 +598,7 @@ class ServersController extends Controller
['id', '=', $request->input('database')], ['id', '=', $request->input('database')],
]); ]);
$this->databasePasswordService->handle($database, str_random(24)); $this->databasePasswordService->handle($database);
return response('', 204); return response('', 204);
} }

View file

@ -87,12 +87,11 @@ class DatabaseController extends ApplicationApiController
* @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\ServerDatabaseWriteRequest $request * @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\ServerDatabaseWriteRequest $request
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function resetPassword(ServerDatabaseWriteRequest $request): Response public function resetPassword(ServerDatabaseWriteRequest $request): Response
{ {
$this->databasePasswordService->handle($request->getModel(Database::class), str_random(24)); $this->databasePasswordService->handle($request->getModel(Database::class));
return response('', 204); return response('', 204);
} }

View file

@ -35,7 +35,26 @@ class ClientController extends ClientApiController
*/ */
public function index(GetServersRequest $request): array public function index(GetServersRequest $request): array
{ {
$servers = $this->repository->filterUserAccessServers($request->user(), User::FILTER_LEVEL_SUBUSER, config('pterodactyl.paginate.frontend.servers')); // Check for the filter parameter on the request.
switch ($request->input('filter')) {
case 'all':
$filter = User::FILTER_LEVEL_ALL;
break;
case 'admin':
$filter = User::FILTER_LEVEL_ADMIN;
break;
case 'owner':
$filter = User::FILTER_LEVEL_OWNER;
break;
case 'subuser-of':
default:
$filter = User::FILTER_LEVEL_SUBUSER;
break;
}
$servers = $this->repository->filterUserAccessServers(
$request->user(), $filter, config('pterodactyl.paginate.frontend.servers')
);
return $this->fractal->collection($servers) return $this->fractal->collection($servers)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith($this->getTransformer(ServerTransformer::class))

View file

@ -135,15 +135,13 @@ class DatabaseController extends Controller
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* *
* @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function update(Request $request): JsonResponse public function update(Request $request): JsonResponse
{ {
$this->authorize('reset-db-password', $request->attributes->get('server')); $this->authorize('reset-db-password', $request->attributes->get('server'));
$password = str_random(24); $password = $this->passwordService->handle($request->attributes->get('database'));
$this->passwordService->handle($request->attributes->get('database'), $password);
return response()->json(['password' => $password]); return response()->json(['password' => $password]);
} }

View file

@ -40,7 +40,7 @@ class StoreAllocationRequest extends ApplicationApiRequest
return [ return [
'allocation_ip' => $data['ip'], 'allocation_ip' => $data['ip'],
'allocation_ports' => $data['ports'], 'allocation_ports' => $data['ports'],
'allocation_alias' => $data['alias'], 'allocation_alias' => $data['alias'] ?? null,
]; ];
} }
} }

View file

@ -41,6 +41,7 @@ class StoreServerRequest extends ApplicationApiRequest
'startup' => $rules['startup'], 'startup' => $rules['startup'],
'environment' => 'present|array', 'environment' => 'present|array',
'skip_scripts' => 'sometimes|boolean', 'skip_scripts' => 'sometimes|boolean',
'oom_disabled' => 'sometimes|boolean',
// Resource limitations // Resource limitations
'limits' => 'required|array', 'limits' => 'required|array',

View file

@ -18,6 +18,7 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
return [ return [
'allocation' => $rules['allocation_id'], 'allocation' => $rules['allocation_id'],
'oom_disabled' => $rules['oom_disabled'],
'limits' => 'sometimes|array', 'limits' => 'sometimes|array',
'limits.memory' => $this->requiredToOptional('memory', $rules['memory'], true), 'limits.memory' => $this->requiredToOptional('memory', $rules['memory'], true),

View file

@ -28,6 +28,16 @@ class Server extends Model implements CleansAttributes, ValidableContract
*/ */
protected $table = 'servers'; protected $table = 'servers';
/**
* Default values when creating the model. We want to switch to disabling OOM killer
* on server instances unless the user specifies otherwise in the request.
*
* @var array
*/
protected $attributes = [
'oom_disabled' => true,
];
/** /**
* The attributes that should be mutated to dates. * The attributes that should be mutated to dates.
* *
@ -53,6 +63,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
'swap' => 'required', 'swap' => 'required',
'io' => 'required', 'io' => 'required',
'cpu' => 'required', 'cpu' => 'required',
'oom_disabled' => 'sometimes',
'disk' => 'required', 'disk' => 'required',
'nest_id' => 'required', 'nest_id' => 'required',
'egg_id' => 'required', 'egg_id' => 'required',
@ -79,6 +90,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
'swap' => 'numeric|min:-1', 'swap' => 'numeric|min:-1',
'io' => 'numeric|between:10,1000', 'io' => 'numeric|between:10,1000',
'cpu' => 'numeric|min:0', 'cpu' => 'numeric|min:0',
'oom_disabled' => 'boolean',
'disk' => 'numeric|min:0', 'disk' => 'numeric|min:0',
'allocation_id' => 'bail|unique:servers|exists:allocations,id', 'allocation_id' => 'bail|unique:servers|exists:allocations,id',
'nest_id' => 'exists:nests,id', 'nest_id' => 'exists:nests,id',
@ -107,7 +119,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
'disk' => 'integer', 'disk' => 'integer',
'io' => 'integer', 'io' => 'integer',
'cpu' => 'integer', 'cpu' => 'integer',
'oom_disabled' => 'integer', 'oom_disabled' => 'boolean',
'allocation_id' => 'integer', 'allocation_id' => 'integer',
'nest_id' => 'integer', 'nest_id' => 'integer',
'egg_id' => 'integer', 'egg_id' => 'integer',
@ -156,11 +168,11 @@ class Server extends Model implements CleansAttributes, ValidableContract
/** /**
* Gets the subusers associated with a server. * Gets the subusers associated with a server.
* *
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/ */
public function subusers() public function subusers()
{ {
return $this->hasMany(Subuser::class); return $this->hasManyThrough(User::class, Subuser::class, 'server_id', 'id', 'id', 'user_id');
} }
/** /**

View file

@ -153,7 +153,7 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor
public function assignUserToDatabase(string $database, string $username, string $remote): bool public function assignUserToDatabase(string $database, string $username, string $remote): bool
{ {
return $this->run(sprintf( return $this->run(sprintf(
'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE ON `%s`.* TO `%s`@`%s`', 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, LOCK TABLES, EXECUTE ON `%s`.* TO `%s`@`%s`',
$database, $database,
$username, $username,
$remote $remote

View file

@ -2,7 +2,9 @@
namespace Pterodactyl\Services\Databases; namespace Pterodactyl\Services\Databases;
use Exception;
use Pterodactyl\Models\Database; use Pterodactyl\Models\Database;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter; use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection; use Pterodactyl\Extensions\DynamicDatabaseConnection;
@ -54,22 +56,30 @@ class DatabasePasswordService
* Updates a password for a given database. * Updates a password for a given database.
* *
* @param \Pterodactyl\Models\Database|int $database * @param \Pterodactyl\Models\Database|int $database
* @param string $password * @return string
* @return bool
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function handle($database, string $password): bool public function handle(Database $database): string
{ {
if (! $database instanceof Database) { $password = str_random(24);
$database = $this->repository->find($database); // Given a random string of characters, randomly loop through the characters and replace some
// with special characters to avoid issues with MySQL password requirements on some servers.
try {
for ($i = 0; $i < random_int(2, 6); $i++) {
$character = ['!', '@', '=', '.', '+', '^'][random_int(0, 5)];
$password = substr_replace($password, $character, random_int(0, 23), 1);
}
} catch (Exception $exception) {
// Just log the error and hope for the best at this point.
Log::error($exception);
} }
$this->connection->transaction(function () use ($database, $password) {
$this->dynamic->set('dynamic', $database->database_host_id); $this->dynamic->set('dynamic', $database->database_host_id);
$this->connection->beginTransaction();
$updated = $this->repository->withoutFreshModel()->update($database->id, [ $this->repository->withoutFreshModel()->update($database->id, [
'password' => $this->encrypter->encrypt($password), 'password' => $this->encrypter->encrypt($password),
]); ]);
@ -77,10 +87,8 @@ class DatabasePasswordService
$this->repository->createUser($database->username, $database->remote, $password); $this->repository->createUser($database->username, $database->remote, $password);
$this->repository->assignUserToDatabase($database->database, $database->username, $database->remote); $this->repository->assignUserToDatabase($database->database, $database->username, $database->remote);
$this->repository->flush(); $this->repository->flush();
});
unset($password); return $password;
$this->connection->commit();
return $updated;
} }
} }

View file

@ -65,13 +65,11 @@ class HostCreationService
* @param array $data * @param array $data
* @return \Pterodactyl\Models\DatabaseHost * @return \Pterodactyl\Models\DatabaseHost
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function handle(array $data): DatabaseHost public function handle(array $data): DatabaseHost
{ {
$this->connection->beginTransaction(); return $this->connection->transaction(function () use ($data) {
$host = $this->repository->create([ $host = $this->repository->create([
'password' => $this->encrypter->encrypt(array_get($data, 'password')), 'password' => $this->encrypter->encrypt(array_get($data, 'password')),
'name' => array_get($data, 'name'), 'name' => array_get($data, 'name'),
@ -85,8 +83,8 @@ class HostCreationService
// Confirm access using the provided credentials before saving data. // Confirm access using the provided credentials before saving data.
$this->dynamic->set('dynamic', $host); $this->dynamic->set('dynamic', $host);
$this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual'); $this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual');
$this->connection->commit();
return $host; return $host;
});
} }
} }

View file

@ -71,10 +71,9 @@ class HostUpdateService
* *
* @param int $hostId * @param int $hostId
* @param array $data * @param array $data
* @return mixed * @return \Pterodactyl\Models\DatabaseHost
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function handle(int $hostId, array $data): DatabaseHost public function handle(int $hostId, array $data): DatabaseHost
{ {
@ -84,13 +83,12 @@ class HostUpdateService
unset($data['password']); unset($data['password']);
} }
$this->connection->beginTransaction(); return $this->connection->transaction(function () use ($data, $hostId) {
$host = $this->repository->update($hostId, $data); $host = $this->repository->update($hostId, $data);
$this->dynamic->set('dynamic', $host); $this->dynamic->set('dynamic', $host);
$this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual'); $this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual');
$this->connection->commit();
return $host; return $host;
});
} }
} }

View file

@ -85,6 +85,7 @@ class BuildModificationService
} }
$server = $this->repository->withFreshModel()->update($server->id, [ $server = $this->repository->withFreshModel()->update($server->id, [
'oom_disabled' => array_get($data, 'oom_disabled'),
'memory' => array_get($data, 'memory'), 'memory' => array_get($data, 'memory'),
'swap' => array_get($data, 'swap'), 'swap' => array_get($data, 'swap'),
'io' => array_get($data, 'io'), 'io' => array_get($data, 'io'),
@ -97,6 +98,7 @@ class BuildModificationService
$allocations = $this->allocationRepository->findWhere([['server_id', '=', $server->id]]); $allocations = $this->allocationRepository->findWhere([['server_id', '=', $server->id]]);
$build['oom_disabled'] = $server->oom_disabled;
$build['memory'] = (int) $server->memory; $build['memory'] = (int) $server->memory;
$build['swap'] = (int) $server->swap; $build['swap'] = (int) $server->swap;
$build['io'] = (int) $server->io; $build['io'] = (int) $server->io;

View file

@ -70,6 +70,7 @@ class ServerConfigurationStructureService
return $item->pluck('port'); return $item->pluck('port');
})->toArray(), })->toArray(),
'env' => $this->environment->handle($server), 'env' => $this->environment->handle($server),
'oom_disabled' => $server->oom_disabled,
'memory' => (int) $server->memory, 'memory' => (int) $server->memory,
'swap' => (int) $server->swap, 'swap' => (int) $server->swap,
'io' => (int) $server->io, 'io' => (int) $server->io,

View file

@ -227,7 +227,7 @@ class ServerCreationService
'disk' => array_get($data, 'disk'), 'disk' => array_get($data, 'disk'),
'io' => array_get($data, 'io'), 'io' => array_get($data, 'io'),
'cpu' => array_get($data, 'cpu'), 'cpu' => array_get($data, 'cpu'),
'oom_disabled' => false, 'oom_disabled' => array_get($data, 'oom_disabled', true),
'allocation_id' => array_get($data, 'allocation_id'), 'allocation_id' => array_get($data, 'allocation_id'),
'nest_id' => array_get($data, 'nest_id'), 'nest_id' => array_get($data, 'nest_id'),
'egg_id' => array_get($data, 'egg_id'), 'egg_id' => array_get($data, 'egg_id'),

View file

@ -71,7 +71,7 @@ class TwoFactorSetupService
'totp_secret' => $this->encrypter->encrypt($secret), 'totp_secret' => $this->encrypter->encrypt($secret),
]); ]);
$company = $this->config->get('app.name'); $company = preg_replace('/\s/', '', $this->config->get('app.name'));
return sprintf( return sprintf(
'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s', 'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s',

View file

@ -45,7 +45,7 @@
"barryvdh/laravel-ide-helper": "^2.5", "barryvdh/laravel-ide-helper": "^2.5",
"codedungeon/phpunit-result-printer": "^0.17.1", "codedungeon/phpunit-result-printer": "^0.17.1",
"filp/whoops": "^2.1", "filp/whoops": "^2.1",
"friendsofphp/php-cs-fixer": "^2.11.1", "friendsofphp/php-cs-fixer": "^2.15.1",
"fzaninotto/faker": "^1.6", "fzaninotto/faker": "^1.6",
"mockery/mockery": "^1.0", "mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0", "nunomaduro/collision": "^2.0",

59
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "8a99f4996405b8080a0dcabb2609c39d", "content-hash": "175a2431d23dd32f79b42bbc8030ace1",
"packages": [ "packages": [
{ {
"name": "appstract/laravel-blade-directives", "name": "appstract/laravel-blade-directives",
@ -46,9 +46,9 @@
"authors": [ "authors": [
{ {
"name": "Gijs Jorissen", "name": "Gijs Jorissen",
"role": "Developer",
"email": "hello@appstract.team", "email": "hello@appstract.team",
"homepage": "https://appstract.team", "homepage": "https://appstract.team"
"role": "Developer"
} }
], ],
"description": "Handy Blade directives", "description": "Handy Blade directives",
@ -973,12 +973,12 @@
"version": "3.0.0", "version": "3.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ivanakimov/hashids.php.git", "url": "https://github.com/vinkla/hashids.git",
"reference": "b6c61142bfe36d43740a5419d11c351dddac0458" "reference": "b6c61142bfe36d43740a5419d11c351dddac0458"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ivanakimov/hashids.php/zipball/b6c61142bfe36d43740a5419d11c351dddac0458", "url": "https://api.github.com/repos/vinkla/hashids/zipball/b6c61142bfe36d43740a5419d11c351dddac0458",
"reference": "b6c61142bfe36d43740a5419d11c351dddac0458", "reference": "b6c61142bfe36d43740a5419d11c351dddac0458",
"shasum": "" "shasum": ""
}, },
@ -2149,8 +2149,8 @@
"authors": [ "authors": [
{ {
"name": "Antonio Carlos Ribeiro", "name": "Antonio Carlos Ribeiro",
"email": "acr@antoniocarlosribeiro.com", "role": "Creator & Designer",
"role": "Creator & Designer" "email": "acr@antoniocarlosribeiro.com"
} }
], ],
"description": "A One Time Password Authentication package, compatible with Google Authenticator.", "description": "A One Time Password Authentication package, compatible with Google Authenticator.",
@ -2259,9 +2259,9 @@
"authors": [ "authors": [
{ {
"name": "Dries Vints", "name": "Dries Vints",
"role": "Maintainer",
"email": "dries.vints@gmail.com", "email": "dries.vints@gmail.com",
"homepage": "http://driesvints.com", "homepage": "http://driesvints.com"
"role": "Maintainer"
} }
], ],
"description": "Prologue Alerts is a package that handles global site messages.", "description": "Prologue Alerts is a package that handles global site messages.",
@ -2720,9 +2720,9 @@
"authors": [ "authors": [
{ {
"name": "Jarek Tkaczyk", "name": "Jarek Tkaczyk",
"role": "Developer",
"email": "jarek@softonsofa.com", "email": "jarek@softonsofa.com",
"homepage": "https://softonsofa.com/", "homepage": "https://softonsofa.com/"
"role": "Developer"
} }
], ],
"description": "Flexible Searchable, Mappable, Metable, Validation and more extensions for Laravel Eloquent ORM.", "description": "Flexible Searchable, Mappable, Metable, Validation and more extensions for Laravel Eloquent ORM.",
@ -2822,9 +2822,9 @@
"authors": [ "authors": [
{ {
"name": "Jarek Tkaczyk", "name": "Jarek Tkaczyk",
"role": "Developer",
"email": "jarek@softonsofa.com", "email": "jarek@softonsofa.com",
"homepage": "http://softonsofa.com/", "homepage": "http://softonsofa.com/"
"role": "Developer"
} }
], ],
"description": "Laravel Eloquent hooks system.", "description": "Laravel Eloquent hooks system.",
@ -4968,16 +4968,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v2.13.1", "version": "v2.15.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
"reference": "54814c62d5beef3ba55297b9b3186ed8b8a1b161" "reference": "20064511ab796593a3990669eff5f5b535001f7c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/54814c62d5beef3ba55297b9b3186ed8b8a1b161", "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/20064511ab796593a3990669eff5f5b535001f7c",
"reference": "54814c62d5beef3ba55297b9b3186ed8b8a1b161", "reference": "20064511ab796593a3990669eff5f5b535001f7c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4986,7 +4986,7 @@
"doctrine/annotations": "^1.2", "doctrine/annotations": "^1.2",
"ext-json": "*", "ext-json": "*",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"php": "^5.6 || >=7.0 <7.3", "php": "^5.6 || ^7.0",
"php-cs-fixer/diff": "^1.3", "php-cs-fixer/diff": "^1.3",
"symfony/console": "^3.4.17 || ^4.1.6", "symfony/console": "^3.4.17 || ^4.1.6",
"symfony/event-dispatcher": "^3.0 || ^4.0", "symfony/event-dispatcher": "^3.0 || ^4.0",
@ -4998,21 +4998,18 @@
"symfony/process": "^3.0 || ^4.0", "symfony/process": "^3.0 || ^4.0",
"symfony/stopwatch": "^3.0 || ^4.0" "symfony/stopwatch": "^3.0 || ^4.0"
}, },
"conflict": {
"hhvm": "*"
},
"require-dev": { "require-dev": {
"johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0",
"justinrainbow/json-schema": "^5.0", "justinrainbow/json-schema": "^5.0",
"keradus/cli-executor": "^1.1", "keradus/cli-executor": "^1.2",
"mikey179/vfsstream": "^1.6", "mikey179/vfsstream": "^1.6",
"php-coveralls/php-coveralls": "^2.1", "php-coveralls/php-coveralls": "^2.1",
"php-cs-fixer/accessible-object": "^1.0", "php-cs-fixer/accessible-object": "^1.0",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.0.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.0.1", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1",
"phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1", "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1",
"phpunitgoodpractices/traits": "^1.5.1", "phpunitgoodpractices/traits": "^1.8",
"symfony/phpunit-bridge": "^4.0" "symfony/phpunit-bridge": "^4.3"
}, },
"suggest": { "suggest": {
"ext-mbstring": "For handling non-UTF8 characters in cache signature.", "ext-mbstring": "For handling non-UTF8 characters in cache signature.",
@ -5055,7 +5052,7 @@
} }
], ],
"description": "A tool to automatically fix PHP code style", "description": "A tool to automatically fix PHP code style",
"time": "2018-10-21T00:32:10+00:00" "time": "2019-06-01T10:32:12+00:00"
}, },
{ {
"name": "fzaninotto/faker", "name": "fzaninotto/faker",
@ -6102,8 +6099,8 @@
"authors": [ "authors": [
{ {
"name": "Sebastian Bergmann", "name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de", "role": "lead",
"role": "lead" "email": "sebastian@phpunit.de"
} }
], ],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
@ -6375,8 +6372,8 @@
"authors": [ "authors": [
{ {
"name": "Sebastian Bergmann", "name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de", "role": "lead",
"role": "lead" "email": "sebastian@phpunit.de"
} }
], ],
"description": "The PHP Unit Testing framework.", "description": "The PHP Unit Testing framework.",

View file

@ -9,7 +9,7 @@ return [
| change this value if you are not maintaining your own internal versions. | change this value if you are not maintaining your own internal versions.
*/ */
'version' => '0.7.14', 'version' => '0.7.15',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View file

@ -3,12 +3,12 @@
"meta": { "meta": {
"version": "PTDL_v1" "version": "PTDL_v1"
}, },
"exported_at": "2019-02-27T22:23:55-05:00", "exported_at": "2019-08-01T04:49:37-04:00",
"name": "Paper", "name": "Paper",
"author": "parker@pterodactyl.io", "author": "parker@pterodactyl.io",
"description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.",
"image": "quay.io\/pterodactyl\/core:java", "image": "quay.io\/pterodactyl\/core:java",
"startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}",
"config": { "config": {
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",
"startup": "{\r\n \"done\": \")! For help, type \",\r\n \"userInteraction\": [\r\n \"Go to eula.txt for more info.\"\r\n ]\r\n}", "startup": "{\r\n \"done\": \")! For help, type \",\r\n \"userInteraction\": [\r\n \"Go to eula.txt for more info.\"\r\n ]\r\n}",
@ -17,7 +17,7 @@
}, },
"scripts": { "scripts": {
"installation": { "installation": {
"script": "#!\/bin\/ash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache --update curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\necho -e \"Downloading MC server.properties\"\r\ncurl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft_java\/server.properties", "script": "#!\/bin\/ash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache --update curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft_java\/server.properties\r\nfi",
"container": "alpine:3.9", "container": "alpine:3.9",
"entrypoint": "ash" "entrypoint": "ash"
} }

View file

@ -8,7 +8,7 @@
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.", "description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.",
"image": "quay.io\/pterodactyl\/core:source", "image": "quay.io\/pterodactyl\/core:source",
"startup": ".\/srcds_run -game garrysmod -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} +gamemode {{GAMEMODE}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}} +host_workshop_collection {{WORKSHOP_ID}} +maxplayers {{MAX_PLAYERS}} -tickrate {{TICKRATE}}", "startup": ".\/srcds_run -game garrysmod -console -port {{SERVER_PORT}} +ip 0.0.0.0 +host_workshop_collection {{WORKSHOP_ID}} +map {{SRCDS_MAP}} +gamemode {{GAMEMODE}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}} +maxplayers {{MAX_PLAYERS}} -tickrate {{TICKRATE}}",
"config": { "config": {
"files": "{}", "files": "{}",
"startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}", "startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}",

View file

@ -27,10 +27,10 @@
"name": "Game ID", "name": "Game ID",
"description": "The ID corresponding to the game to download and run using SRCDS.", "description": "The ID corresponding to the game to download and run using SRCDS.",
"env_variable": "SRCDS_APPID", "env_variable": "SRCDS_APPID",
"default_value": "17705", "default_value": "237410",
"user_viewable": 1, "user_viewable": 1,
"user_editable": 0, "user_editable": 0,
"rules": "required|regex:\/^(17705)$\/" "rules": "required|regex:\/^(237410)$\/"
}, },
{ {
"name": "Game Name", "name": "Game Name",

View file

@ -3,21 +3,21 @@
"meta": { "meta": {
"version": "PTDL_v1" "version": "PTDL_v1"
}, },
"exported_at": "2019-05-01T16:35:15+00:00", "exported_at": "2019-07-05T11:59:29-04:00",
"name": "Teamspeak3 Server", "name": "Teamspeak3 Server",
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.", "description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.",
"image": "quay.io\/pterodactyl\/core:glibc", "image": "quay.io/parkervcp/pterodactyl-images:base_debian",
"startup": ".\/ts3server_minimal_runscript.sh default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}} license_accepted=1", "startup": "./ts3server default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} license_accepted=1",
"config": { "config": {
"files": "{}", "files": "{}",
"startup": "{\"done\": \"listening on 0.0.0.0:\", \"userInteraction\": []}", "startup": "{\r\n \"done\": \"listening on 0.0.0.0:\",\r\n \"userInteraction\": []\r\n}",
"logs": "{\"custom\": true, \"location\": \"logs\/ts3.log\"}", "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs/ts3.log\"\r\n}",
"stop": "^C" "stop": "^C"
}, },
"scripts": { "scripts": {
"installation": { "installation": {
"script": "#!\/bin\/ash\n# TS3 Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add tar curl\n\ncd \/mnt\/server\n\ncurl http:\/\/dl.4players.de\/ts\/releases\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1", "script": "#!/bin/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: /mnt/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https://teamspeak.com/versions/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd /mnt/server\r\n\r\n\r\necho -e \"getting files from http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1",
"container": "alpine:3.9", "container": "alpine:3.9",
"entrypoint": "ash" "entrypoint": "ash"
} }
@ -27,10 +27,19 @@
"name": "Server Version", "name": "Server Version",
"description": "The version of Teamspeak 3 to use when running the server.", "description": "The version of Teamspeak 3 to use when running the server.",
"env_variable": "TS_VERSION", "env_variable": "TS_VERSION",
"default_value": "3.7.1", "default_value": "latest",
"user_viewable": 1, "user_viewable": 1,
"user_editable": 1, "user_editable": 1,
"rules": "required|regex:\/^([0-9_\\.-]{5,10})$\/" "rules": "required|string|max:6"
},
{
"name": "File Transfer Port",
"description": "The Teamspeak file transfer port",
"env_variable": "FILE_TRANSFER",
"default_value": "30033",
"user_viewable": 1,
"user_editable": 0,
"rules": "required|integer|between:1,65535"
} }
] ]
} }

View file

@ -101,9 +101,11 @@
currentCpu = parseFloat(((info.proc.cpu.total / cpuMax) * 100).toFixed(2).toString()); currentCpu = parseFloat(((info.proc.cpu.total / cpuMax) * 100).toFixed(2).toString());
} }
element.find('[data-action="memory"]').html(parseInt(info.proc.memory.total / (1024 * 1024))); element.find('[data-action="memory"]').html(parseInt(info.proc.memory.total / (1024 * 1024)));
element.find('[data-action="disk"]').html(parseInt(info.proc.disk.used));
element.find('[data-action="cpu"]').html(currentCpu); element.find('[data-action="cpu"]').html(currentCpu);
} else { } else {
element.find('[data-action="memory"]').html('--'); element.find('[data-action="memory"]').html('--');
element.find('[data-action="disk"]').html('--');
element.find('[data-action="cpu"]').html('--'); element.find('[data-action="cpu"]').html('--');
} }
}); });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -397,8 +397,10 @@ class ActionsClass {
{ {
let formattedItems = ""; let formattedItems = "";
let i = 0; let i = 0;
let self = this;
$.each(selectedItems, function(key, value) { $.each(selectedItems, function(key, value) {
formattedItems += ("<code>" + this.sanitizedString(value) + "</code>, "); formattedItems += ("<code>" + self.sanitizedString(value) + "</code>, ");
i++; i++;
return i < 5; return i < 5;
}); });
@ -411,7 +413,7 @@ class ActionsClass {
swal({ swal({
type: 'warning', type: 'warning',
title: '', title: '',
text: 'Are you sure you want to delete the following files: ' + this.sanitizedString(formattedItems) + '?', text: 'Are you sure you want to delete the following files: ' + formattedItems + '?',
html: true, html: true,
showCancelButton: true, showCancelButton: true,
showConfirmButton: true, showConfirmButton: true,

View file

@ -74,9 +74,11 @@
} }
element.find('[data-action="memory"]').html(parseInt(data.proc.memory.total / (1024 * 1024))); element.find('[data-action="memory"]').html(parseInt(data.proc.memory.total / (1024 * 1024)));
element.find('[data-action="cpu"]').html(currentCpu); element.find('[data-action="cpu"]').html(currentCpu);
element.find('[data-action="disk"]').html(parseInt(data.proc.disk.used));
} else { } else {
element.find('[data-action="memory"]').html('--'); element.find('[data-action="memory"]').html('--');
element.find('[data-action="cpu"]').html('--'); element.find('[data-action="cpu"]').html('--');
element.find('[data-action="disk"]').html('--');
} }
} }
}).fail(function (jqXHR) { }).fail(function (jqXHR) {

View file

@ -14,6 +14,7 @@ return [
'connection' => 'Connection', 'connection' => 'Connection',
'memory' => 'Memory', 'memory' => 'Memory',
'cpu' => 'CPU', 'cpu' => 'CPU',
'disk' => 'Disk',
'status' => 'Status', 'status' => 'Status',
'search' => 'Search', 'search' => 'Search',
'suspended' => 'Suspended', 'suspended' => 'Suspended',

View file

@ -57,10 +57,10 @@
<td><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></td> <td><a href="{{ route('admin.servers.view', $server->id) }}">{{ $server->name }}</a></td>
<td><a href="{{ route('admin.users.view', $server->owner_id) }}">{{ $server->user->username }}</a></td> <td><a href="{{ route('admin.users.view', $server->owner_id) }}">{{ $server->user->username }}</a></td>
<td>{{ $server->nest->name }} ({{ $server->egg->name }})</td> <td>{{ $server->nest->name }} ({{ $server->egg->name }})</td>
<td class="text-center"><span data-action="memory">NaN</span> / {{ $server->memory === 0 ? '∞' : $server->memory }} MB</td> <td class="text-center"><span data-action="memory">--</span> / {{ $server->memory === 0 ? '∞' : $server->memory }} MB</td>
<td class="text-center">{{ $server->disk }} MB</td> <td class="text-center"><span data-action="disk">--</span> / {{ $server->disk === 0 ? '∞' : $server->disk }} MB </td>
<td class="text-center"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">NaN</span> %</td> <td class="text-center"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">--</span> %</td>
<td class="text-center" data-action="status">NaN</td> <td class="text-center" data-action="status">--</td>
</tr> </tr>
@endforeach @endforeach
</table> </table>

View file

@ -67,8 +67,8 @@
<div class="form-group col-xs-12"> <div class="form-group col-xs-12">
<label for="public" class="control-label">Allow Automatic Allocation <sup><a data-toggle="tooltip" data-placement="top" title="Allow automatic allocation to this Node?">?</a></sup></label> <label for="public" class="control-label">Allow Automatic Allocation <sup><a data-toggle="tooltip" data-placement="top" title="Allow automatic allocation to this Node?">?</a></sup></label>
<div> <div>
<input type="radio" name="public" value="1" {{ (old('public', $node->public) === '1') ? 'checked' : '' }} id="public_1" checked> <label for="public_1" style="padding-left:5px;">Yes</label><br /> <input type="radio" name="public" value="1" {{ (old('public', $node->public)) ? 'checked' : '' }} id="public_1" checked> <label for="public_1" style="padding-left:5px;">Yes</label><br />
<input type="radio" name="public" value="0" {{ (old('public', $node->public) === '0') ? 'checked' : '' }} id="public_0"> <label for="public_0" style="padding-left:5px;">No</label> <input type="radio" name="public" value="0" {{ (old('public', $node->public)) ? '' : 'checked' }} id="public_0"> <label for="public_0" style="padding-left:5px;">No</label>
</div> </div>
</div> </div>
<div class="form-group col-xs-12"> <div class="form-group col-xs-12">

View file

@ -85,6 +85,22 @@
</div> </div>
<p class="text-muted small">This server will not be allowed to boot if it is using more than this amount of space. If a server goes over this limit while running it will be safely stopped and locked until enough space is available.</p> <p class="text-muted small">This server will not be allowed to boot if it is using more than this amount of space. If a server goes over this limit while running it will be safely stopped and locked until enough space is available.</p>
</div> </div>
<div class="form-group">
<label for="cpu" class="control-label">OOM Killer</label>
<div>
<div class="radio radio-danger radio-inline">
<input type="radio" id="pOomKillerEnabled" value="0" name="oom_disabled" @if(!$server->oom_disabled)checked @endif>
<label for="pOomKillerEnabled">Enabled</label>
</div>
<div class="radio radio-success radio-inline">
<input type="radio" id="pOomKillerDisabled" value="1" name="oom_disabled" @if($server->oom_disabled)checked @endif>
<label for="pOomKillerDisabled">Disabled</label>
</div>
<p class="text-muted small">
Enabling OOM killer may cause server processes to exit unexpectedly.
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -111,9 +111,7 @@
@endsection @endsection
@section('footer-scripts') @section('footer-scripts')
{!! Theme::js('js/laroute.js?t={cache-version}') !!} @parent
{!! Theme::js('vendor/jquery/jquery.min.js?t={cache-version}') !!}
{!! Theme::js('vendor/sweetalert/sweetalert.min.js?t={cache-version}') !!}
<script> <script>
function saveSettings() { function saveSettings() {

View file

@ -44,6 +44,7 @@
<th>@lang('strings.connection')</th> <th>@lang('strings.connection')</th>
<th class="text-center hidden-sm hidden-xs">@lang('strings.memory')</th> <th class="text-center hidden-sm hidden-xs">@lang('strings.memory')</th>
<th class="text-center hidden-sm hidden-xs">@lang('strings.cpu')</th> <th class="text-center hidden-sm hidden-xs">@lang('strings.cpu')</th>
<th class="text-center hidden-sm hidden-xs">@lang('strings.disk')</th>
<th class="text-center">@lang('strings.relation')</th> <th class="text-center">@lang('strings.relation')</th>
<th class="text-center">@lang('strings.status')</th> <th class="text-center">@lang('strings.status')</th>
</tr> </tr>
@ -55,6 +56,7 @@
<td><code>{{ $server->getRelation('allocation')->alias }}:{{ $server->getRelation('allocation')->port }}</code></td> <td><code>{{ $server->getRelation('allocation')->alias }}:{{ $server->getRelation('allocation')->port }}</code></td>
<td class="text-center hidden-sm hidden-xs"><span data-action="memory">--</span> / {{ $server->memory === 0 ? '∞' : $server->memory }} MB</td> <td class="text-center hidden-sm hidden-xs"><span data-action="memory">--</span> / {{ $server->memory === 0 ? '∞' : $server->memory }} MB</td>
<td class="text-center hidden-sm hidden-xs"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">--</span> %</td> <td class="text-center hidden-sm hidden-xs"><span data-action="cpu" data-cpumax="{{ $server->cpu }}">--</span> %</td>
<td class="text-center hidden-sm hidden-xs"><span data-action="disk">--</span> / {{ $server->disk === 0 ? '∞' : $server->disk }} MB </td>
<td class="text-center"> <td class="text-center">
@if($server->user->id === Auth::user()->id) @if($server->user->id === Auth::user()->id)
<span class="label bg-purple">@lang('strings.owner')</span> <span class="label bg-purple">@lang('strings.owner')</span>

View file

@ -3,6 +3,8 @@
namespace Tests\Unit\Commands\Server; namespace Tests\Unit\Commands\Server;
use Mockery as m; use Mockery as m;
use Pterodactyl\Models\Node;
use GuzzleHttp\Psr7\Response;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Illuminate\Validation\Factory; use Illuminate\Validation\Factory;
use Tests\Unit\Commands\CommandTestCase; use Tests\Unit\Commands\CommandTestCase;
@ -38,8 +40,13 @@ class BulkPowerActionCommandTest extends CommandTestCase
*/ */
public function testSendAction() public function testSendAction()
{ {
/** @var \Pterodactyl\Models\Server[] $servers */
$servers = factory(Server::class)->times(2)->make(); $servers = factory(Server::class)->times(2)->make();
foreach ($servers as &$server) {
$server->setRelation('node', factory(Node::class)->make());
}
$this->repository->shouldReceive('getServersForPowerActionCount') $this->repository->shouldReceive('getServersForPowerActionCount')
->once() ->once()
->with([], []) ->with([], [])
@ -51,7 +58,7 @@ class BulkPowerActionCommandTest extends CommandTestCase
->andReturn($servers); ->andReturn($servers);
for ($i = 0; $i < count($servers); $i++) { for ($i = 0; $i < count($servers); $i++) {
$this->powerRepository->shouldReceive('setServer->sendSignal') $this->powerRepository->shouldReceive('setNode->setServer->sendSignal')
->once() ->once()
->with('kill') ->with('kill')
->andReturnNull(); ->andReturnNull();
@ -70,6 +77,7 @@ class BulkPowerActionCommandTest extends CommandTestCase
public function testSendWithFilters() public function testSendWithFilters()
{ {
$server = factory(Server::class)->make(); $server = factory(Server::class)->make();
$server->setRelation('node', $node = factory(Node::class)->make());
$this->repository->shouldReceive('getServersForPowerActionCount') $this->repository->shouldReceive('getServersForPowerActionCount')
->once() ->once()
@ -81,10 +89,9 @@ class BulkPowerActionCommandTest extends CommandTestCase
->with([1, 2], [3, 4]) ->with([1, 2], [3, 4])
->andReturn([$server]); ->andReturn([$server]);
$this->powerRepository->shouldReceive('setServer->sendSignal') $this->powerRepository->expects('setNode')->with($node)->andReturnSelf();
->once() $this->powerRepository->expects('setServer')->with($server)->andReturnSelf();
->with('kill') $this->powerRepository->expects('sendSignal')->with('kill')->andReturn(new Response);
->andReturnNull();
$display = $this->runCommand($this->getCommand(), [ $display = $this->runCommand($this->getCommand(), [
'action' => 'kill', 'action' => 'kill',
@ -103,6 +110,7 @@ class BulkPowerActionCommandTest extends CommandTestCase
public function testSendWithEmptyOptions() public function testSendWithEmptyOptions()
{ {
$server = factory(Server::class)->make(); $server = factory(Server::class)->make();
$server->setRelation('node', factory(Node::class)->make());
$this->repository->shouldReceive('getServersForPowerActionCount') $this->repository->shouldReceive('getServersForPowerActionCount')
->once() ->once()
@ -110,7 +118,7 @@ class BulkPowerActionCommandTest extends CommandTestCase
->andReturn(1); ->andReturn(1);
$this->repository->shouldReceive('getServersForPowerAction')->once()->with([], [])->andReturn([$server]); $this->repository->shouldReceive('getServersForPowerAction')->once()->with([], [])->andReturn([$server]);
$this->powerRepository->shouldReceive('setServer->sendSignal')->once()->with('kill')->andReturnNull(); $this->powerRepository->shouldReceive('setNode->setServer->sendSignal')->once()->with('kill')->andReturnNull();
$display = $this->runCommand($this->getCommand(), [ $display = $this->runCommand($this->getCommand(), [
'action' => 'kill', 'action' => 'kill',

View file

@ -48,43 +48,35 @@ class DatabasePasswordServiceTest extends TestCase
/** /**
* Test that a password can be updated. * Test that a password can be updated.
*
* @dataProvider useModelDataProvider
*/ */
public function testPasswordIsChanged(bool $useModel) public function testPasswordIsChanged()
{ {
$model = factory(Database::class)->make(); $model = factory(Database::class)->make();
if (! $useModel) { $this->connection->expects('transaction')->with(m::on(function ($closure) {
$this->repository->shouldReceive('find')->with(1234)->once()->andReturn($model); return is_null($closure());
} }));
$this->dynamic->shouldReceive('set')->with('dynamic', $model->database_host_id)->once()->andReturnNull(); $this->dynamic->shouldReceive('set')->with('dynamic', $model->database_host_id)->once()->andReturnNull();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldReceive('encrypt')->with('test123')->once()->andReturn('enc123'); $this->encrypter->expects('encrypt')->with(m::on(function ($string) {
preg_match_all('/[!@+=^-]/', $string, $matches, PREG_SET_ORDER);
$this->assertTrue(count($matches) >= 2 && count($matches) <= 6, "Failed asserting that [{$string}] contains 2 to 6 special characters.");
$this->assertTrue(strlen($string) === 24, "Failed asserting that [{$string}] is 24 characters in length.");
return true;
}))->andReturn('enc123');
$this->repository->shouldReceive('withoutFreshModel')->withNoArgs()->once()->andReturnSelf(); $this->repository->shouldReceive('withoutFreshModel')->withNoArgs()->once()->andReturnSelf();
$this->repository->shouldReceive('update')->with($model->id, ['password' => 'enc123'])->once()->andReturn(true); $this->repository->shouldReceive('update')->with($model->id, ['password' => 'enc123'])->once()->andReturn(true);
$this->repository->shouldReceive('dropUser')->with($model->username, $model->remote)->once()->andReturn(true); $this->repository->shouldReceive('dropUser')->with($model->username, $model->remote)->once()->andReturn(true);
$this->repository->shouldReceive('createUser')->with($model->username, $model->remote, 'test123')->once()->andReturn(true); $this->repository->shouldReceive('createUser')->with($model->username, $model->remote, m::any())->once()->andReturn(true);
$this->repository->shouldReceive('assignUserToDatabase')->with($model->database, $model->username, $model->remote)->once()->andReturn(true); $this->repository->shouldReceive('assignUserToDatabase')->with($model->database, $model->username, $model->remote)->once()->andReturn(true);
$this->repository->shouldReceive('flush')->withNoArgs()->once()->andReturn(true); $this->repository->shouldReceive('flush')->withNoArgs()->once()->andReturn(true);
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturn(true);
$response = $this->getService()->handle($useModel ? $model : 1234, 'test123'); $response = $this->getService()->handle($model);
$this->assertNotEmpty($response); $this->assertNotEmpty($response);
$this->assertTrue($response);
}
/**
* Data provider to determine if a model should be passed or an int.
*
* @return array
*/
public function useModelDataProvider(): array
{
return [[false], [true]];
} }
/** /**

View file

@ -60,18 +60,20 @@ class HostCreationServiceTest extends TestCase
{ {
$model = factory(DatabaseHost::class)->make(); $model = factory(DatabaseHost::class)->make();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); $this->connection->expects('transaction')->with(m::on(function ($closure) {
$this->encrypter->shouldReceive('encrypt')->with('test123')->once()->andReturn('enc123'); return ! is_null($closure());
$this->repository->shouldReceive('create')->with(m::subset([ }))->andReturn($model);
$this->encrypter->expects('encrypt')->with('test123')->andReturn('enc123');
$this->repository->expects('create')->with(m::subset([
'password' => 'enc123', 'password' => 'enc123',
'username' => $model->username, 'username' => $model->username,
'node_id' => $model->node_id, 'node_id' => $model->node_id,
]))->once()->andReturn($model); ]))->andReturn($model);
$this->dynamic->shouldReceive('set')->with('dynamic', $model)->once()->andReturnNull(); $this->dynamic->expects('set')->with('dynamic', $model)->andReturnNull();
$this->databaseManager->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf(); $this->databaseManager->expects('connection')->with('dynamic')->andReturnSelf();
$this->databaseManager->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull(); $this->databaseManager->expects('select')->with('SELECT 1 FROM dual')->andReturnNull();
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->handle([ $response = $this->getService()->handle([
'password' => 'test123', 'password' => 'test123',

View file

@ -60,14 +60,15 @@ class HostUpdateServiceTest extends TestCase
{ {
$model = factory(DatabaseHost::class)->make(); $model = factory(DatabaseHost::class)->make();
$this->encrypter->shouldReceive('encrypt')->with('test123')->once()->andReturn('enc123'); $this->connection->expects('transaction')->with(m::on(function ($closure) {
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); return ! is_null($closure());
$this->repository->shouldReceive('update')->with(1234, ['password' => 'enc123'])->once()->andReturn($model); }))->andReturn($model);
$this->dynamic->shouldReceive('set')->with('dynamic', $model)->once()->andReturnNull(); $this->encrypter->expects('encrypt')->with('test123')->andReturn('enc123');
$this->databaseManager->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf(); $this->repository->expects('update')->with(1234, ['password' => 'enc123'])->andReturn($model);
$this->databaseManager->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull(); $this->dynamic->expects('set')->with('dynamic', $model)->andReturnNull();
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); $this->databaseManager->expects('connection')->with('dynamic')->andReturnSelf();
$this->databaseManager->expects('select')->with('SELECT 1 FROM dual')->andReturnNull();
$response = $this->getService()->handle(1234, ['password' => 'test123']); $response = $this->getService()->handle(1234, ['password' => 'test123']);
$this->assertNotEmpty($response); $this->assertNotEmpty($response);
@ -81,13 +82,14 @@ class HostUpdateServiceTest extends TestCase
{ {
$model = factory(DatabaseHost::class)->make(); $model = factory(DatabaseHost::class)->make();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); $this->connection->expects('transaction')->with(m::on(function ($closure) {
$this->repository->shouldReceive('update')->with(1234, ['username' => 'test'])->once()->andReturn($model); return ! is_null($closure());
}))->andReturn($model);
$this->dynamic->shouldReceive('set')->with('dynamic', $model)->once()->andReturnNull(); $this->repository->expects('update')->with(1234, ['username' => 'test'])->andReturn($model);
$this->databaseManager->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf(); $this->dynamic->expects('set')->with('dynamic', $model)->andReturnNull();
$this->databaseManager->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull(); $this->databaseManager->expects('connection')->with('dynamic')->andReturnSelf();
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); $this->databaseManager->expects('select')->with('SELECT 1 FROM dual')->andReturnNull();
$response = $this->getService()->handle(1234, ['password' => '', 'username' => 'test']); $response = $this->getService()->handle(1234, ['password' => '', 'username' => 'test']);
$this->assertNotEmpty($response); $this->assertNotEmpty($response);