chore: add phpstan static analysis minimum (#4511)

This commit is contained in:
Lance Pioch 2022-11-28 11:56:03 -05:00 committed by GitHub
parent 3f7e2a565f
commit a1a52754ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 561 additions and 279 deletions

View file

@ -1,4 +1,4 @@
name: Publish Docker Image
name: Docker
on:
push:
@ -8,16 +8,16 @@ on:
jobs:
push:
name: Push Image to GitHub Packages
name: Push
runs-on: ubuntu-20.04
# Always run against a tag, even if the commit into the tag has [docker skip]
# within the commit message.
if: "!contains(github.ref, 'develop') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))"
steps:
- name: Code Checkout
- name: Code checkout
uses: actions/checkout@v3
- name: Docker Metadata
- name: Docker metadata
uses: docker/metadata-action@v4
id: docker_meta
with:
@ -26,10 +26,10 @@ jobs:
- name: Setup QEMU
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v2
- name: Docker Login
- name: Docker login
uses: docker/login-action@v2
with:
registry: ghcr.io

View file

@ -1,4 +1,4 @@
name: Tests
name: Laravel
on:
push:
@ -11,8 +11,58 @@ on:
- "1.0-develop"
jobs:
analysis:
name: Static Analysis
runs-on: ubuntu-20.04
env:
APP_ENV: testing
APP_DEBUG: "true"
APP_KEY: SomeRandomString3232RandomString
CACHE_DRIVER: array
MAIL_MAILER: array
SESSION_DRIVER: array
QUEUE_CONNECTION: sync
steps:
- name: Code checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: Install dependencies
run: composer install --no-interaction --no-progress --no-suggest --prefer-dist
- name: Analyze
run: vendor/bin/phpstan analyse
lint:
name: Lint
runs-on: ubuntu-20.04
steps:
- name: Code checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: Install dependencies
run: composer install --no-interaction --no-progress --no-suggest --prefer-dist
- name: PHP CS Fixer
run: vendor/bin/php-cs-fixer fix --dry-run --diff
mysql:
name: MySQL
name: Tests (MySQL)
runs-on: ubuntu-20.04
strategy:
fail-fast: false
@ -36,20 +86,17 @@ jobs:
APP_TIMEZONE: UTC
APP_URL: http://localhost/
APP_ENVIRONMENT_ONLY: "true"
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
CACHE_DRIVER: array
MAIL_MAILER: array
SESSION_DRIVER: array
QUEUE_CONNECTION: sync
HASHIDS_SALT: test123
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
steps:
- name: Code Checkout
- name: Code checkout
uses: actions/checkout@v3
- name: Get cache directory
@ -78,7 +125,6 @@ jobs:
- name: Unit tests
run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit
if: ${{ always() }}
env:
DB_HOST: UNIT_NO_DB
@ -88,7 +134,7 @@ jobs:
DB_PORT: ${{ job.services.database.ports[3306] }}
postgres:
name: PostgreSQL
name: Tests (PostgreSQL)
runs-on: ubuntu-20.04
if: "!contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'ci skip')"
strategy:
@ -114,35 +160,32 @@ jobs:
APP_TIMEZONE: UTC
APP_URL: http://localhost/
APP_ENVIRONMENT_ONLY: "true"
CACHE_DRIVER: array
MAIL_MAILER: array
SESSION_DRIVER: array
QUEUE_CONNECTION: sync
HASHIDS_SALT: test123
DB_CONNECTION: pgsql
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: postgres
DB_PASSWORD: postgres
CACHE_DRIVER: array
MAIL_MAILER: array
SESSION_DRIVER: array
QUEUE_CONNECTION: sync
HASHIDS_SALT: test123
steps:
- name: Code Checkout
- name: Code checkout
uses: actions/checkout@v3
- name: Get cache directory
id: composer-cache
run: |
echo "::set-output name=dir::$(composer config cache-files-dir)"
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache
uses: actions/cache@v3
with:
path: |
~/.php_cs.cache
${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-cache-${{ matrix.php }}-${{ hashFiles('**.composer.lock') }}
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-${{ matrix.php }}-
- name: Setup PHP
uses: shivammathur/setup-php@v2
@ -157,7 +200,6 @@ jobs:
- name: Unit tests
run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit
if: ${{ always() }}
env:
DB_HOST: UNIT_NO_DB

View file

@ -1,33 +0,0 @@
name: Lint
on:
push:
branches:
- "develop"
- "1.0-develop"
pull_request:
branches:
- "develop"
- "1.0-develop"
jobs:
lint:
name: Lint
runs-on: ubuntu-20.04
steps:
- name: Code Checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: Install dependencies
run: composer install --no-interaction --no-progress --no-suggest --prefer-dist
- name: PHP CS Fixer
run: vendor/bin/php-cs-fixer fix --dry-run --diff

View file

@ -10,20 +10,20 @@ jobs:
name: Release
runs-on: ubuntu-20.04
steps:
- name: Code Checkout
- name: Code checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 16
cache: "yarn"
node-version: 18
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build
run: yarn build:production
run: yarn build
- name: Create release branch and bump version
env:
@ -41,7 +41,7 @@ jobs:
- name: Create release archive
run: |
rm -rf node_modules/ test/ codecov.yml CODE_OF_CONDUCT.md CONTRIBUTING.md phpunit.xml Vagrantfile
rm -rf node_modules/ tests/ CODE_OF_CONDUCT.md CONTRIBUTING.md phpstan.neon phpunit.xml
tar -czf panel.tar.gz * .env.example .eslintignore .eslintrc.js
- name: Extract changelog
@ -58,7 +58,7 @@ jobs:
echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG
echo $SUM > checksum.txt
- name: Create Release
- name: Create release
id: create_release
uses: actions/create-release@v1
env:

View file

@ -11,28 +11,47 @@ on:
- "1.0-develop"
jobs:
build-and-test:
name: Build and Test
lint:
name: Lint
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [16, 18]
steps:
- name: Code Checkout
- name: Code checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "yarn"
node-version: 18
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Lint
run: yarn run lint
tests:
name: Tests
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node: [16, 18]
steps:
- name: Code checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build
run: yarn build
run: yarn run build
- name: Tests
run: yarn test
run: yarn run test

View file

@ -25,7 +25,7 @@ use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class Handler extends ExceptionHandler
final class Handler extends ExceptionHandler
{
/**
* The validation parser in Laravel formats custom rules using the class name

View file

@ -9,7 +9,6 @@ use Pterodactyl\Models\ApiKey;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Illuminate\View\Factory as ViewFactory;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Api\KeyCreationService;
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
@ -23,8 +22,7 @@ class ApiController extends Controller
public function __construct(
private AlertsMessageBag $alert,
private ApiKeyRepositoryInterface $repository,
private KeyCreationService $keyCreationService,
private ViewFactory $view,
private KeyCreationService $keyCreationService
) {
}
@ -33,7 +31,7 @@ class ApiController extends Controller
*/
public function index(Request $request): View
{
return $this->view->make('admin.api.index', [
return view('admin.api.index', [
'keys' => $this->repository->getApplicationKeys($request->user()),
]);
}
@ -48,7 +46,7 @@ class ApiController extends Controller
$resources = AdminAcl::getResourceList();
sort($resources);
return $this->view->make('admin.api.new', [
return view('admin.api.new', [
'resources' => $resources,
'permissions' => [
'r' => AdminAcl::READ,

View file

@ -3,7 +3,6 @@
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\View\View;
use Illuminate\View\Factory as ViewFactory;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
@ -12,7 +11,7 @@ class BaseController extends Controller
/**
* BaseController constructor.
*/
public function __construct(private SoftwareVersionService $version, private ViewFactory $view)
public function __construct(private SoftwareVersionService $version)
{
}
@ -21,6 +20,6 @@ class BaseController extends Controller
*/
public function index(): View
{
return $this->view->make('admin.index', ['version' => $this->version]);
return view('admin.index', ['version' => $this->version]);
}
}

View file

@ -8,7 +8,6 @@ use Illuminate\View\View;
use Pterodactyl\Models\DatabaseHost;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\View\Factory as ViewFactory;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Databases\Hosts\HostUpdateService;
use Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest;
@ -30,8 +29,7 @@ class DatabaseController extends Controller
private HostCreationService $creationService,
private HostDeletionService $deletionService,
private HostUpdateService $updateService,
private LocationRepositoryInterface $locationRepository,
private ViewFactory $view
private LocationRepositoryInterface $locationRepository
) {
}
@ -40,7 +38,7 @@ class DatabaseController extends Controller
*/
public function index(): View
{
return $this->view->make('admin.databases.index', [
return view('admin.databases.index', [
'locations' => $this->locationRepository->getAllWithNodes(),
'hosts' => $this->repository->getWithViewDetails(),
]);
@ -53,7 +51,7 @@ class DatabaseController extends Controller
*/
public function view(int $host): View
{
return $this->view->make('admin.databases.view', [
return view('admin.databases.view', [
'locations' => $this->locationRepository->getAllWithNodes(),
'host' => $this->repository->find($host),
'databases' => $this->databaseRepository->getDatabasesForHost($host),

View file

@ -35,7 +35,7 @@ class LocationController extends Controller
*/
public function index(): View
{
return $this->view->make('admin.locations.index', [
return view('admin.locations.index', [
'locations' => $this->repository->getAllWithDetails(),
]);
}
@ -47,7 +47,7 @@ class LocationController extends Controller
*/
public function view(int $id): View
{
return $this->view->make('admin.locations.view', [
return view('admin.locations.view', [
'location' => $this->repository->getWithNodes($id),
]);
}

View file

@ -37,7 +37,7 @@ class MountController extends Controller
*/
public function index(): View
{
return $this->view->make('admin.mounts.index', [
return view('admin.mounts.index', [
'mounts' => $this->repository->getAllWithDetails(),
]);
}
@ -52,7 +52,7 @@ class MountController extends Controller
$nests = Nest::query()->with('eggs')->get();
$locations = Location::query()->with('nodes')->get();
return $this->view->make('admin.mounts.view', [
return view('admin.mounts.view', [
'mount' => $this->repository->getWithRelations($id),
'nests' => $nests,
'locations' => $locations,

View file

@ -42,7 +42,7 @@ class EggController extends Controller
$nests = $this->nestRepository->getWithEggs();
JavaScript::put(['nests' => $nests->keyBy('id')]);
return $this->view->make('admin.eggs.new', ['nests' => $nests]);
return view('admin.eggs.new', ['nests' => $nests]);
}
/**
@ -67,7 +67,7 @@ class EggController extends Controller
*/
public function view(Egg $egg): View
{
return $this->view->make('admin.eggs.view', [
return view('admin.eggs.view', [
'egg' => $egg,
'images' => array_map(
fn ($key, $value) => $key === $value ? $value : "$key|$value",

View file

@ -41,7 +41,7 @@ class EggScriptController extends Controller
['copy_script_from', '=', $egg->id],
]);
return $this->view->make('admin.eggs.scripts', [
return view('admin.eggs.scripts', [
'copyFromOptions' => $copy,
'relyOnScript' => $rely,
'egg' => $egg,

View file

@ -39,7 +39,7 @@ class EggVariableController extends Controller
{
$egg = $this->repository->getWithVariables($egg);
return $this->view->make('admin.eggs.variables', ['egg' => $egg]);
return view('admin.eggs.variables', ['egg' => $egg]);
}
/**

View file

@ -35,7 +35,7 @@ class NestController extends Controller
*/
public function index(): View
{
return $this->view->make('admin.nests.index', [
return view('admin.nests.index', [
'nests' => $this->repository->getWithCounts(),
]);
}
@ -45,7 +45,7 @@ class NestController extends Controller
*/
public function create(): View
{
return $this->view->make('admin.nests.new');
return view('admin.nests.new');
}
/**
@ -68,7 +68,7 @@ class NestController extends Controller
*/
public function view(int $nest): View
{
return $this->view->make('admin.nests.view', [
return view('admin.nests.view', [
'nest' => $this->repository->getWithEggServers($nest),
]);
}

View file

@ -7,17 +7,9 @@ use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\View\Factory as ViewFactory;
class NodeController extends Controller
{
/**
* NodeController constructor.
*/
public function __construct(private ViewFactory $view)
{
}
/**
* Returns a listing of nodes on the system.
*/
@ -30,6 +22,6 @@ class NodeController extends Controller
->allowedSorts(['id'])
->paginate(25);
return $this->view->make('admin.nodes.index', ['nodes' => $nodes]);
return view('admin.nodes.index', ['nodes' => $nodes]);
}
}

View file

@ -8,13 +8,11 @@ use Pterodactyl\Models\Node;
use Illuminate\Support\Collection;
use Pterodactyl\Models\Allocation;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
class NodeViewController extends Controller
{
@ -24,12 +22,10 @@ class NodeViewController extends Controller
* NodeViewController constructor.
*/
public function __construct(
private AllocationRepository $allocationRepository,
private LocationRepository $locationRepository,
private NodeRepository $repository,
private ServerRepository $serverRepository,
private SoftwareVersionService $versionService,
private ViewFactory $view
private SoftwareVersionService $versionService
) {
}
@ -40,7 +36,7 @@ class NodeViewController extends Controller
{
$node = $this->repository->loadLocationAndServerCount($node);
return $this->view->make('admin.nodes.view.index', [
return view('admin.nodes.view.index', [
'node' => $node,
'stats' => $this->repository->getUsageStats($node),
'version' => $this->versionService,
@ -52,7 +48,7 @@ class NodeViewController extends Controller
*/
public function settings(Request $request, Node $node): View
{
return $this->view->make('admin.nodes.view.settings', [
return view('admin.nodes.view.settings', [
'node' => $node,
'locations' => $this->locationRepository->all(),
]);
@ -63,7 +59,7 @@ class NodeViewController extends Controller
*/
public function configuration(Request $request, Node $node): View
{
return $this->view->make('admin.nodes.view.configuration', compact('node'));
return view('admin.nodes.view.configuration', compact('node'));
}
/**
@ -75,7 +71,7 @@ class NodeViewController extends Controller
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
return $this->view->make('admin.nodes.view.allocation', [
return view('admin.nodes.view.allocation', [
'node' => $node,
'allocations' => Allocation::query()->where('node_id', $node->id)
->groupBy('ip')
@ -94,7 +90,7 @@ class NodeViewController extends Controller
->only(['scheme', 'fqdn', 'daemonListen', 'daemon_token_id', 'daemon_token']),
]);
return $this->view->make('admin.nodes.view.servers', [
return view('admin.nodes.view.servers', [
'node' => $node,
'servers' => $this->serverRepository->loadAllServersForNode($node->id, 25),
]);

View file

@ -60,7 +60,7 @@ class NodesController extends Controller
return redirect()->route('admin.locations');
}
return $this->view->make('admin.nodes.new', ['locations' => $locations]);
return view('admin.nodes.new', ['locations' => $locations]);
}
/**

View file

@ -4,11 +4,11 @@ namespace Pterodactyl\Http\Controllers\Admin\Servers;
use JavaScript;
use Illuminate\View\View;
use Pterodactyl\Models\Nest;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\Location;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\View\Factory as ViewFactory;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Eloquent\NestRepository;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
@ -24,8 +24,7 @@ class CreateServerController extends Controller
private AlertsMessageBag $alert,
private NestRepository $nestRepository,
private NodeRepository $nodeRepository,
private ServerCreationService $creationService,
private ViewFactory $view
private ServerCreationService $creationService
) {
}
@ -47,14 +46,14 @@ class CreateServerController extends Controller
JavaScript::put([
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
'nests' => $nests->map(function ($item) {
'nests' => $nests->map(function (Nest $item) {
return array_merge($item->toArray(), [
'eggs' => $item->eggs->keyBy('id')->toArray(),
]);
})->keyBy('id'),
]);
return $this->view->make('admin.servers.new', [
return view('admin.servers.new', [
'locations' => Location::all(),
'nests' => $nests,
]);

View file

@ -9,17 +9,9 @@ use Spatie\QueryBuilder\QueryBuilder;
use Spatie\QueryBuilder\AllowedFilter;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Models\Filters\AdminServerFilter;
use Illuminate\Contracts\View\Factory as ViewFactory;
class ServerController extends Controller
{
/**
* ServerController constructor.
*/
public function __construct(private ViewFactory $view)
{
}
/**
* Returns all the servers that exist on the system using a paginated result set. If
* a query is passed along in the request it is also passed to the repository function.
@ -33,6 +25,6 @@ class ServerController extends Controller
])
->paginate(config()->get('pterodactyl.paginate.admin.servers'));
return $this->view->make('admin.servers.index', ['servers' => $servers]);
return view('admin.servers.index', ['servers' => $servers]);
}
}

View file

@ -4,6 +4,7 @@ namespace Pterodactyl\Http\Controllers\Admin\Servers;
use Carbon\CarbonImmutable;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\Server;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
@ -11,7 +12,6 @@ use Pterodactyl\Models\ServerTransfer;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Nodes\NodeJWTService;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Wings\DaemonTransferRepository;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
@ -25,8 +25,7 @@ class ServerTransferController extends Controller
private AllocationRepositoryInterface $allocationRepository,
private ConnectionInterface $connection,
private DaemonTransferRepository $daemonTransferRepository,
private NodeJWTService $nodeJWTService,
private NodeRepository $nodeRepository
private NodeJWTService $nodeJWTService
) {
}
@ -48,7 +47,7 @@ class ServerTransferController extends Controller
$additional_allocations = array_map('intval', $validatedData['allocation_additional'] ?? []);
// Check if the node is viable for the transfer.
$node = $this->nodeRepository->getNodeWithResourceUsage($node_id);
$node = Node::query()->findOrFail($node_id);
if (!$node->isViable($server->memory, $server->disk)) {
$this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash();
@ -58,7 +57,6 @@ class ServerTransferController extends Controller
$server->validateTransferState();
$this->connection->transaction(function () use ($server, $node_id, $allocation_id, $additional_allocations) {
// Create a new ServerTransfer entry.
$transfer = new ServerTransfer();
$transfer->server_id = $server->id;
@ -66,7 +64,7 @@ class ServerTransferController extends Controller
$transfer->new_node = $node_id;
$transfer->old_allocation = $server->allocation_id;
$transfer->new_allocation = $allocation_id;
$transfer->old_additional_allocations = $server->allocations->where('id', '!=', $server->allocation_id)->pluck('id');
$transfer->old_additional_allocations = $server->allocations->where('id', '!=', $server->allocation_id)->pluck('id')->all();
$transfer->new_additional_allocations = $additional_allocations;
$transfer->save();

View file

@ -10,11 +10,9 @@ use Pterodactyl\Models\Server;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Servers\EnvironmentService;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Pterodactyl\Repositories\Eloquent\NestRepository;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Eloquent\MountRepository;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
@ -32,9 +30,7 @@ class ServerViewController extends Controller
private MountRepository $mountRepository,
private NestRepository $nestRepository,
private NodeRepository $nodeRepository,
private ServerRepository $repository,
private EnvironmentService $environmentService,
private ViewFactory $view
private EnvironmentService $environmentService
) {
}
@ -43,7 +39,7 @@ class ServerViewController extends Controller
*/
public function index(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.index', compact('server'));
return view('admin.servers.view.index', compact('server'));
}
/**
@ -51,7 +47,7 @@ class ServerViewController extends Controller
*/
public function details(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.details', compact('server'));
return view('admin.servers.view.details', compact('server'));
}
/**
@ -61,7 +57,7 @@ class ServerViewController extends Controller
{
$allocations = $server->node->allocations->toBase();
return $this->view->make('admin.servers.view.build', [
return view('admin.servers.view.build', [
'server' => $server,
'assigned' => $allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
'unassigned' => $allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
@ -88,7 +84,7 @@ class ServerViewController extends Controller
})->keyBy('id'),
]);
return $this->view->make('admin.servers.view.startup', compact('server', 'nests'));
return view('admin.servers.view.startup', compact('server', 'nests'));
}
/**
@ -96,7 +92,7 @@ class ServerViewController extends Controller
*/
public function database(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.database', [
return view('admin.servers.view.database', [
'hosts' => $this->databaseHostRepository->all(),
'server' => $server,
]);
@ -109,7 +105,7 @@ class ServerViewController extends Controller
{
$server->load('mounts');
return $this->view->make('admin.servers.view.mounts', [
return view('admin.servers.view.mounts', [
'mounts' => $this->mountRepository->getMountListForServer($server),
'server' => $server,
]);
@ -138,7 +134,7 @@ class ServerViewController extends Controller
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
]);
return $this->view->make('admin.servers.view.manage', [
return view('admin.servers.view.manage', [
'server' => $server,
'locations' => $this->locationRepository->all(),
'canTransfer' => $canTransfer,
@ -150,6 +146,6 @@ class ServerViewController extends Controller
*/
public function delete(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.delete', compact('server'));
return view('admin.servers.view.delete', compact('server'));
}
}

View file

@ -6,7 +6,6 @@ use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\View\Factory as ViewFactory;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface;
@ -21,8 +20,7 @@ class AdvancedController extends Controller
private AlertsMessageBag $alert,
private ConfigRepository $config,
private Kernel $kernel,
private SettingsRepositoryInterface $settings,
private ViewFactory $view
private SettingsRepositoryInterface $settings
) {
}
@ -39,7 +37,7 @@ class AdvancedController extends Controller
$showRecaptchaWarning = true;
}
return $this->view->make('admin.settings.advanced', [
return view('admin.settings.advanced', [
'showRecaptchaWarning' => $showRecaptchaWarning,
]);
}

View file

@ -6,7 +6,6 @@ use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\View\Factory as ViewFactory;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
@ -24,8 +23,7 @@ class IndexController extends Controller
private AlertsMessageBag $alert,
private Kernel $kernel,
private SettingsRepositoryInterface $settings,
private SoftwareVersionService $versionService,
private ViewFactory $view
private SoftwareVersionService $versionService
) {
}
@ -34,7 +32,7 @@ class IndexController extends Controller
*/
public function index(): View
{
return $this->view->make('admin.settings.index', [
return view('admin.settings.index', [
'version' => $this->versionService,
'languages' => $this->getAvailableLanguages(true),
]);

View file

@ -8,7 +8,6 @@ use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Contracts\Console\Kernel;
use Pterodactyl\Notifications\MailTested;
use Illuminate\View\Factory as ViewFactory;
use Illuminate\Support\Facades\Notification;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
@ -27,8 +26,7 @@ class MailController extends Controller
private ConfigRepository $config,
private Encrypter $encrypter,
private Kernel $kernel,
private SettingsRepositoryInterface $settings,
private ViewFactory $view
private SettingsRepositoryInterface $settings
) {
}
@ -38,7 +36,7 @@ class MailController extends Controller
*/
public function index(): View
{
return $this->view->make('admin.settings.mail', [
return view('admin.settings.mail', [
'disabled' => $this->config->get('mail.default') !== 'smtp',
]);
}

View file

@ -6,13 +6,13 @@ use Illuminate\View\View;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Model;
use Illuminate\Support\Collection;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Spatie\QueryBuilder\QueryBuilder;
use Illuminate\View\Factory as ViewFactory;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Contracts\Translation\Translator;
use Pterodactyl\Services\Users\UserUpdateService;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
@ -57,7 +57,7 @@ class UserController extends Controller
->allowedSorts(['id', 'uuid'])
->paginate(50);
return $this->view->make('admin.users.index', ['users' => $users]);
return view('admin.users.index', ['users' => $users]);
}
/**
@ -65,7 +65,7 @@ class UserController extends Controller
*/
public function create(): View
{
return $this->view->make('admin.users.new', [
return view('admin.users.new', [
'languages' => $this->getAvailableLanguages(true),
]);
}
@ -75,7 +75,7 @@ class UserController extends Controller
*/
public function view(User $user): View
{
return $this->view->make('admin.users.view', [
return view('admin.users.view', [
'user' => $user,
'languages' => $this->getAvailableLanguages(true),
]);
@ -132,22 +132,13 @@ class UserController extends Controller
/**
* Get a JSON response of users on the system.
*/
public function json(Request $request): Model|Collection
public function json(Request $request): Model|LengthAwarePaginator
{
$users = QueryBuilder::for(User::query())->allowedFilters(['email'])->paginate(25);
// Handle single user requests.
if ($request->query('user_id')) {
$user = User::query()->findOrFail($request->input('user_id'));
$user->md5 = md5(strtolower($user->email));
return $user;
return User::query()->findOrFail($request->input('user_id'));
}
return $users->map(function ($item) {
$item->md5 = md5(strtolower($item->email));
return $item;
});
return QueryBuilder::for(User::query())->allowedFilters(['email'])->paginate(25);
}
}

View file

@ -52,7 +52,6 @@ class StartupController extends ClientApiController
*/
public function update(UpdateStartupVariableRequest $request, Server $server): array
{
/** @var \Pterodactyl\Models\EggVariable $variable */
$variable = $server->variables()->where('env_variable', $request->input('key'))->first();
$original = $variable->server_value;
@ -62,6 +61,8 @@ class StartupController extends ClientApiController
throw new BadRequestHttpException('The environment variable you are trying to edit is read-only.');
}
/* @var \Pterodactyl\Models\EggVariable $variable */
// Revalidate the variable value using the egg variable specific validation rules for it.
$this->validate($request, ['value' => $variable->rules]);

View file

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Controllers\Api\Remote\Servers;
use Illuminate\Http\Request;
use Pterodactyl\Models\Backup;
use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
@ -98,9 +99,11 @@ class ServerDetailsController extends Controller
if ($subject = $activity->subjects->where('subject_type', 'backup')->first()) {
// Just create a new audit entry for this event and update the server state
// so that power actions, file management, and backups can resume as normal.
/** @var Backup $actualSubject */
$actualSubject = $subject->subject;
Activity::event('server:backup.restore-failed')
->subject($server, $subject->subject)
->property('name', $subject->subject->name)
->subject($server, $actualSubject)
->property('name', $actualSubject->name)
->log();
}
}

View file

@ -49,7 +49,9 @@ abstract class AbstractLoginController extends Controller
/**
* Get the failed login response instance.
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @return never
*
* @throws DisplayException
*/
protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null, string $message = null)
{

View file

@ -72,7 +72,7 @@ class LoginCheckpointController extends AbstractLoginController
} else {
$decrypted = $this->encrypter->decrypt($user->totp_secret);
if ($this->google2FA->verifyKey($decrypted, (string) $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) {
if ($this->google2FA->verifyKey($decrypted, $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) {
Event::dispatch(new ProvidedAuthenticationToken($user));
return $this->sendLoginResponse($user, $request);

View file

@ -9,19 +9,10 @@ use Pterodactyl\Models\User;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Illuminate\Contracts\View\View;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class LoginController extends AbstractLoginController
{
/**
* LoginController constructor.
*/
public function __construct(private ViewFactory $view)
{
parent::__construct();
}
/**
* Handle all incoming requests for the authentication routes and render the
* base authentication view component. React will take over at this point and
@ -29,7 +20,7 @@ class LoginController extends AbstractLoginController
*/
public function index(): View
{
return $this->view->make('templates/auth.core');
return view('templates/auth.core');
}
/**

View file

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Controllers\Auth;
use Illuminate\Support\Str;
use Pterodactyl\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Support\Facades\Password;
@ -67,13 +68,12 @@ class ResetPasswordController extends Controller
* account do not automatically log them in. In those cases, send the user back to the login
* form with a note telling them their password was changed and to log back in.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword|\Pterodactyl\Models\User $user
* @param string $password
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
protected function resetPassword($user, $password)
protected function resetPassword(User $user, $password)
{
$user = $this->userRepository->update($user->id, [
'password' => $this->hasher->make($password),

View file

@ -23,6 +23,6 @@ class IndexController extends Controller
*/
public function index(): View
{
return $this->view->make('templates/base.core');
return view('templates/base.core');
}
}

View file

@ -29,11 +29,11 @@ class ResourceBelongsToServer
public function handle(Request $request, Closure $next): mixed
{
$params = $request->route()->parameters();
if (is_null($params) || !$params['server'] instanceof Server) {
if (!$params['server'] instanceof Server) {
throw new InvalidArgumentException('This middleware cannot be used in a context that is missing a server in the parameters.');
}
/** @var \Pterodactyl\Models\Server $server */
/** @var Server $server */
$server = $request->route()->parameter('server');
$exception = new NotFoundHttpException('The requested resource was not found for this server.');
foreach ($params as $key => $model) {
@ -45,6 +45,7 @@ class ResourceBelongsToServer
continue;
}
/** @var Allocation|Backup|Database|Schedule|Subuser $model */
switch (get_class($model)) {
// All of these models use "server_id" as the field key for the server
// they are assigned to, so the logic is identical for them all.
@ -71,6 +72,7 @@ class ResourceBelongsToServer
// Tasks are special since they're (currently) the only item in the API
// that requires something in addition to the server in order to be accessed.
case Task::class:
/** @var Schedule $schedule */
$schedule = $request->route()->parameter('schedule');
if ($model->schedule_id !== $schedule->id || $schedule->server_id !== $server->id) {
throw $exception;

View file

@ -5,6 +5,7 @@ namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Exceptions\Http\TwoFactorAuthRequiredException;
@ -36,12 +37,17 @@ class RequireTwoFactorAuthentication
*/
public function handle(Request $request, Closure $next): mixed
{
/** @var \Pterodactyl\Models\User $user */
/** @var User $user */
$user = $request->user();
$uri = rtrim($request->getRequestUri(), '/') . '/';
$current = $request->route()->getName();
if (!$user || Str::startsWith($uri, ['/auth/']) || Str::startsWith($current, ['auth.', 'account.'])) {
// Must be logged in
if (!$user instanceof User) {
return $next($request);
}
if (Str::startsWith($uri, ['/auth/']) || Str::startsWith($current, ['auth.', 'account.'])) {
return $next($request);
}

View file

@ -12,7 +12,10 @@ class LocationFormRequest extends AdminFormRequest
public function rules(): array
{
if ($this->method() === 'PATCH') {
return Location::getRulesForUpdate($this->route()->parameter('location')->id);
/** @var Location $location */
$location = $this->route()->parameter('location');
return Location::getRulesForUpdate($location->id);
}
return Location::getRules();

View file

@ -12,7 +12,10 @@ class MountFormRequest extends AdminFormRequest
public function rules(): array
{
if ($this->method() === 'PATCH') {
return Mount::getRulesForUpdate($this->route()->parameter('mount')->id);
/** @var Mount $mount */
$mount = $this->route()->parameter('mount');
return Mount::getRulesForUpdate($mount->id);
}
return Mount::getRules();

View file

@ -29,7 +29,7 @@ abstract class ApplicationApiRequest extends FormRequest
* Determine if the current user is authorized to perform
* the requested action against the API.
*
* @throws \Pterodactyl\Exceptions\PterodactylException
* @throws PterodactylException
*/
public function authorize(): bool
{
@ -42,6 +42,7 @@ abstract class ApplicationApiRequest extends FormRequest
return true;
}
/** @var ApiKey $token */
if ($token->key_type === ApiKey::TYPE_ACCOUNT) {
return true;
}
@ -81,6 +82,7 @@ abstract class ApplicationApiRequest extends FormRequest
*/
public function parameter(string $key, string $expect)
{
/** @var ApiKey $value */
$value = $this->route()->parameter($key);
Assert::isInstanceOf($value, $expect);

View file

@ -11,7 +11,9 @@ class UpdateLocationRequest extends StoreLocationRequest
*/
public function rules(): array
{
$locationId = $this->route()->parameter('location')->id;
/** @var Location $location */
$location = $this->route()->parameter('location');
$locationId = $location->id;
return collect(Location::getRulesForUpdate($locationId))->only([
'short',

View file

@ -12,8 +12,9 @@ class UpdateNodeRequest extends StoreNodeRequest
*/
public function rules(array $rules = null): array
{
$node = $this->route()->parameter('node')->id;
/** @var Node $node */
$node = $this->route()->parameter('node');
return parent::rules(Node::getRulesForUpdate($node));
return parent::rules(Node::getRulesForUpdate($node->id));
}
}

View file

@ -21,6 +21,7 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest
*/
public function rules(): array
{
/** @var Server $server */
$server = $this->route()->parameter('server');
return [
@ -67,6 +68,7 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest
*/
public function databaseName(): string
{
/** @var Server $server */
$server = $this->route()->parameter('server');
Assert::isInstanceOf($server, Server::class);

View file

@ -21,6 +21,7 @@ class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissions
public function rules(): array
{
/** @var Server $server */
$server = $this->route()->parameter('server');
Assert::isInstanceOf($server, Server::class);

View file

@ -63,7 +63,6 @@ abstract class SubuserRequest extends ClientApiRequest
// Otherwise, get the current subuser's permission set, and ensure that the
// permissions they are trying to assign are not _more_ than the ones they
// already have.
/** @var \Pterodactyl\Models\Subuser|null $subuser */
/** @var \Pterodactyl\Services\Servers\GetUserPermissionsService $service */
$service = $this->container->make(GetUserPermissionsService::class);

View file

@ -26,9 +26,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property string|null $startup
* @property bool $script_is_privileged
* @property string|null $script_install
* @property string $script_entry
* @property string $script_container
* @property int|null $copy_script_from
* @property ?string $script_entry
* @property ?string $script_container
* @property ?int $copy_script_from
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string|null $copy_script_install

View file

@ -18,8 +18,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property \Carbon\CarbonImmutable $created_at
* @property \Carbon\CarbonImmutable $updated_at
* @property bool $required
* @property \Pterodactyl\Models\Egg $egg
* @property \Pterodactyl\Models\ServerVariable $serverVariable
* @property Egg $egg
* @property ServerVariable $serverVariable
* @property string $field_type
*
* The "server_value" variable is only present on the object if you've loaded this model
* using the server relationship.

View file

@ -155,6 +155,7 @@ abstract class Model extends IlluminateModel
return;
}
/** @var \Illuminate\Validation\Validator $validator */
$validator = $this->getValidator();
$validator->setData(
// Trying to do self::toArray() here will leave out keys based on the whitelist/blacklist

View file

@ -6,6 +6,7 @@ use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Illuminate\Container\Container;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -24,20 +25,24 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough;
* @property bool $maintenance_mode
* @property int $memory
* @property int $memory_overallocate
* @property int $sum_memory
* @property int $disk
* @property int $disk_overallocate
* @property int $sum_disk
* @property int $upload_size
* @property string $daemon_token_id
* @property string $daemon_token
* @property int $daemonListen
* @property int $daemonSFTP
* @property string $daemonBase
* @property int $servers_count
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Pterodactyl\Models\Location $location
* @property \Pterodactyl\Models\Mount[]|\Illuminate\Database\Eloquent\Collection $mounts
* @property \Pterodactyl\Models\Server[]|\Illuminate\Database\Eloquent\Collection $servers
* @property \Pterodactyl\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations
* @property Location $location
* @property int[]|\Illuminate\Support\Collection $ports
* @property Mount[]|Collection $mounts
* @property Server[]|Collection $servers
* @property Allocation[]|Collection $allocations
*/
class Node extends Model
{
@ -220,11 +225,21 @@ class Node extends Model
return $this->hasMany(Allocation::class);
}
public function loadServerSums(): self
{
$this->loadSum('servers as sum_memory', 'memory');
$this->loadSum('servers as sum_disk', 'disk');
return $this;
}
/**
* Returns a boolean if the node is viable for an additional server to be placed on it.
*/
public function isViable(int $memory, int $disk): bool
public function isViable(int $memory = 0, int $disk = 0): bool
{
$this->loadServerSums();
$memoryLimit = $this->memory * (1.0 + ($this->memory_overallocate / 100.0));
$diskLimit = $this->disk * (1.0 + ($this->disk_overallocate / 100.0));

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Models;
/** @property string $value */
class Setting extends Model
{
/**

View file

@ -11,6 +11,7 @@ use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Builder;
use Pterodactyl\Models\Traits\HasAccessTokens;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\Access\Authorizable;
@ -129,6 +130,10 @@ class User extends Model implements
'root_admin',
];
protected $appends = [
'md5',
];
/**
* Cast values to correct type.
*/
@ -259,6 +264,13 @@ class User extends Model implements
return $this->morphToMany(ActivityLog::class, 'subject', 'activity_log_subjects');
}
public function md5(): Attribute
{
return Attribute::make(
get: fn () => md5(strtolower($this->email)),
);
}
/**
* Returns all the servers that a user can access by way of being the owner of the
* server, or because they are assigned as a subuser for that server.

View file

@ -26,12 +26,14 @@ class ServerInstalled extends Notification implements ShouldQueue, ReceivesEvent
* Handle a direct call to this notification from the server installed event. This is configured
* in the event service provider.
*/
public function handle(Event|Installed $event): void
public function handle(Event|Installed $notification): void
{
$event->server->loadMissing('user');
abort_unless($notification instanceof Installed, 500);
/* @var Installed $notification */
$notification->server->loadMissing('user');
$this->server = $event->server;
$this->user = $event->server->user;
$this->server = $notification->server;
$this->user = $notification->server->user;
// Since we are calling this notification directly from an event listener we need to fire off the dispatcher
// to send the email now. Don't use send() or you'll end up firing off two different events.

View file

@ -78,6 +78,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
*/
public function create(array $fields, bool $validate = true, bool $force = false): Model|bool
{
/** @var \Pterodactyl\Models\Model $instance */
$instance = $this->getBuilder()->newModelInstance();
($force) ? $instance->forceFill($fields) : $instance->fill($fields);
@ -163,6 +164,7 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
public function update(int $id, array $fields, bool $validate = true, bool $force = false): Model|bool
{
try {
/** @var \Pterodactyl\Models\Model $instance */
$instance = $this->getBuilder()->where('id', $id)->firstOrFail();
} catch (ModelNotFoundException) {
throw new RecordNotFoundException();

View file

@ -21,11 +21,7 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
*/
public function getUsageStats(Node $node): array
{
$stats = $this->getBuilder()
->selectRaw('COALESCE(SUM(servers.memory), 0) as sum_memory, COALESCE(SUM(servers.disk), 0) as sum_disk')
->join('servers', 'servers.node_id', '=', 'nodes.id')
->where('node_id', '=', $node->id)
->first();
$stats = $node->loadServerSums();
return Collection::make(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])
->mapWithKeys(function ($value, $key) use ($node) {
@ -53,9 +49,7 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
*/
public function getUsageStatsRaw(Node $node): array
{
$stats = $this->getBuilder()->select(
$this->getBuilder()->raw('COALESCE(SUM(servers.memory), 0) as sum_memory, COALESCE(SUM(servers.disk), 0) as sum_disk')
)->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $node->id)->first();
$stats = $node->loadServerSums();
return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])->mapWithKeys(function ($value, $key) use ($node) {
$maxUsage = $node->{$key};
@ -84,9 +78,7 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
// This is quite ugly and can probably be improved down the road.
// And by probably, I mean it should.
if (is_null($node->servers_count) || $refresh) {
$node->load('servers');
$node->setRelation('servers_count', count($node->getRelation('servers')));
unset($node->servers);
$node->loadCount('servers');
}
return $node;
@ -135,18 +127,4 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
];
})->values();
}
/**
* Returns a node with the given id with the Node's resource usage.
*/
public function getNodeWithResourceUsage(int $node_id): Node
{
$instance = $this->getBuilder()
->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemonListen', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate'])
->selectRaw('COALESCE(SUM(servers.memory), 0) as sum_memory, COALESCE(SUM(servers.disk), 0) as sum_disk')
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
->where('nodes.id', $node_id);
return $instance->first();
}
}

View file

@ -46,6 +46,7 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor
return value($default);
}
/** @var Setting $instance */
$instance = $this->getBuilder()->where('key', $key)->first();
if (is_null($instance)) {
self::$databaseMiss[$key] = true;

View file

@ -6,11 +6,11 @@ use Illuminate\Support\Arr;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\DataAwareRule;
class Fqdn implements Rule, DataAwareRule
final class Fqdn implements Rule, DataAwareRule
{
protected array $data = [];
protected string $message = '';
protected ?string $schemeField = null;
private array $data = [];
private string $message = '';
private ?string $schemeField = null;
/**
* @param array $data

View file

@ -40,23 +40,24 @@ class AssignmentService
*/
public function handle(Node $node, array $data): void
{
$explode = explode('/', $data['allocation_ip']);
$allocationIp = $data['allocation_ip'];
$explode = explode('/', $allocationIp);
if (count($explode) !== 1) {
if (!ctype_digit($explode[1]) || ($explode[1] > self::CIDR_MIN_BITS || $explode[1] < self::CIDR_MAX_BITS)) {
throw new CidrOutOfRangeException();
}
}
$underlying = 'Unknown IP';
try {
// TODO: how should we approach supporting IPv6 with this?
// gethostbyname only supports IPv4, but the alternative (dns_get_record) returns
// an array of records, which is not ideal for this use case, we need a SINGLE
// IP to use, not multiple.
$underlying = gethostbyname($data['allocation_ip']);
$underlying = gethostbyname($allocationIp);
$parsed = Network::parse($underlying);
} catch (Exception $exception) {
/* @noinspection PhpUndefinedVariableInspection */
throw new DisplayException("Could not parse provided allocation IP address ({$underlying}): {$exception->getMessage()}", $exception);
throw new DisplayException("Could not parse provided allocation IP address for $allocationIp ($underlying): {$exception->getMessage()}", $exception);
}
$this->connection->beginTransaction();

View file

@ -73,7 +73,10 @@ class DeleteBackupService
/** @var \Pterodactyl\Extensions\Filesystem\S3Filesystem $adapter */
$adapter = $this->manager->adapter(Backup::ADAPTER_AWS_S3);
$adapter->getClient()->deleteObject([
/** @var \Aws\S3\S3Client $client */
$client = $adapter->getClient();
$client->deleteObject([
'Bucket' => $adapter->getBucket(),
'Key' => sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid),
]);

View file

@ -98,17 +98,17 @@ class InitiateBackupService
// Get the oldest backup the server has that is not "locked" (indicating a backup that should
// never be automatically purged). If we find a backup we will delete it and then continue with
// this process. If no backup is found that can be used an exception is thrown.
/** @var \Pterodactyl\Models\Backup $oldest */
$oldest = $successful->where('is_locked', false)->orderBy('created_at')->first();
if (!$oldest) {
throw new TooManyBackupsException($server->backup_limit);
}
/* @var Backup $oldest */
$this->deleteBackupService->handle($oldest);
}
return $this->connection->transaction(function () use ($server, $name) {
/** @var \Pterodactyl\Models\Backup $backup */
/** @var Backup $backup */
$backup = $this->repository->create([
'server_id' => $server->id,
'uuid' => Uuid::uuid4()->toString(),

View file

@ -119,6 +119,7 @@ class DatabaseManagementService
});
} catch (Exception $exception) {
try {
/** @var ?Database $database */
if ($database instanceof Database) {
$this->repository->dropDatabase($database->database);
$this->repository->dropUser($database->username, $database->remote);

View file

@ -78,7 +78,7 @@ class FindViableNodesService
->where('nodes.public', 1);
if (!empty($this->locations)) {
$query = $query->whereIn('nodes.location_id', $this->locations);
$query = $query->whereIn('location_id', $this->locations);
}
$results = $query->groupBy('nodes.id')

View file

@ -27,13 +27,13 @@ class ProcessScheduleService
*/
public function handle(Schedule $schedule, bool $now = false): void
{
/** @var \Pterodactyl\Models\Task $task */
$task = $schedule->tasks()->orderBy('sequence_id')->first();
if (is_null($task)) {
throw new DisplayException('Cannot process schedule for task execution: no tasks are registered.');
}
/* @var \Pterodactyl\Models\Task $task */
$this->connection->transaction(function () use ($schedule, $task) {
$schedule->forceFill([
'is_processing' => true,

View file

@ -88,14 +88,13 @@ class BuildModificationService
// Handle the addition of allocations to this server. Only assign allocations that are not currently
// assigned to a different server, and only allocations on the same node as the server.
if (!empty($data['add_allocations'])) {
$query = Allocation::query()
->where('node_id', $server->node_id)
$query = $server->node->allocations()
->whereIn('id', $data['add_allocations'])
->whereNull('server_id');
// Keep track of all the allocations we're just now adding so that we can use the first
// one to reset the default allocation to.
$freshlyAllocated = $query->pluck('id')->first();
$freshlyAllocated = $query->first()->id ?? null;
$query->update(['server_id' => $server->id, 'notes' => null]);
}

View file

@ -82,7 +82,7 @@ class ServerCreationService
//
// If that connection fails out we will attempt to perform a cleanup by just
// deleting the server itself from the system.
/** @var \Pterodactyl\Models\Server $server */
/** @var Server $server */
$server = $this->connection->transaction(function () use ($data, $eggVariableData) {
// Create the server and assign any additional allocations to it.
$server = $this->createModel($data);
@ -115,7 +115,7 @@ class ServerCreationService
*/
private function configureDeployment(array $data, DeploymentObject $deployment): Allocation
{
/** @var \Illuminate\Support\Collection $nodes */
/** @var Collection $nodes */
$nodes = $this->findViableNodesService->setLocations($deployment->getLocations())
->setDisk(Arr::get($data, 'disk'))
->setMemory(Arr::get($data, 'memory'))
@ -136,7 +136,7 @@ class ServerCreationService
{
$uuid = $this->generateUniqueUuidCombo();
/** @var \Pterodactyl\Models\Server $model */
/** @var Server $model */
$model = $this->repository->create([
'external_id' => Arr::get($data, 'external_id'),
'uuid' => $uuid,

View file

@ -25,7 +25,7 @@ class UserDeletionService
*
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function handle(int|User $user): ?bool
public function handle(int|User $user): void
{
if ($user instanceof User) {
$user = $user->id;
@ -36,6 +36,6 @@ class UserDeletionService
throw new DisplayException($this->translator->get('admin/user.exceptions.user_has_servers'));
}
return $this->repository->delete($user);
$this->repository->delete($user);
}
}

View file

@ -4,6 +4,7 @@ namespace Pterodactyl\Transformers\Api\Application;
use League\Fractal\Resource\Item;
use Pterodactyl\Models\EggVariable;
use Pterodactyl\Models\ServerVariable;
use League\Fractal\Resource\NullResource;
use Pterodactyl\Services\Acl\Api\AdminAcl;

View file

@ -61,6 +61,7 @@
"laravel/sail": "~1.16",
"mockery/mockery": "~1.5",
"nunomaduro/collision": "~6.3",
"nunomaduro/larastan": "^2.0",
"php-mock/php-mock-phpunit": "~2.6",
"phpunit/phpunit": "~9.5",
"spatie/laravel-ignition": "~1.5"

231
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ae61e7d6e405e3a59c8a54f3eefa2c50",
"content-hash": "88999658a97429a6840f4da57ea115b1",
"packages": [
{
"name": "aws/aws-crt-php",
@ -9004,6 +9004,103 @@
],
"time": "2022-09-29T12:29:49+00:00"
},
{
"name": "nunomaduro/larastan",
"version": "2.2.7",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/larastan.git",
"reference": "a3f67a4a668e477751557b0b19ad2c870e1e4e56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nunomaduro/larastan/zipball/a3f67a4a668e477751557b0b19ad2c870e1e4e56",
"reference": "a3f67a4a668e477751557b0b19ad2c870e1e4e56",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/console": "^9",
"illuminate/container": "^9",
"illuminate/contracts": "^9",
"illuminate/database": "^9",
"illuminate/http": "^9",
"illuminate/pipeline": "^9",
"illuminate/support": "^9",
"mockery/mockery": "^1.4.4",
"php": "^8.0.2",
"phpmyadmin/sql-parser": "^5.5",
"phpstan/phpstan": "^1.8.7"
},
"require-dev": {
"nikic/php-parser": "^4.13.2",
"orchestra/testbench": "^7.0.0",
"phpunit/phpunit": "^9.5.11"
},
"suggest": {
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench"
},
"type": "phpstan-extension",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
},
"phpstan": {
"includes": [
"extension.neon"
]
}
},
"autoload": {
"psr-4": {
"NunoMaduro\\Larastan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nuno Maduro",
"email": "enunomaduro@gmail.com"
}
],
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel",
"keywords": [
"PHPStan",
"code analyse",
"code analysis",
"larastan",
"laravel",
"package",
"php",
"static analysis"
],
"support": {
"issues": "https://github.com/nunomaduro/larastan/issues",
"source": "https://github.com/nunomaduro/larastan/tree/2.2.7"
},
"funding": [
{
"url": "https://www.paypal.com/paypalme/enunomaduro",
"type": "custom"
},
{
"url": "https://github.com/canvural",
"type": "github"
},
{
"url": "https://github.com/nunomaduro",
"type": "github"
},
{
"url": "https://www.patreon.com/nunomaduro",
"type": "patreon"
}
],
"time": "2022-10-30T15:02:40+00:00"
},
{
"name": "phar-io/manifest",
"version": "2.0.3",
@ -9422,6 +9519,138 @@
},
"time": "2022-10-14T12:47:21+00:00"
},
{
"name": "phpmyadmin/sql-parser",
"version": "5.5.0",
"source": {
"type": "git",
"url": "https://github.com/phpmyadmin/sql-parser.git",
"reference": "8ab99cd0007d880f49f5aa1807033dbfa21b1cb5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/8ab99cd0007d880f49f5aa1807033dbfa21b1cb5",
"reference": "8ab99cd0007d880f49f5aa1807033dbfa21b1cb5",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"symfony/polyfill-mbstring": "^1.3"
},
"conflict": {
"phpmyadmin/motranslator": "<3.0"
},
"require-dev": {
"phpmyadmin/coding-standard": "^3.0",
"phpmyadmin/motranslator": "^4.0 || ^5.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.2",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/php-code-coverage": "*",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "^0.16.1",
"vimeo/psalm": "^4.11",
"zumba/json-serializer": "^3.0"
},
"suggest": {
"ext-mbstring": "For best performance",
"phpmyadmin/motranslator": "Translate messages to your favorite locale"
},
"bin": [
"bin/highlight-query",
"bin/lint-query",
"bin/tokenize-query"
],
"type": "library",
"autoload": {
"psr-4": {
"PhpMyAdmin\\SqlParser\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "The phpMyAdmin Team",
"email": "developers@phpmyadmin.net",
"homepage": "https://www.phpmyadmin.net/team/"
}
],
"description": "A validating SQL lexer and parser with a focus on MySQL dialect.",
"homepage": "https://github.com/phpmyadmin/sql-parser",
"keywords": [
"analysis",
"lexer",
"parser",
"sql"
],
"support": {
"issues": "https://github.com/phpmyadmin/sql-parser/issues",
"source": "https://github.com/phpmyadmin/sql-parser"
},
"time": "2021-12-09T04:31:52+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.8.11",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "46e223dd68a620da18855c23046ddb00940b4014"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014",
"reference": "46e223dd68a620da18855c23046ddb00940b4014",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.8.11"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift"
}
],
"time": "2022-10-24T15:45:13+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.17",

35
phpstan.neon Normal file
View file

@ -0,0 +1,35 @@
includes:
- ./vendor/nunomaduro/larastan/extension.neon
parameters:
paths:
- app/
# Level 9 is the highest level
level: 4
ignoreErrors:
# Ignore dynamic methods from 3rd Party Vendor
- '#Call to an undefined method Prologue\\Alerts\\AlertsMessageBag::(success|info|warning|danger)\(\)#'
# Ignore repository interface missing methods
- '#Call to an undefined method Pterodactyl\\Repositories\\Wings\\DaemonRepository::(\w+)\(\)#'
# Ignore magic spatie calls
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder::allowed(\w+)\(\)#'
# This should be replaced with resources instead of a magic transformer factory, robots in disguise
- '#Method Pterodactyl\\Http\\Controllers\\Api\\Client\\ClientApiController::getTransformer\(\) should return T#'
excludePaths:
- app/Repositories
# Bug in Laravel Framework #44807
- app/Console/Commands/Overrides/UpCommand.php
# More magic spatie to be replaced
- app/Extensions/Spatie/Fractalistic/Fractal.php
#
# checkMissingIterableValueType: false