Compare commits

..

No commits in common. "1.0-develop" and "v1.11.4" have entirely different histories.

577 changed files with 10120 additions and 12573 deletions

View File

@ -47,6 +47,5 @@ module.exports = {
'@typescript-eslint/no-use-before-define': 'warn',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }],
'react/no-unknown-property': ['error', { ignore: ['css'] }],
},
};

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
github: [pterodactyl]
github: [matthewpi]

View File

@ -1,82 +0,0 @@
name: Bug Reports
description: For reporting known and reproducible issues with the software.
body:
- type: markdown
attributes:
value: |
Bug reports should only be used for reporting issues with how the software works. For assistance installing this software, as well as debugging issues with dependencies, please use our [Discord server](https://discord.gg/pterodactyl).
- type: textarea
attributes:
label: Current Behavior
description: Please provide a clear & concise description of the issue.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: Please describe what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: Please be as detailed as possible when providing steps to reproduce, failure to provide steps will result in this issue being closed.
validations:
required: true
- type: input
id: panel-version
attributes:
label: Panel Version
description: Version number of your Panel (latest is not a version)
placeholder: 1.4.0
validations:
required: true
- type: input
id: wings-version
attributes:
label: Wings Version
description: Version number of your Wings (latest is not a version)
placeholder: 1.4.2
validations:
required: true
- type: input
id: egg-details
attributes:
label: Games and/or Eggs Affected
description: Please include the specific game(s) or egg(s) you are running into this bug with.
placeholder: Minecraft (Paper), Minecraft (Forge)
- type: input
id: docker-image
attributes:
label: Docker Image
description: The specific Docker image you are using for the game(s) above.
placeholder: ghcr.io/pterodactyl/yolks:java_17
- type: textarea
id: panel-logs
attributes:
label: Error Logs
description: |
Run the following command to collect logs on your system.
Wings: `sudo wings diagnostics`
Panel: `tail -n 150 /var/www/pterodactyl/storage/logs/laravel-$(date +%F).log | nc pteropaste.com 99`
placeholder: "https://pteropaste.com/a1h6z"
render: bash
validations:
required: false
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please [search here](https://github.com/pterodactyl/panel/issues) to see if an issue already exists for your problem.
options:
- label: I have searched the existing issues before opening this issue. I understand that maintainers may close this issue without communication if I have not provided sufficient information.
required: true

View File

@ -1,18 +0,0 @@
name: Pre-Discussed and Approved Topics
description: For topics already previously discussed and approved in the GitHub Discussions section.
body:
- type: markdown
attributes:
value: |
**DO NOT** open a new issue for feature requests _without prior maintainer approval_ and corresponding
thread in the discussions section. You MUST link to the corresponding discussion thread when opening
this feature request issue or it will be closed without further consideration.
- type: textarea
attributes:
label: The Feature Request
- type: input
attributes:
label: GitHub Discussion Link
description: Link to the GitHub Discussions thread where the feature was previously discussed and approved.
validations:
required: true

87
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@ -0,0 +1,87 @@
name: Bug Report
description: Something isn't working quite right in the software.
labels: [not confirmed]
body:
- type: markdown
attributes:
value: |
Bug reports should only be used for reporting issues with how the software works. For assistance installing this software, as well as debugging issues with dependencies, please use our [Discord server](https://discord.gg/pterodactyl).
- type: textarea
attributes:
label: Current Behavior
description: Please provide a clear & concise description of the issue.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: Please describe what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: Please be as detailed as possible when providing steps to reproduce, failure to provide steps will result in this issue being closed.
validations:
required: true
- type: input
id: panel-version
attributes:
label: Panel Version
description: Version number of your Panel (latest is not a version)
placeholder: 1.4.0
validations:
required: true
- type: input
id: wings-version
attributes:
label: Wings Version
description: Version number of your Wings (latest is not a version)
placeholder: 1.4.2
validations:
required: true
- type: input
id: egg-details
attributes:
label: Games and/or Eggs Affected
description: Please include the specific game(s) or egg(s) you are running into this bug with.
placeholder: Minecraft (Paper), Minecraft (Forge)
- type: input
id: docker-image
attributes:
label: Docker Image
description: The specific Docker image you are using for the game(s) above.
placeholder: ghcr.io/pterodactyl/yolks:java_17
- type: textarea
id: panel-logs
attributes:
label: Error Logs
description: |
Run the following command to collect logs on your system.
Wings: `sudo wings diagnostics`
Panel: `tail -n 150 /var/www/pterodactyl/storage/logs/laravel-$(date +%F).log | nc pteropaste.com 99`
placeholder: "https://pteropaste.com/a1h6z"
render: bash
validations:
required: false
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please [search here](https://github.com/pterodactyl/panel/issues) to see if an issue already exists for your problem.
options:
- label: I have searched the existing issues before opening this issue.
required: true
- label: I have provided all relevant details, including the specific game and Docker images I am using if this issue is related to running a server.
required: true
- label: I have checked in the Discord server and believe this is a bug with the software, and not a configuration issue with my specific system.
required: true

View File

@ -1,8 +1,8 @@
blank_issues_enabled: false
blank_issues_enabled: true
contact_links:
- name: Features, Help, or Questions
url: https://github.com/orgs/pterodactyl/discussions/categories/feature-requests
about: The preferred method for getting help with Pterodactyl or seeking new features for the software.
- name: Discord
- name: Installation Help
url: https://discord.gg/pterodactyl
about: For quicker support with installations issues or running ideas by the community.
about: Please visit our Discord for help with your installation.
- name: General Question
url: https://discord.gg/pterodactyl
about: Please visit our Discord for general questions about Pterodactyl.

View File

@ -0,0 +1,32 @@
name: Feature Request
description: Suggest a new feature or improvement for the software.
labels: [feature request]
body:
- type: checkboxes
attributes:
label: Is there an existing feature request for this?
description: Please [search here](https://github.com/pterodactyl/panel/issues?q=is%3Aissue) to see if someone else has already suggested this.
options:
- label: I have searched the existing issues before opening this feature request.
required: true
- type: textarea
attributes:
label: Describe the feature you would like to see.
description: "A clear & concise description of the feature you'd like to have added, and what issues it would solve."
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like.
description: "You must explain how you'd like to see this feature implemented. Technical implementation details are not necessary, rather an idea of how you'd like to see this feature used."
validations:
required: true
- type: textarea
attributes:
label: Additional context to this request.
description: "Add any other context or screenshots about the feature request."
validations:
required: false

View File

@ -1,32 +1,35 @@
name: Build
on:
push:
branches:
- "develop"
- "1.0-develop"
pull_request:
branches:
- "develop"
- "1.0-develop"
jobs:
ui:
name: UI
runs-on: ubuntu-24.04
permissions:
contents: read
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [ 22 ]
node-version: [16]
steps:
- name: Code Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: yarn
cache: "yarn"
- name: Install dependencies
run: yarn install --frozen-lockfile
- run: yarn tsc
- run: yarn lint
- run: yarn build:production
- name: Build
run: yarn build:production

View File

@ -1,27 +1,24 @@
name: Tests
on:
push:
branches:
- "develop"
- "1.0-develop"
pull_request:
branches:
- "develop"
- "1.0-develop"
jobs:
tests:
name: Tests
runs-on: ubuntu-24.04
permissions:
contents: read
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
php:
- 8.2
- 8.3
database:
- mariadb:10
- mariadb:11
- mysql:8
- mysql:9
php: [8.1, 8.2]
database: ["mariadb:10.2", "mysql:8"]
services:
database:
image: ${{ matrix.database }}
@ -30,30 +27,46 @@ jobs:
MYSQL_DATABASE: testing
ports:
- 3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- uses: actions/checkout@v4
- id: composer-cache
- name: Code Checkout
uses: actions/checkout@v3
- name: Get cache directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
- name: Cache
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-${{ matrix.php }}-
- uses: shivammathur/setup-php@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- run: cp .env.ci .env
- run: composer install --no-interaction --no-progress --no-suggest --prefer-dist
- run: vendor/bin/php-cs-fixer fix --dry-run --diff
- run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit
- name: Setup .env
run: cp .env.ci .env
- name: Install dependencies
run: composer install --no-interaction --no-progress --no-suggest --prefer-dist
- name: Unit tests
run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit
if: ${{ always() }}
env:
DB_HOST: UNIT_NO_DB
- run: vendor/bin/phpunit tests/Integration
- name: Integration tests
run: vendor/bin/phpunit tests/Integration
env:
DB_PORT: ${{ job.services.database.ports[3306] }}
DB_USERNAME: root

View File

@ -3,6 +3,11 @@ name: Docker
on:
push:
branches:
- develop
- 1.0-develop
pull_request:
branches:
- develop
- 1.0-develop
release:
types:
@ -11,18 +16,15 @@ on:
jobs:
push:
name: Push
runs-on: ubuntu-24.04
if: "!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip')"
permissions:
contents: read
packages: write
runs-on: ubuntu-20.04
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
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Docker metadata
id: docker_meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: ghcr.io/pterodactyl/panel
flavor: |
@ -33,18 +35,18 @@ jobs:
type=ref,event=branch
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v2
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v2
if: "github.event_name != 'pull_request'"
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Update version
if: "github.event_name == 'release' && github.event.action == 'published'"
@ -54,7 +56,7 @@ jobs:
sed -i "s/ 'version' => 'canary',/ 'version' => '${REF:1}',/" config/app.php
- name: Build and Push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile

36
.github/workflows/lint.yaml vendored Normal file
View File

@ -0,0 +1,36 @@
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: Setup .env
run: cp .env.ci .env
- 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

@ -1,49 +1,89 @@
name: Release
on:
push:
tags:
- "v*"
jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Code checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 22
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn tsc
- run: yarn build:production
- name: create release branch and bump version
node-version: 16
cache: "yarn"
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build
run: yarn build:production
- name: Create release branch and bump version
env:
VERSION: ${{ github.ref_name }}
REF: ${{ github.ref }}
run: |
BRANCH=release/${{ env.VERSION }}
git config --local user.email 'ci@pterodactyl.io'
git config --local user.name 'Pterodactyl CI'
git checkout -b "$BRANCH"
git push -u origin "$BRANCH"
sed -i "s/'canary'/'${VERSION:1}'/" config/app.php
BRANCH=release/${REF:10}
git config --local user.email "ci@pterodactyl.io"
git config --local user.name "Pterodactyl CI"
git checkout -b $BRANCH
git push -u origin $BRANCH
sed -i "s/ 'version' => 'canary',/ 'version' => '${REF:11}',/" config/app.php
git add config/app.php
git commit -m 'ci(release): bump version'
git commit -m "ci(release): bump version"
git push
- name: create release archive
- name: Create release archive
run: |
rm -rf node_modules tests CONTRIBUTING.md flake.lock flake.nix phpunit.xml shell.nix
rm -rf node_modules tests CODE_OF_CONDUCT.md CONTRIBUTING.md flake.lock flake.nix phpunit.xml shell.nix
tar -czf panel.tar.gz * .editorconfig .env.example .eslintignore .eslintrc.js .gitignore .prettierrc.json
- name: write changelog
- name: Extract changelog
env:
REF: ${{ github.ref }}
run: |
sed -n "/^## ${{ github.ref_name }}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG
- uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
sed -n "/^## ${REF:10}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG
- name: Create checksum and add to changelog
run: |
SUM=`sha256sum panel.tar.gz`
echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG
echo $SUM > checksum.txt
- name: Create release
id: create_release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
draft: true
prerelease: ${{ contains(github.ref_name, 'rc') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }}
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
body_path: ./RELEASE_CHANGELOG
files: |
panel.tar.gz
- name: Upload release archive
id: upload-release-archive
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: panel.tar.gz
asset_name: panel.tar.gz
asset_content_type: application/gzip
- name: Upload release checksum
id: upload-release-checksum
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./checksum.txt
asset_name: checksum.txt
asset_content_type: text/plain

5
.gitignore vendored
View File

@ -21,9 +21,7 @@ public/assets/manifest.json
# For local development with docker
# Remove if we ever put the Dockerfile in the repo
.dockerignore
docker-compose.*
!docker-compose.example.yml
!docker-compose.example.yaml
docker-compose.yml
# for image related files
misc
@ -35,3 +33,4 @@ resources/lang/locales.js
/public/build
/public/hot
result
docker-compose.yaml

View File

@ -2,25 +2,20 @@
use PhpCsFixer\Config;
use PhpCsFixer\Finder;
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
$finder = (new Finder())
->name('*.php')
->ignoreVCSIgnored(true)
->exclude([__DIR__ . '/bootstrap/cache'])
->in([
__DIR__ . '/app',
__DIR__ . '/bootstrap',
__DIR__ . '/config',
__DIR__ . '/database',
__DIR__ . '/routes',
__DIR__ . '/tests',
]);
->in(__DIR__)
->exclude([
'vendor',
'node_modules',
'storage',
'bootstrap/cache',
])
->notName(['_ide_helper*']);
return (new Config())
->setRiskyAllowed(true)
->setFinder($finder)
->setUsingCache(true)
->setParallelConfig(ParallelConfigFactory::detect())
->setRules([
'@Symfony' => true,
'@PSR1' => true,
@ -30,8 +25,8 @@ return (new Config())
'combine_consecutive_unsets' => true,
'concat_space' => ['spacing' => 'one'],
'heredoc_to_nowdoc' => true,
// 'no_alias_functions' => true,
// 'no_unreachable_default_argument_value' => true,
'no_alias_functions' => true,
'no_unreachable_default_argument_value' => true,
'no_useless_return' => true,
'ordered_imports' => [
'sort_algorithm' => 'length',
@ -47,7 +42,7 @@ return (new Config())
'var',
],
],
// 'random_api_migration' => true,
'random_api_migration' => true,
'ternary_to_null_coalescing' => true,
'yoda_style' => [
'equal' => false,

View File

@ -3,70 +3,6 @@ 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.
## v1.12.0
### Fixed
* [CVE-2025-68954](https://github.com/pterodactyl/panel/security/advisories/GHSA-8c39-xppg-479c)
* [CVE-2025-69197](https://github.com/pterodactyl/panel/security/advisories/GHSA-rgmp-4873-r683)
* [CVE-2025-69198](https://github.com/pterodactyl/panel/security/advisories/GHSA-jw2v-cq5x-q68g)
* Fixes a self-XSS issue when entering random data into boxes while creating a new database host.
* Fixes missing `HttpForbiddenException` import in the backup status controller.
* Fixes issue where scheduled tasks would execute every minute regardless of their configured cron syntax.
* Pressing `Ctrl+Z` to undo while editing a file no longer deletes the initial file content.
* Fixed incorrect error message being returned when attempting to delete your own account as an admin.
* Fixes node description not being settable via the API.
* Fixes 0-bytes files returning an error when attempting to upload.
* Fixes nodes displaying the first available location even when that field was not edited and the node has a different value set.
* Fixes allocation notes not being reset when a server is deleted. ([#5157](https://github.com/pterodactyl/panel/pull/5157))
### Changed
* Minimum NodeJS version updated to 22 for building.
* Updated all JS and PHP dependencies to their latest versions (where feasible).
* The endpoint for disabling 2FA on an account using the client API changed from `DELETE /api/client/account/two-factor` to `POST /api/client/account/two-factor/disable`
* `^C` in an egg's stop configuration no longer rewrites itself into the default stop configuration.
* `IBM Plex Sans` font is now bundled with the local assets instead of loading from Google CDNs.
* Upload size on nodes is no longer restricted to a max of 1024MB, any positive integer value can be used.
* Administrators are now listed first when viewing a list of all users on the system.
* Websocket no longer endlessly polls when connection issues are encountered, or when Wings disconnects the user for a reason that should not be re-attempted.
## v1.11.10
### Fixed
* Update Laravel to address [CVE-2024-52301](https://github.com/advisories/GHSA-gv7v-rgg6-548h)
### Changed
* Minimum PHP version is now 8.2 due to Laravel upgrade!
## v1.11.9
### Fixed
* Fixed issue with CI not pushing Docker image
## v1.11.8
### Fixed
* Fixed an issue where a `DELETE` request was used instead of a `POST`, potentially logging user passwords in plain text if they disable 2FA.
## v1.11.7
### Added
* Java 21 to Minecraft eggs
### Changed
* Updated Minecraft EULA link
### Fixed
* Fixed backups not ever being marked as completed (#5088)
* Fixed `.7z` files not being detected as a compressed file (#5016)
## v1.11.6
### Changed
* Better node ownership checks for internal backup endpoints
* Improved validation rules on `docker_image` fields to prevent invalid inputs
### Fixed
* Multiple XSS vulnerabilities in the admin area ([GHSA-384w-wffr-x63q](https://github.com/pterodactyl/panel/security/advisories/GHSA-384w-wffr-x63q))
## v1.11.5
### Fixed
* Rust egg using the wrong Docker image, breaking Rust modding frameworks.
## v1.11.4
### Added
* Added support for the `server.queryport` option on the Rust egg.

74
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at support@pterodactyl.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1,33 +1,31 @@
# Contributing
**Pterodactyl does not accept Pull Requests (PRs) _for new functionality_** that was not previously approved by project
maintainers on GitHub discussions. It has become overwhelming to try and give the proper time and attention that such
complicated PRs tend to require. As a result, it is in the project's best interest to limit the scope of work on new
functionality to work done within the core project team.
Pterodactyl does not accept Pull Requests (PRs) _for new functionality_ from users that are not currently part of the
core project team. It has become overwhelming to try and give the proper time and attention that such complicated PRs
tend to require — and deserve. As a result, it is in the project's best interest to limit the scope of work on
new functionality to work done within the core project team.
PRs that address existing bugs or features **with a corresponding issue opened in our issue tracker** will continue to
be accepted and reviewed. Their scope is often significantly more targeted, and simply improving upon existing and well
defined logic.
PRs that address existing _bugs_ with a corresponding issue opened in our issue tracker will continue to be accepted
and reviewed. Their scope is often significantly more targeted, and simply improving upon existing and well defined
logic.
## AI Assistance
### Responsible Disclosure
If you use any type of AI assistance while developing for Pterodactyl **it must be disclosed** within the
corresponding review PR and you must identify what it was used for. All interaction with the community must be written
by a human. **You MUST NOT use AI to compose any PR titles, descriptions, or comments**, and must not use AI to
interact with the GitHub Discussions community.
## Responsible Disclosure
This is an in-depth project making use of many moving pieces. While we strive to keep everything as secure as possible
This is a fairly in-depth project and makes use of a lot of parts. We strive to keep everything as secure as possible
and welcome you to take a look at the code provided in this project yourself. We do ask that you be considerate of
others who are using the software and not publicly disclose security issues. Please see [`SECURITY.md`](/SECURITY.md)
for information on how to report security issues to the team.
others who are using the software and not publicly disclose security issues without contacting us first by email.
## Contact Us
We'll make a deal with you: if you contact us by email, and we fail to respond to you within a week you are welcome to
publicly disclose whatever issue you have found. We understand how frustrating it is when you find something big and
no one will respond to you. This holds us to a standard of providing prompt attention to any issues that arise and
keeping this community safe.
If you've found what you believe is a security issue please email `matthew@pterodactyl.io`. Please check
[SECURITY.md](/SECURITY.md) for additional details.
### Contact Us
You can find us in a couple places online. First and foremost, we're active right here on GitHub. If you encounter a
bug or other problems, open an issue on here for us to take a look at it. Please make use of
our [GitHub Discussions](https://github.com/orgs/pterodactyl/discussions/categories/feature-requests)
for any feature requests, general questions, or help with the software.
bug or other problems, open an issue on here for us to take a look at it. We also accept feature requests here as well.
You can also find us on [Discord](https://discord.gg/pterodactyl).

View File

@ -2,7 +2,7 @@
# Build the assets that are needed for the frontend. This build stage is then discarded
# since we won't need NodeJS anymore in the future. This Docker image ships a final production
# level distribution of Pterodactyl.
FROM --platform=$TARGETOS/$TARGETARCH node:22-alpine
FROM --platform=$TARGETOS/$TARGETARCH mhart/alpine-node:14
WORKDIR /app
COPY . ./
RUN yarn install --frozen-lockfile \
@ -10,7 +10,7 @@ RUN yarn install --frozen-lockfile \
# Stage 1:
# Build the actual container with all of the needed PHP dependencies that will run the application.
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
FROM --platform=$TARGETOS/$TARGETARCH php:8.1-fpm-alpine
WORKDIR /app
COPY . ./
COPY --from=0 /app/public/assets ./public/assets

View File

@ -7,7 +7,7 @@
# Pterodactyl Panel
Pterodactyl® is a free, open-source game server management panel built with PHP, React, and Go. Designed with security
Pterodactyl® is a free, open-source game server management panel built with PHP, React, and Go. Designed with security
in mind, Pterodactyl runs all game servers in isolated Docker containers while exposing a beautiful and intuitive
UI to end users.
@ -25,16 +25,19 @@ Stop settling for less. Make game servers a first class citizen on your platform
## Sponsors
I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's development.
[Interested in becoming a sponsor?](https://github.com/sponsors/pterodactyl)
[Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi)
| Company | About |
|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. |
| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! |
| [**HostEZ**](https://hostez.io) | US & EU Rust & Minecraft Hosting. DDoS Protected bare metal, VPS and colocation with low latency, high uptime and maximum availability. EZ! |
| [**Blueprint**](https://blueprint.zip/?utm_source=pterodactyl&utm_medium=sponsor) | Create and install Pterodactyl addons and themes with the growing Blueprint framework - the package-manager for Pterodactyl. Use multiple modifications at once without worrying about conflicts and make use of the large extension ecosystem. |
| [**indifferent broccoli**](https://indifferentbroccoli.com/) | indifferent broccoli is a game server hosting and rental company. With us, you get top-notch computer power for your gaming sessions. We destroy lag, latency, and complexity--letting you focus on the fun stuff. |
| Company | About |
|-----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**WISP**](https://wisp.gg) | Extra features. |
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**WemX**](https://wemx.net/) | WemX helps automate your hosting company or SaaS business by automating billing, user management, authentication, and much more. |
| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. |
| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! |
| [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa. |
| [**DutchIS**](https://dutchis.net?ref=pterodactyl) | DutchIS provides instant infrastructure such as pay per use VPS hosting. Start your game hosting journey on DutchIS. |
| [**Skoali**](https://skoali.com/) | Skoali is a French company that hosts game servers and other types of services (VPS, WEB, Dedicated servers, ...). We also have a free plan for Minecraft and Garry's Mod. |
| [**Rabbit Computing**](https://www.rabbitcomputing.com/link.php?id=5) | Rabbit Computing offers powerful VPS servers, highly available game hosting, and fully unlimited web hosting. Use code README for 20% off your first three months! |
### Supported Games
@ -63,7 +66,7 @@ and there are plenty more games available provided by the community. Some of the
* Xonotic
* Starmade
* Discord ATLBot, and most other Node.js/Python discord bots
* [and many more...](https://pterodactyleggs.com)
* [and many more...](https://github.com/parkervcp/eggs)
## License

View File

@ -2,23 +2,19 @@
## Supported Versions
Pterodactyl only provides security support for the latest `major.minor` versions of the Panel and Wings software.
If a security vulnerability is found in an older version but cannot be reproduced on a supported version it will
not be considered. Additionally, security issues found in unreleased code will be addressed, but do not warrant a
security advisory.
The following versions of Pterodactyl are receiving active support and maintenance. Any security vulnerabilities discovered must be reproducible in supported versions.
| Panel | Daemon | Supported |
|--------|--------------|--------------------|
| 1.10.x | wings@1.7.x | :white_check_mark: |
| 1.11.x | wings@1.11.x | :white_check_mark: |
| 0.7.x | daemon@0.6.x | :x: |
For example, if the latest version of the Panel is `1.2.5` then we only support security reports for issues that
occur on `>= 1.2.x` versions of the Panel software. The Panel and Wings have their own versions, but they generally
follow eachother.
## Reporting a Vulnerability
Please use our GitHub Security reporting meachnism to quickly alert the team to any security issues you come across,
or send an email to `security@pterodactyl.io` with the details of your report.
Please reach out directly to any project team member on Discord when reporting a security vulnerability, or you can email `matthew@pterodactyl.io`.
We make every effort to respond as soon as possible, although it may take a day or two for us to sync internally and
determine the severity of the report and its impact. Please, _do not_ use a public facing channel or GitHub issues to
report sensitive security issues.
We make every effort to respond as soon as possible, although it may take a day or two for us to sync internally and determine the severity of the report and its impact. Please, _do not_ use a public facing channel or GitHub issues to report sensitive security issues.
As part of our process, we will create a security advisory for the affected versions and disclose it publicly, usually
two to four weeks after a releasing a version that addresses it.
As part of our process, we will create a security advisory for the affected versions and disclose it publicly, usually two to four weeks after a releasing a version that addresses it.

View File

@ -20,7 +20,7 @@ class DeleteLocationCommand extends Command
*/
public function __construct(
private LocationDeletionService $deletionService,
private LocationRepositoryInterface $repository,
private LocationRepositoryInterface $repository
) {
parent::__construct();
}

View File

@ -36,7 +36,7 @@ class CleanServiceBackupFilesCommand extends Command
collect($files)->each(function (\SplFileInfo $file) {
$lastModified = Carbon::createFromTimestamp($this->disk->lastModified($file->getPath()));
if ((int) $lastModified->diffInMinutes(Carbon::now()) > self::BACKUP_THRESHOLD_MINUTES) {
if ($lastModified->diffInMinutes(Carbon::now()) > self::BACKUP_THRESHOLD_MINUTES) {
$this->disk->delete($file->getPath());
$this->info(trans('command/messages.maintenance.deleting_service_backup', ['file' => $file->getFilename()]));
}

View File

@ -17,7 +17,7 @@ class NodeConfigurationCommand extends Command
{
$column = ctype_digit((string) $this->argument('node')) ? 'id' : 'uuid';
/** @var Node $node */
/** @var \Pterodactyl\Models\Node $node */
$node = Node::query()->where($column, $this->argument('node'))->firstOr(function () {
$this->error('The selected node does not exist.');

View File

@ -21,6 +21,6 @@ class UpCommand extends BaseUpCommand
return 1;
}
return parent::handle();
return parent::handle() ?? 0;
}
}

View File

@ -2,6 +2,7 @@
namespace Pterodactyl\Console\Commands\Schedule;
use Exception;
use Illuminate\Console\Command;
use Pterodactyl\Models\Schedule;
use Illuminate\Support\Facades\Log;
@ -66,7 +67,7 @@ class ProcessRunnableCommand extends Command
'schedule' => $schedule->name,
'hash' => $schedule->hashid,
]));
} catch (\Throwable $exception) {
} catch (\Throwable|\Exception $exception) {
Log::error($exception, ['schedule_id' => $schedule->id]);
$this->error("An error was encountered while processing Schedule #$schedule->id: " . $exception->getMessage());

View File

@ -30,7 +30,7 @@ class BulkPowerActionCommand extends Command
/**
* Handle the bulk power request.
*
* @throws ValidationException
* @throws \Illuminate\Validation\ValidationException
*/
public function handle()
{
@ -97,7 +97,7 @@ class BulkPowerActionCommand extends Command
$instance->whereIn('id', $servers)->orWhereIn('node_id', $nodes);
} elseif (empty($nodes) && !empty($servers)) {
$instance->whereIn('id', $servers);
} elseif (!empty($nodes) && empty($servers)) { // @phpstan-ignore empty.variable
} elseif (!empty($nodes) && empty($servers)) {
$instance->whereIn('node_id', $nodes);
}

View File

@ -39,8 +39,8 @@ class UpgradeCommand extends Command
$this->line($this->getUrl());
}
if (version_compare(PHP_VERSION, '8.2.0', '<')) {
$this->error('Cannot execute self-upgrade process. The minimum required PHP version required is 8.2.0, you have [' . PHP_VERSION . '].');
if (version_compare(PHP_VERSION, '7.4.0') < 0) {
$this->error('Cannot execute self-upgrade process. The minimum required PHP version required is 7.4.0, you have [' . PHP_VERSION . '].');
}
$user = 'www-data';
@ -133,7 +133,7 @@ class UpgradeCommand extends Command
/** @var \Illuminate\Foundation\Application $app */
$app = require __DIR__ . '/../../../bootstrap/app.php';
/** @var Kernel $kernel */
/** @var \Pterodactyl\Console\Kernel $kernel */
$kernel = $app->make(Kernel::class);
$kernel->bootstrap();
$this->setLaravel($app);

View File

@ -11,5 +11,5 @@ interface HashidsInterface extends VendorHashidsInterface
*
* @throws \InvalidArgumentException
*/
public function decodeFirst(string $encoded, ?string $default = null): mixed;
public function decodeFirst(string $encoded, string $default = null): mixed;
}

View File

@ -12,14 +12,14 @@ interface NestRepositoryInterface extends RepositoryInterface
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function getWithEggs(?int $id = null): Collection|Nest;
public function getWithEggs(int $id = null): Collection|Nest;
/**
* Return a nest or all nests and the count of eggs and servers for that nest.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function getWithCounts(?int $id = null): Collection|Nest;
public function getWithCounts(int $id = null): Collection|Nest;
/**
* Return a nest along with its associated eggs and the servers relation on those eggs.

View File

@ -16,12 +16,12 @@ interface ServerRepositoryInterface extends RepositoryInterface
/**
* Return a collection of servers with their associated data for rebuild operations.
*/
public function getDataForRebuild(?int $server = null, ?int $node = null): Collection;
public function getDataForRebuild(int $server = null, int $node = null): Collection;
/**
* Return a collection of servers with their associated data for reinstall operations.
*/
public function getDataForReinstall(?int $server = null, ?int $node = null): Collection;
public function getDataForReinstall(int $server = null, int $node = null): Collection;
/**
* Return a server model and all variables associated with the server.

View File

@ -10,7 +10,7 @@ interface SettingsRepositoryInterface extends RepositoryInterface
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function set(string $key, ?string $value = null);
public function set(string $key, string $value = null);
/**
* Retrieve a persistent setting from the database.

View File

@ -1,65 +0,0 @@
<?php
namespace Pterodactyl\Enum;
use Illuminate\Http\Request;
use Webmozart\Assert\Assert;
use Pterodactyl\Models\Server;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Routing\Middleware\ThrottleRequests;
/**
* A basic resource throttler for individual servers. This is applied in addition
* to existing rate limits and allows the code to slow down speedy users that might
* be creating resources a little too quickly for comfort. This throttle generally
* only applies to creation flows, and not general view/edit/delete flows.
*/
enum ResourceLimit
{
case Allocation;
case Backup;
case Database;
case Schedule;
case Subuser;
case Websocket;
case FilePull;
public function throttleKey(): string
{
return mb_strtolower("api.client:server-resource:{$this->name}");
}
/**
* Returns a middleware that will throttle the specific resource by server. This
* throttle applies to any user making changes to that resource on the specific
* server, it is NOT per-user.
*/
public function middleware(): string
{
return ThrottleRequests::using($this->throttleKey());
}
public function limit(): Limit
{
return match($this) {
self::Backup => Limit::perMinutes(15, 3),
self::Database => Limit::perMinute(2),
self::FilePull => Limit::perMinutes(10, 5),
self::Subuser => Limit::perMinutes(15, 10),
self::Websocket => Limit::perMinute(5),
default => Limit::perMinute(2),
};
}
public static function boot(): void
{
foreach (self::cases() as $case) {
RateLimiter::for($case->throttleKey(), function (Request $request) use ($case) {
Assert::isInstanceOf($server = $request->route()->parameter('server'), Server::class);
return $case->limit()->by($server->uuid);
});
}
}
}

View File

@ -72,7 +72,7 @@ class DisplayException extends PterodactylException implements HttpExceptionInte
try {
$logger = Container::getInstance()->make(LoggerInterface::class);
} catch (\Exception) {
} catch (Exception) {
throw $this->getPrevious();
}

View File

@ -235,7 +235,7 @@ class Handler extends ExceptionHandler
*/
public static function isReportable(\Exception $exception): bool
{
return (new self(Container::getInstance()))->shouldReport($exception);
return (new static(Container::getInstance()))->shouldReport($exception);
}
/**
@ -262,7 +262,7 @@ class Handler extends ExceptionHandler
{
$previous = [];
while ($value = $e->getPrevious()) {
if (!$value instanceof \Throwable) { // @phpstan-ignore instanceof.alwaysTrue
if (!$value instanceof \Throwable) {
break;
}
$previous[] = $value;

View File

@ -10,7 +10,7 @@ class HttpForbiddenException extends HttpException
/**
* HttpForbiddenException constructor.
*/
public function __construct(?string $message = null, ?\Throwable $previous = null)
public function __construct(string $message = null, \Throwable $previous = null)
{
parent::__construct(Response::HTTP_FORBIDDEN, $message, $previous);
}

View File

@ -11,7 +11,7 @@ class ServerStateConflictException extends ConflictHttpException
* Exception thrown when the server is in an unsupported state for API access or
* certain operations within the codebase.
*/
public function __construct(Server $server, ?\Throwable $previous = null)
public function __construct(Server $server, \Throwable $previous = null)
{
$message = 'This server is currently in an unsupported state, please try again later.';
if ($server->isSuspended()) {

View File

@ -11,7 +11,7 @@ class TwoFactorAuthRequiredException extends HttpException implements HttpExcept
/**
* TwoFactorAuthRequiredException constructor.
*/
public function __construct(?\Throwable $previous = null)
public function __construct(\Throwable $previous = null)
{
parent::__construct(Response::HTTP_BAD_REQUEST, 'Two-factor authentication is required on this account in order to access this endpoint.', $previous);
}

View File

@ -10,7 +10,7 @@ class ServiceLimitExceededException extends DisplayException
* Exception thrown when something goes over a defined limit, such as allocated
* ports, tasks, databases, etc.
*/
public function __construct(string $message, ?\Throwable $previous = null)
public function __construct(string $message, \Throwable $previous = null)
{
parent::__construct($message, $previous, self::LEVEL_WARNING);
}

View File

@ -38,7 +38,7 @@ class BackupManager
/**
* Returns a backup adapter instance.
*/
public function adapter(?string $name = null): FilesystemAdapter
public function adapter(string $name = null): FilesystemAdapter
{
return $this->get($name ?: $this->getDefaultAdapter());
}

View File

@ -19,7 +19,7 @@ class DynamicDatabaseConnection
public function __construct(
protected ConfigRepository $config,
protected Encrypter $encrypter,
protected DatabaseHostRepositoryInterface $repository,
protected DatabaseHostRepositoryInterface $repository
) {
}

View File

@ -2,16 +2,21 @@
namespace Pterodactyl\Extensions;
use Illuminate\Support\Arr;
use Hashids\Hashids as VendorHashids;
use Pterodactyl\Contracts\Extensions\HashidsInterface;
class Hashids extends VendorHashids implements HashidsInterface
{
public function decodeFirst(string $encoded, ?string $default = null): mixed
/**
* {@inheritdoc}
*/
public function decodeFirst(string $encoded, string $default = null): mixed
{
$result = $this->decode($encoded);
if (!is_array($result)) {
return $default;
}
return Arr::first($result, null, $default);
return array_first($result, null, $default);
}
}

View File

@ -6,7 +6,7 @@ use Pterodactyl\Models\ApiKey;
use Laravel\Sanctum\NewAccessToken as SanctumAccessToken;
/**
* @property ApiKey $accessToken
* @property \Pterodactyl\Models\ApiKey $accessToken
*/
class NewAccessToken extends SanctumAccessToken
{

View File

@ -5,9 +5,6 @@ namespace Pterodactyl\Facades;
use Illuminate\Support\Facades\Facade;
use Pterodactyl\Services\Activity\ActivityLogTargetableService;
/**
* @mixin \Pterodactyl\Services\Activity\ActivityLogTargetableService
*/
class LogTarget extends Facade
{
protected static function getFacadeAccessor(): string

View File

@ -9,6 +9,7 @@ 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,6 +24,7 @@ class ApiController extends Controller
private AlertsMessageBag $alert,
private ApiKeyRepositoryInterface $repository,
private KeyCreationService $keyCreationService,
private ViewFactory $view,
) {
}
@ -31,7 +33,7 @@ class ApiController extends Controller
*/
public function index(Request $request): View
{
return view('admin.api.index', [
return $this->view->make('admin.api.index', [
'keys' => $this->repository->getApplicationKeys($request->user()),
]);
}
@ -46,7 +48,7 @@ class ApiController extends Controller
$resources = AdminAcl::getResourceList();
sort($resources);
return view('admin.api.new', [
return $this->view->make('admin.api.new', [
'resources' => $resources,
'permissions' => [
'r' => AdminAcl::READ,

View File

@ -3,6 +3,7 @@
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;
@ -11,7 +12,7 @@ class BaseController extends Controller
/**
* BaseController constructor.
*/
public function __construct(private SoftwareVersionService $version)
public function __construct(private SoftwareVersionService $version, private ViewFactory $view)
{
}
@ -20,6 +21,6 @@ class BaseController extends Controller
*/
public function index(): View
{
return view('admin.index', ['version' => $this->version]);
return $this->view->make('admin.index', ['version' => $this->version]);
}
}

View File

@ -2,10 +2,12 @@
namespace Pterodactyl\Http\Controllers\Admin;
use Exception;
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;
@ -28,6 +30,7 @@ class DatabaseController extends Controller
private HostDeletionService $deletionService,
private HostUpdateService $updateService,
private LocationRepositoryInterface $locationRepository,
private ViewFactory $view
) {
}
@ -36,7 +39,7 @@ class DatabaseController extends Controller
*/
public function index(): View
{
return view('admin.databases.index', [
return $this->view->make('admin.databases.index', [
'locations' => $this->locationRepository->getAllWithNodes(),
'hosts' => $this->repository->getWithViewDetails(),
]);
@ -49,7 +52,7 @@ class DatabaseController extends Controller
*/
public function view(int $host): View
{
return view('admin.databases.view', [
return $this->view->make('admin.databases.view', [
'locations' => $this->locationRepository->getAllWithNodes(),
'host' => $this->repository->find($host),
'databases' => $this->databaseRepository->getDatabasesForHost($host),

View File

@ -26,7 +26,7 @@ class LocationController extends Controller
protected LocationDeletionService $deletionService,
protected LocationRepositoryInterface $repository,
protected LocationUpdateService $updateService,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -35,7 +35,7 @@ class LocationController extends Controller
*/
public function index(): View
{
return view('admin.locations.index', [
return $this->view->make('admin.locations.index', [
'locations' => $this->repository->getAllWithDetails(),
]);
}
@ -47,7 +47,7 @@ class LocationController extends Controller
*/
public function view(int $id): View
{
return view('admin.locations.view', [
return $this->view->make('admin.locations.view', [
'location' => $this->repository->getWithNodes($id),
]);
}
@ -86,7 +86,7 @@ class LocationController extends Controller
* Delete a location from the system.
*
* @throws \Exception
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function delete(Location $location): RedirectResponse
{

View File

@ -28,7 +28,7 @@ class MountController extends Controller
protected NestRepositoryInterface $nestRepository,
protected LocationRepositoryInterface $locationRepository,
protected MountRepository $repository,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -37,7 +37,7 @@ class MountController extends Controller
*/
public function index(): View
{
return view('admin.mounts.index', [
return $this->view->make('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 view('admin.mounts.view', [
return $this->view->make('admin.mounts.view', [
'mount' => $this->repository->getWithRelations($id),
'nests' => $nests,
'locations' => $locations,

View File

@ -27,7 +27,7 @@ class EggController extends Controller
protected EggRepositoryInterface $repository,
protected EggUpdateService $updateService,
protected NestRepositoryInterface $nestRepository,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -41,7 +41,7 @@ class EggController extends Controller
$nests = $this->nestRepository->getWithEggs();
\JavaScript::put(['nests' => $nests->keyBy('id')]);
return view('admin.eggs.new', ['nests' => $nests]);
return $this->view->make('admin.eggs.new', ['nests' => $nests]);
}
/**
@ -66,7 +66,7 @@ class EggController extends Controller
*/
public function view(Egg $egg): View
{
return view('admin.eggs.view', [
return $this->view->make('admin.eggs.view', [
'egg' => $egg,
'images' => array_map(
fn ($key, $value) => $key === $value ? $value : "$key|$value",
@ -111,7 +111,7 @@ class EggController extends Controller
/**
* Normalizes a string of docker image data into the expected egg format.
*/
protected function normalizeDockerImages(?string $input = null): array
protected function normalizeDockerImages(string $input = null): array
{
$data = array_map(fn ($value) => trim($value), explode("\n", $input ?? ''));

View File

@ -21,7 +21,7 @@ class EggScriptController extends Controller
protected AlertsMessageBag $alert,
protected EggRepositoryInterface $repository,
protected InstallScriptService $installScriptService,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -41,7 +41,7 @@ class EggScriptController extends Controller
['copy_script_from', '=', $egg->id],
]);
return view('admin.eggs.scripts', [
return $this->view->make('admin.eggs.scripts', [
'copyFromOptions' => $copy,
'relyOnScript' => $rely,
'egg' => $egg,

View File

@ -21,7 +21,7 @@ class EggShareController extends Controller
protected AlertsMessageBag $alert,
protected EggExporterService $exporterService,
protected EggImporterService $importerService,
protected EggUpdateImporterService $updateImporterService,
protected EggUpdateImporterService $updateImporterService
) {
}

View File

@ -26,7 +26,7 @@ class EggVariableController extends Controller
protected VariableUpdateService $updateService,
protected EggRepositoryInterface $repository,
protected EggVariableRepositoryInterface $variableRepository,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -39,7 +39,7 @@ class EggVariableController extends Controller
{
$egg = $this->repository->getWithVariables($egg);
return view('admin.eggs.variables', ['egg' => $egg]);
return $this->view->make('admin.eggs.variables', ['egg' => $egg]);
}
/**
@ -69,7 +69,7 @@ class EggVariableController extends Controller
{
$this->updateService->handle($variable, $request->normalize());
$this->alert->success(trans('admin/nests.variables.notices.variable_updated', [
'variable' => htmlspecialchars($variable->name),
'variable' => $variable->name,
]))->flash();
return redirect()->route('admin.nests.egg.variables', $egg->id);
@ -82,7 +82,7 @@ class EggVariableController extends Controller
{
$this->variableRepository->delete($variable->id);
$this->alert->success(trans('admin/nests.variables.notices.variable_deleted', [
'variable' => htmlspecialchars($variable->name),
'variable' => $variable->name,
]))->flash();
return redirect()->route('admin.nests.egg.variables', $egg);

View File

@ -24,7 +24,7 @@ class NestController extends Controller
protected NestDeletionService $nestDeletionService,
protected NestRepositoryInterface $repository,
protected NestUpdateService $nestUpdateService,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -35,7 +35,7 @@ class NestController extends Controller
*/
public function index(): View
{
return view('admin.nests.index', [
return $this->view->make('admin.nests.index', [
'nests' => $this->repository->getWithCounts(),
]);
}
@ -45,7 +45,7 @@ class NestController extends Controller
*/
public function create(): View
{
return view('admin.nests.new');
return $this->view->make('admin.nests.new');
}
/**
@ -56,7 +56,7 @@ class NestController extends Controller
public function store(StoreNestFormRequest $request): RedirectResponse
{
$nest = $this->nestCreationService->handle($request->normalize());
$this->alert->success(trans('admin/nests.notices.created', ['name' => htmlspecialchars($nest->name)]))->flash();
$this->alert->success(trans('admin/nests.notices.created', ['name' => $nest->name]))->flash();
return redirect()->route('admin.nests.view', $nest->id);
}
@ -68,7 +68,7 @@ class NestController extends Controller
*/
public function view(int $nest): View
{
return view('admin.nests.view', [
return $this->view->make('admin.nests.view', [
'nest' => $this->repository->getWithEggServers($nest),
]);
}

View File

@ -19,7 +19,7 @@ class NodeAutoDeployController extends Controller
public function __construct(
private ApiKeyRepository $repository,
private Encrypter $encrypter,
private KeyCreationService $keyCreationService,
private KeyCreationService $keyCreationService
) {
}
@ -31,7 +31,7 @@ class NodeAutoDeployController extends Controller
*/
public function __invoke(Request $request, Node $node): JsonResponse
{
/** @var ApiKey|null $key */
/** @var \Pterodactyl\Models\ApiKey|null $key */
$key = $this->repository->getApplicationKeys($request->user())
->filter(function (ApiKey $key) {
foreach ($key->getAttributes() as $permission => $value) {

View File

@ -7,9 +7,17 @@ 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.
*/
@ -22,6 +30,6 @@ class NodeController extends Controller
->allowedSorts(['id'])
->paginate(25);
return view('admin.nodes.index', ['nodes' => $nodes]);
return $this->view->make('admin.nodes.index', ['nodes' => $nodes]);
}
}

View File

@ -8,11 +8,13 @@ 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
{
@ -22,10 +24,12 @@ 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
) {
}
@ -36,7 +40,7 @@ class NodeViewController extends Controller
{
$node = $this->repository->loadLocationAndServerCount($node);
return view('admin.nodes.view.index', [
return $this->view->make('admin.nodes.view.index', [
'node' => $node,
'stats' => $this->repository->getUsageStats($node),
'version' => $this->versionService,
@ -48,7 +52,7 @@ class NodeViewController extends Controller
*/
public function settings(Request $request, Node $node): View
{
return view('admin.nodes.view.settings', [
return $this->view->make('admin.nodes.view.settings', [
'node' => $node,
'locations' => $this->locationRepository->all(),
]);
@ -59,7 +63,7 @@ class NodeViewController extends Controller
*/
public function configuration(Request $request, Node $node): View
{
return view('admin.nodes.view.configuration', compact('node'));
return $this->view->make('admin.nodes.view.configuration', compact('node'));
}
/**
@ -69,9 +73,9 @@ class NodeViewController extends Controller
{
$node = $this->repository->loadNodeAllocations($node);
$this->plainInject(['node' => Collection::make([$node])->only(['id'])]);
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
return view('admin.nodes.view.allocation', [
return $this->view->make('admin.nodes.view.allocation', [
'node' => $node,
'allocations' => Allocation::query()->where('node_id', $node->id)
->groupBy('ip')
@ -86,11 +90,11 @@ class NodeViewController extends Controller
public function servers(Request $request, Node $node): View
{
$this->plainInject([
'node' => Collection::make([$node->makeVisible(['daemon_token_id', 'daemon_token'])])
'node' => Collection::wrap($node->makeVisible(['daemon_token_id', 'daemon_token']))
->only(['scheme', 'fqdn', 'daemonListen', 'daemon_token_id', 'daemon_token']),
]);
return view('admin.nodes.view.servers', [
return $this->view->make('admin.nodes.view.servers', [
'node' => $node,
'servers' => $this->serverRepository->loadAllServersForNode($node->id, 25),
]);

View File

@ -44,7 +44,7 @@ class NodesController extends Controller
protected ServerRepositoryInterface $serverRepository,
protected NodeUpdateService $updateService,
protected SoftwareVersionService $versionService,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -60,7 +60,7 @@ class NodesController extends Controller
return redirect()->route('admin.locations');
}
return view('admin.nodes.new', ['locations' => $locations]);
return $this->view->make('admin.nodes.new', ['locations' => $locations]);
}
/**
@ -131,7 +131,7 @@ class NodesController extends Controller
['ip', '=', $request->input('ip')],
]);
$this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => htmlspecialchars($request->input('ip'))]))
$this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => $request->input('ip')]))
->flash();
return redirect()->route('admin.nodes.view.allocation', $node);

View File

@ -3,11 +3,11 @@
namespace Pterodactyl\Http\Controllers\Admin\Servers;
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,6 +24,7 @@ class CreateServerController extends Controller
private NestRepository $nestRepository,
private NodeRepository $nodeRepository,
private ServerCreationService $creationService,
private ViewFactory $view
) {
}
@ -45,14 +46,14 @@ class CreateServerController extends Controller
\JavaScript::put([
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
'nests' => $nests->map(function (Nest $item) {
'nests' => $nests->map(function ($item) {
return array_merge($item->toArray(), [
'eggs' => $item->eggs->keyBy('id')->toArray(),
]);
})->keyBy('id'),
]);
return view('admin.servers.new', [
return $this->view->make('admin.servers.new', [
'locations' => Location::all(),
'nests' => $nests,
]);

View File

@ -9,9 +9,17 @@ 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.
@ -25,6 +33,6 @@ class ServerController extends Controller
])
->paginate(config()->get('pterodactyl.paginate.admin.servers'));
return view('admin.servers.index', ['servers' => $servers]);
return $this->view->make('admin.servers.index', ['servers' => $servers]);
}
}

View File

@ -26,7 +26,7 @@ class ServerTransferController extends Controller
private ConnectionInterface $connection,
private DaemonTransferRepository $daemonTransferRepository,
private NodeJWTService $nodeJWTService,
private NodeRepository $nodeRepository,
private NodeRepository $nodeRepository
) {
}
@ -66,7 +66,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')->values()->toArray();
$transfer->old_additional_allocations = $server->allocations->where('id', '!=', $server->allocation_id)->pluck('id');
$transfer->new_additional_allocations = $additional_allocations;
$transfer->save();

View File

@ -9,9 +9,11 @@ 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;
@ -29,7 +31,9 @@ class ServerViewController extends Controller
private MountRepository $mountRepository,
private NestRepository $nestRepository,
private NodeRepository $nodeRepository,
private ServerRepository $repository,
private EnvironmentService $environmentService,
private ViewFactory $view
) {
}
@ -38,7 +42,7 @@ class ServerViewController extends Controller
*/
public function index(Request $request, Server $server): View
{
return view('admin.servers.view.index', compact('server'));
return $this->view->make('admin.servers.view.index', compact('server'));
}
/**
@ -46,7 +50,7 @@ class ServerViewController extends Controller
*/
public function details(Request $request, Server $server): View
{
return view('admin.servers.view.details', compact('server'));
return $this->view->make('admin.servers.view.details', compact('server'));
}
/**
@ -56,7 +60,7 @@ class ServerViewController extends Controller
{
$allocations = $server->node->allocations->toBase();
return view('admin.servers.view.build', [
return $this->view->make('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'),
@ -83,7 +87,7 @@ class ServerViewController extends Controller
})->keyBy('id'),
]);
return view('admin.servers.view.startup', compact('server', 'nests'));
return $this->view->make('admin.servers.view.startup', compact('server', 'nests'));
}
/**
@ -91,7 +95,7 @@ class ServerViewController extends Controller
*/
public function database(Request $request, Server $server): View
{
return view('admin.servers.view.database', [
return $this->view->make('admin.servers.view.database', [
'hosts' => $this->databaseHostRepository->all(),
'server' => $server,
]);
@ -104,7 +108,7 @@ class ServerViewController extends Controller
{
$server->load('mounts');
return view('admin.servers.view.mounts', [
return $this->view->make('admin.servers.view.mounts', [
'mounts' => $this->mountRepository->getMountListForServer($server),
'server' => $server,
]);
@ -114,7 +118,7 @@ class ServerViewController extends Controller
* Returns the base server management page, or an exception if the server
* is in a state that cannot be recovered from.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function manage(Request $request, Server $server): View
{
@ -133,7 +137,7 @@ class ServerViewController extends Controller
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
]);
return view('admin.servers.view.manage', [
return $this->view->make('admin.servers.view.manage', [
'server' => $server,
'locations' => $this->locationRepository->all(),
'canTransfer' => $canTransfer,
@ -145,6 +149,6 @@ class ServerViewController extends Controller
*/
public function delete(Request $request, Server $server): View
{
return view('admin.servers.view.delete', compact('server'));
return $this->view->make('admin.servers.view.delete', compact('server'));
}
}

View File

@ -57,14 +57,14 @@ class ServersController extends Controller
protected NestRepositoryInterface $nestRepository,
protected ServerConfigurationStructureService $serverConfigurationStructureService,
protected StartupModificationService $startupModificationService,
protected SuspensionService $suspensionService,
protected SuspensionService $suspensionService
) {
}
/**
* Update the details for a server.
*
* @throws DataValidationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function setDetails(Request $request, Server $server): RedirectResponse
@ -81,8 +81,8 @@ class ServersController extends Controller
/**
* Toggles the installation status for a server.
*
* @throws DisplayException
* @throws DataValidationException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function toggleInstall(Server $server): RedirectResponse
@ -103,8 +103,8 @@ class ServersController extends Controller
/**
* Reinstalls the server with the currently assigned service.
*
* @throws DisplayException
* @throws DataValidationException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function reinstallServer(Server $server): RedirectResponse
@ -118,8 +118,8 @@ class ServersController extends Controller
/**
* Manage the suspension status for a server.
*
* @throws DisplayException
* @throws DataValidationException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function manageSuspension(Request $request, Server $server): RedirectResponse
@ -135,9 +135,9 @@ class ServersController extends Controller
/**
* Update the build configuration for a server.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws ValidationException
* @throws \Illuminate\Validation\ValidationException
*/
public function updateBuild(Request $request, Server $server): RedirectResponse
{
@ -159,7 +159,7 @@ class ServersController extends Controller
/**
* Start the server deletion process.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Throwable
*/
public function delete(Request $request, Server $server): RedirectResponse
@ -173,7 +173,7 @@ class ServersController extends Controller
/**
* Update the startup command as well as variables.
*
* @throws ValidationException
* @throws \Illuminate\Validation\ValidationException
*/
public function saveStartup(Request $request, Server $server): RedirectResponse
{
@ -220,7 +220,7 @@ class ServersController extends Controller
*/
public function resetDatabasePassword(Request $request, Server $server): Response
{
/** @var Database $database */
/** @var \Pterodactyl\Models\Database $database */
$database = $server->databases()->findOrFail($request->input('database'));
$this->databasePasswordService->handle($database);

View File

@ -6,6 +6,7 @@ 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,6 +22,7 @@ class AdvancedController extends Controller
private ConfigRepository $config,
private Kernel $kernel,
private SettingsRepositoryInterface $settings,
private ViewFactory $view
) {
}
@ -37,7 +39,7 @@ class AdvancedController extends Controller
$showRecaptchaWarning = true;
}
return view('admin.settings.advanced', [
return $this->view->make('admin.settings.advanced', [
'showRecaptchaWarning' => $showRecaptchaWarning,
]);
}

View File

@ -6,6 +6,7 @@ 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,6 +25,7 @@ class IndexController extends Controller
private Kernel $kernel,
private SettingsRepositoryInterface $settings,
private SoftwareVersionService $versionService,
private ViewFactory $view
) {
}
@ -32,7 +34,7 @@ class IndexController extends Controller
*/
public function index(): View
{
return view('admin.settings.index', [
return $this->view->make('admin.settings.index', [
'version' => $this->versionService,
'languages' => $this->getAvailableLanguages(true),
]);

View File

@ -7,6 +7,7 @@ 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;
@ -26,6 +27,7 @@ class MailController extends Controller
private Encrypter $encrypter,
private Kernel $kernel,
private SettingsRepositoryInterface $settings,
private ViewFactory $view
) {
}
@ -35,7 +37,7 @@ class MailController extends Controller
*/
public function index(): View
{
return view('admin.settings.mail', [
return $this->view->make('admin.settings.mail', [
'disabled' => $this->config->get('mail.default') !== 'smtp',
]);
}

View File

@ -36,7 +36,7 @@ class UserController extends Controller
protected Translator $translator,
protected UserUpdateService $updateService,
protected UserRepositoryInterface $repository,
protected ViewFactory $view,
protected ViewFactory $view
) {
}
@ -54,11 +54,10 @@ class UserController extends Controller
->groupBy('users.id')
)
->allowedFilters(['username', 'email', 'uuid'])
->defaultSort('-root_admin')
->allowedSorts(['id', 'uuid'])
->paginate(50);
return view('admin.users.index', ['users' => $users]);
return $this->view->make('admin.users.index', ['users' => $users]);
}
/**
@ -66,7 +65,7 @@ class UserController extends Controller
*/
public function create(): View
{
return view('admin.users.new', [
return $this->view->make('admin.users.new', [
'languages' => $this->getAvailableLanguages(true),
]);
}
@ -76,7 +75,7 @@ class UserController extends Controller
*/
public function view(User $user): View
{
return view('admin.users.view', [
return $this->view->make('admin.users.view', [
'user' => $user,
'languages' => $this->getAvailableLanguages(true),
]);
@ -86,12 +85,12 @@ class UserController extends Controller
* Delete a user from the system.
*
* @throws \Exception
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function delete(Request $request, User $user): RedirectResponse
{
if ($request->user()->is($user)) {
throw new DisplayException(__('admin/user.exceptions.delete_self'));
if ($request->user()->id === $user->id) {
throw new DisplayException($this->translator->get('admin/user.exceptions.user_has_servers'));
}
$this->deletionService->handle($user);
@ -140,14 +139,12 @@ class UserController extends Controller
// Handle single user requests.
if ($request->query('user_id')) {
$user = User::query()->findOrFail($request->input('user_id'));
// @phpstan-ignore-next-line property.notFound
$user->md5 = md5(strtolower($user->email));
return $user;
}
return $users->map(function ($item) {
// @phpstan-ignore-next-line property.notFound
$item->md5 = md5(strtolower($item->email));
return $item;

View File

@ -59,7 +59,7 @@ abstract class ApplicationApiController extends Controller
*/
public function getTransformer(string $abstract)
{
Assert::subclassOf($abstract, BaseTransformer::class); // @phpstan-ignore staticMethod.alreadyNarrowedType
Assert::subclassOf($abstract, BaseTransformer::class);
return $abstract::fromRequest($this->request);
}

View File

@ -25,7 +25,7 @@ class LocationController extends ApplicationApiController
public function __construct(
private LocationCreationService $creationService,
private LocationDeletionService $deletionService,
private LocationUpdateService $updateService,
private LocationUpdateService $updateService
) {
parent::__construct();
}

View File

@ -23,7 +23,7 @@ class AllocationController extends ApplicationApiController
*/
public function __construct(
private AssignmentService $assignmentService,
private AllocationDeletionService $deletionService,
private AllocationDeletionService $deletionService
) {
parent::__construct();
}

View File

@ -24,7 +24,7 @@ class NodeController extends ApplicationApiController
public function __construct(
private NodeCreationService $creationService,
private NodeDeletionService $deletionService,
private NodeUpdateService $updateService,
private NodeUpdateService $updateService
) {
parent::__construct();
}

View File

@ -22,7 +22,7 @@ class DatabaseController extends ApplicationApiController
*/
public function __construct(
private DatabaseManagementService $databaseManagementService,
private DatabasePasswordService $databasePasswordService,
private DatabasePasswordService $databasePasswordService
) {
parent::__construct();
}

View File

@ -22,7 +22,7 @@ class ServerController extends ApplicationApiController
*/
public function __construct(
private ServerCreationService $creationService,
private ServerDeletionService $deletionService,
private ServerDeletionService $deletionService
) {
parent::__construct();
}

View File

@ -17,7 +17,7 @@ class ServerDetailsController extends ApplicationApiController
*/
public function __construct(
private BuildModificationService $buildModificationService,
private DetailsModificationService $detailsModificationService,
private DetailsModificationService $detailsModificationService
) {
parent::__construct();
}

View File

@ -16,7 +16,7 @@ class ServerManagementController extends ApplicationApiController
*/
public function __construct(
private ReinstallServerService $reinstallServerService,
private SuspensionService $suspensionService,
private SuspensionService $suspensionService
) {
parent::__construct();
}

View File

@ -23,7 +23,7 @@ class UserController extends ApplicationApiController
public function __construct(
private UserCreationService $creationService,
private UserDeletionService $deletionService,
private UserUpdateService $updateService,
private UserUpdateService $updateService
) {
parent::__construct();
}

View File

@ -64,7 +64,7 @@ class AccountController extends ClientApiController
$guard->setUser($user);
// This method doesn't exist in the stateless Sanctum world.
if (method_exists($guard, 'logoutOtherDevices')) { // @phpstan-ignore function.alreadyNarrowedType
if (method_exists($guard, 'logoutOtherDevices')) {
$guard->logoutOtherDevices($request->input('password'));
}

View File

@ -25,7 +25,7 @@ class ApiKeyController extends ClientApiController
/**
* Store a new API key for a user's account.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function store(StoreApiKeyRequest $request): array
{
@ -54,7 +54,7 @@ class ApiKeyController extends ClientApiController
*/
public function delete(ClientApiRequest $request, string $identifier): JsonResponse
{
/** @var ApiKey $key */
/** @var \Pterodactyl\Models\ApiKey $key */
$key = $request->user()->apiKeys()
->where('key_type', ApiKey::TYPE_ACCOUNT)
->where('identifier', $identifier)

View File

@ -49,7 +49,7 @@ abstract class ClientApiController extends ApplicationApiController
*/
public function getTransformer(string $abstract)
{
Assert::subclassOf($abstract, BaseClientTransformer::class); // @phpstan-ignore staticMethod.alreadyNarrowedType
Assert::subclassOf($abstract, BaseClientTransformer::class);
return $abstract::fromRequest($this->request);
}

View File

@ -30,7 +30,7 @@ class BackupController extends ClientApiController
private DeleteBackupService $deleteBackupService,
private InitiateBackupService $initiateBackupService,
private DownloadLinkService $downloadLinkService,
private BackupRepository $repository,
private BackupRepository $repository
) {
parent::__construct();
}
@ -39,7 +39,7 @@ class BackupController extends ClientApiController
* Returns all the backups for a given server instance in a paginated
* result set.
*
* @throws AuthorizationException
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request, Server $server): array
{
@ -74,21 +74,15 @@ class BackupController extends ClientApiController
// how best to allow a user to create a backup that is locked without also preventing
// them from just filling up a server with backups that can never be deleted?
if ($request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) {
$action->setIsLocked($request->boolean('is_locked'));
$action->setIsLocked((bool) $request->input('is_locked'));
}
$backup = Activity::event('server:backup.start')->transaction(function ($log) use ($action, $server, $request) {
$server->backups()->lockForUpdate();
$backup = $action->handle($server, $request->input('name'));
$backup = $action->handle($server, $request->input('name'));
$log->subject($backup)->property([
'name' => $backup->name,
'locked' => $request->boolean('is_locked'),
]);
return $backup;
});
Activity::event('server:backup.start')
->subject($backup)
->property(['name' => $backup->name, 'locked' => (bool) $request->input('is_locked')])
->log();
return $this->fractal->item($backup)
->transformWith($this->getTransformer(BackupTransformer::class))
@ -99,7 +93,7 @@ class BackupController extends ClientApiController
* Toggles the lock status of a given backup for a server.
*
* @throws \Throwable
* @throws AuthorizationException
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function toggleLock(Request $request, Server $server, Backup $backup): array
{
@ -121,7 +115,7 @@ class BackupController extends ClientApiController
/**
* Returns information about a single backup.
*
* @throws AuthorizationException
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function view(Request $request, Server $server, Backup $backup): array
{
@ -162,7 +156,7 @@ class BackupController extends ClientApiController
* which the user is redirected to.
*
* @throws \Throwable
* @throws AuthorizationException
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function download(Request $request, Server $server, Backup $backup): JsonResponse
{

View File

@ -5,6 +5,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Illuminate\Http\Response;
use Pterodactyl\Models\Server;
use Pterodactyl\Facades\Activity;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Exception\BadResponseException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Pterodactyl\Repositories\Wings\DaemonCommandRepository;
@ -25,7 +26,7 @@ class CommandController extends ClientApiController
/**
* Send a command to a running server.
*
* @throws DaemonConnectionException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function index(SendCommandRequest $request, Server $server): Response
{
@ -35,7 +36,10 @@ class CommandController extends ClientApiController
$previous = $exception->getPrevious();
if ($previous instanceof BadResponseException) {
if ($previous->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY) {
if (
$previous->getResponse() instanceof ResponseInterface
&& $previous->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY
) {
throw new HttpException(Response::HTTP_BAD_GATEWAY, 'Server must be online in order to send commands.', $exception);
}
}

View File

@ -24,7 +24,7 @@ class DatabaseController extends ClientApiController
public function __construct(
private DeployServerDatabaseService $deployDatabaseService,
private DatabaseManagementService $managementService,
private DatabasePasswordService $passwordService,
private DatabasePasswordService $passwordService
) {
parent::__construct();
}
@ -48,15 +48,12 @@ class DatabaseController extends ClientApiController
*/
public function store(StoreDatabaseRequest $request, Server $server): array
{
$database = Activity::event('server:database.create')->transaction(function ($log) use ($request, $server) {
$server->databases()->lockForUpdate();
$database = $this->deployDatabaseService->handle($server, $request->validated());
$database = $this->deployDatabaseService->handle($server, $request->validated());
$log->subject($database)->property('name', $database->database);
return $database;
});
Activity::event('server:database.create')
->subject($database)
->property('name', $database->database)
->log();
return $this->fractal->item($database)
->parseIncludes(['password'])
@ -72,16 +69,15 @@ class DatabaseController extends ClientApiController
*/
public function rotatePassword(RotatePasswordRequest $request, Server $server, Database $database): array
{
$this->passwordService->handle($database);
$database->refresh();
Activity::event('server:database.rotate-password')
->subject($database)
->property('name', $database->database)
->transaction(function () use ($database) {
$database->lockForUpdate();
->log();
$this->passwordService->handle($database);
});
return $this->fractal->item($database->refresh())
return $this->fractal->item($database)
->parseIncludes(['password'])
->transformWith($this->getTransformer(DatabaseTransformer::class))
->toArray();

View File

@ -30,7 +30,7 @@ class FileController extends ClientApiController
*/
public function __construct(
private NodeJWTService $jwtService,
private DaemonFileRepository $fileRepository,
private DaemonFileRepository $fileRepository
) {
parent::__construct();
}

View File

@ -16,7 +16,7 @@ class FileUploadController extends ClientApiController
* FileUploadController constructor.
*/
public function __construct(
private NodeJWTService $jwtService,
private NodeJWTService $jwtService
) {
parent::__construct();
}

View File

@ -6,7 +6,6 @@ use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Pterodactyl\Models\Allocation;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Transformers\Api\Client\AllocationTransformer;
@ -24,9 +23,8 @@ class NetworkAllocationController extends ClientApiController
* NetworkAllocationController constructor.
*/
public function __construct(
protected readonly ConnectionInterface $connection,
private FindAssignableAllocationService $assignableAllocationService,
private ServerRepository $serverRepository,
private ServerRepository $serverRepository
) {
parent::__construct();
}
@ -90,21 +88,20 @@ class NetworkAllocationController extends ClientApiController
* Set the notes for the allocation for a server.
*s.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function store(NewAllocationRequest $request, Server $server): array
{
$allocation = Activity::event('server:allocation.create')->transaction(function ($log) use ($server) {
if ($server->allocations()->lockForUpdate()->count() >= $server->allocation_limit) {
throw new DisplayException('Cannot assign additional allocations to this server: limit has been reached.');
}
if ($server->allocations()->count() >= $server->allocation_limit) {
throw new DisplayException('Cannot assign additional allocations to this server: limit has been reached.');
}
$allocation = $this->assignableAllocationService->handle($server);
$allocation = $this->assignableAllocationService->handle($server);
$log->subject($allocation)->property('allocation', $allocation->toString());
return $allocation;
});
Activity::event('server:allocation.create')
->subject($allocation)
->property('allocation', $allocation->toString())
->log();
return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class))
@ -114,7 +111,7 @@ class NetworkAllocationController extends ClientApiController
/**
* Delete an allocation from a server.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function delete(DeleteAllocationRequest $request, Server $server, Allocation $allocation): JsonResponse
{

View File

@ -47,12 +47,12 @@ class ScheduleController extends ClientApiController
/**
* Store a new schedule for a server.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(StoreScheduleRequest $request, Server $server): array
{
/** @var Schedule $model */
/** @var \Pterodactyl\Models\Schedule $model */
$model = $this->repository->create([
'server_id' => $server->id,
'name' => $request->input('name'),
@ -95,7 +95,7 @@ class ScheduleController extends ClientApiController
/**
* Updates a given schedule with the new data provided.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
@ -165,7 +165,7 @@ class ScheduleController extends ClientApiController
/**
* Get the next run timestamp based on the cron data provided.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
protected function getNextRunAt(Request $request): Carbon
{

View File

@ -26,7 +26,7 @@ class ScheduleTaskController extends ClientApiController
*/
public function __construct(
private ConnectionInterface $connection,
private TaskRepository $repository,
private TaskRepository $repository
) {
parent::__construct();
}
@ -35,7 +35,7 @@ class ScheduleTaskController extends ClientApiController
* Create a new task for a given schedule and store it in the database.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws ServiceLimitExceededException
* @throws \Pterodactyl\Exceptions\Service\ServiceLimitExceededException
*/
public function store(StoreTaskRequest $request, Server $server, Schedule $schedule): array
{
@ -48,10 +48,10 @@ class ScheduleTaskController extends ClientApiController
throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0.");
}
/** @var Task|null $lastTask */
/** @var \Pterodactyl\Models\Task|null $lastTask */
$lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first();
/** @var Task $task */
/** @var \Pterodactyl\Models\Task $task */
$task = $this->connection->transaction(function () use ($request, $schedule, $lastTask) {
$sequenceId = ($lastTask->sequence_id ?? 0) + 1;
$requestSequenceId = $request->integer('sequence_id', $sequenceId);

View File

@ -21,7 +21,7 @@ class SettingsController extends ClientApiController
*/
public function __construct(
private ServerRepository $repository,
private ReinstallServerService $reinstallServerService,
private ReinstallServerService $reinstallServerService
) {
parent::__construct();
}

View File

@ -19,7 +19,7 @@ class StartupController extends ClientApiController
*/
public function __construct(
private StartupCommandService $startupCommandService,
private ServerVariableRepository $repository,
private ServerVariableRepository $repository
) {
parent::__construct();
}
@ -52,7 +52,9 @@ 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;
if (is_null($variable) || !$variable->user_viewable) {
throw new BadRequestHttpException('The environment variable you are trying to edit does not exist.');
@ -60,8 +62,6 @@ class StartupController extends ClientApiController
throw new BadRequestHttpException('The environment variable you are trying to edit is read-only.');
}
$original = $variable->server_value;
// Revalidate the variable value using the egg variable specific validation rules for it.
$this->validate($request, ['value' => $variable->rules]);

View File

@ -10,8 +10,8 @@ use Pterodactyl\Models\Permission;
use Illuminate\Support\Facades\Log;
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
use Pterodactyl\Services\Subusers\SubuserCreationService;
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
use Pterodactyl\Transformers\Api\Client\SubuserTransformer;
use Pterodactyl\Repositories\Wings\DaemonRevocationRepository;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest;
@ -27,7 +27,7 @@ class SubuserController extends ClientApiController
public function __construct(
private SubuserRepository $repository,
private SubuserCreationService $creationService,
private DaemonRevocationRepository $revocationRepository,
private DaemonServerRepository $serverRepository
) {
parent::__construct();
}
@ -115,10 +115,7 @@ class SubuserController extends ClientApiController
]);
try {
$this->revocationRepository->setNode($server->node)->deauthorize(
$subuser->user->uuid,
[$server->uuid],
);
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
} catch (DaemonConnectionException $exception) {
// Don't block this request if we can't connect to the Wings instance. Chances are it is
// offline and the token will be invalid once Wings boots back.
@ -153,10 +150,7 @@ class SubuserController extends ClientApiController
$subuser->delete();
try {
$this->revocationRepository->setNode($server->node)->deauthorize(
$subuser->user->uuid,
[$server->uuid],
);
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
} catch (DaemonConnectionException $exception) {
// Don't block this request if we can't connect to the Wings instance.
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);

View File

@ -19,7 +19,7 @@ class WebsocketController extends ClientApiController
*/
public function __construct(
private NodeJWTService $jwtService,
private GetUserPermissionsService $permissionsService,
private GetUserPermissionsService $permissionsService
) {
parent::__construct();
}

View File

@ -20,7 +20,7 @@ class TwoFactorController extends ClientApiController
public function __construct(
private ToggleTwoFactorService $toggleTwoFactorService,
private TwoFactorSetupService $setupService,
private ValidationFactory $validation,
private ValidationFactory $validation
) {
parent::__construct();
}

View File

@ -27,7 +27,7 @@ class ActivityProcessingController extends Controller
$logs = [];
foreach ($request->input('data') as $datum) {
/** @var Server|null $server */
/** @var \Pterodactyl\Models\Server|null $server */
$server = $servers->get($datum['server']);
if (is_null($server) || !Str::startsWith($datum['event'], 'server:')) {
continue;

View File

@ -9,7 +9,6 @@ use Illuminate\Http\JsonResponse;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Extensions\Backups\BackupManager;
use Pterodactyl\Extensions\Filesystem\S3Filesystem;
use Pterodactyl\Exceptions\Http\HttpForbiddenException;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
@ -33,28 +32,18 @@ class BackupRemoteUploadController extends Controller
*/
public function __invoke(Request $request, string $backup): JsonResponse
{
// Get the node associated with the request.
/** @var \Pterodactyl\Models\Node $node */
$node = $request->attributes->get('node');
// Get the size query parameter.
$size = (int) $request->query('size');
if (empty($size)) {
throw new BadRequestHttpException('A non-empty "size" query parameter must be provided.');
}
$model = Backup::query()->where('uuid', $backup)->firstOrFail();
// Check that the backup is "owned" by the node making the request. This avoids other nodes
// from messing with backups that they don't own.
$server = $model->server;
if ($server->node_id !== $node->id) {
throw new HttpForbiddenException('You do not have permission to access that backup.');
}
/** @var \Pterodactyl\Models\Backup $backup */
$backup = Backup::query()->where('uuid', $backup)->firstOrFail();
// Prevent backups that have already been completed from trying to
// be uploaded again.
if (!is_null($model->completed_at)) {
if (!is_null($backup->completed_at)) {
throw new ConflictHttpException('This backup is already in a completed state.');
}
@ -65,7 +54,7 @@ class BackupRemoteUploadController extends Controller
}
// The path where backup will be uploaded to
$path = sprintf('%s/%s.tar.gz', $model->server->uuid, $model->uuid);
$path = sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid);
// Get the S3 client
$client = $adapter->getClient();
@ -103,7 +92,7 @@ class BackupRemoteUploadController extends Controller
}
// Set the upload_id on the backup in the database.
$model->update(['upload_id' => $params['UploadId']]);
$backup->update(['upload_id' => $params['UploadId']]);
return new JsonResponse([
'parts' => $parts,

View File

@ -11,7 +11,6 @@ use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Extensions\Backups\BackupManager;
use Pterodactyl\Extensions\Filesystem\S3Filesystem;
use Pterodactyl\Exceptions\Http\HttpForbiddenException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Pterodactyl\Http\Requests\Api\Remote\ReportBackupCompleteRequest;
@ -31,22 +30,8 @@ class BackupStatusController extends Controller
*/
public function index(ReportBackupCompleteRequest $request, string $backup): JsonResponse
{
// Get the node associated with the request.
/** @var \Pterodactyl\Models\Node $node */
$node = $request->attributes->get('node');
/** @var Backup $model */
$model = Backup::query()
->where('uuid', $backup)
->firstOrFail();
// Check that the backup is "owned" by the node making the request. This avoids other nodes
// from messing with backups that they don't own.
/** @var \Pterodactyl\Models\Server $server */
$server = $model->server;
if ($server->node_id !== $node->id) {
throw new HttpForbiddenException('You do not have permission to access that backup.');
}
/** @var \Pterodactyl\Models\Backup $model */
$model = Backup::query()->where('uuid', $backup)->firstOrFail();
if ($model->is_successful) {
throw new BadRequestHttpException('Cannot update the status of a backup that is already marked as completed.');
@ -92,7 +77,7 @@ class BackupStatusController extends Controller
*/
public function restore(Request $request, string $backup): JsonResponse
{
/** @var Backup $model */
/** @var \Pterodactyl\Models\Backup $model */
$model = Backup::query()->where('uuid', $backup)->firstOrFail();
$model->server->update(['status' => null]);
@ -110,7 +95,7 @@ class BackupStatusController extends Controller
* the given backup.
*
* @throws \Exception
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
protected function completeMultipartUpload(Backup $backup, S3Filesystem $adapter, bool $successful, ?array $parts): void
{

View File

@ -22,7 +22,7 @@ class ServerDetailsController extends Controller
protected ConnectionInterface $connection,
private ServerRepository $repository,
private ServerConfigurationStructureService $configurationStructureService,
private EggConfigurationService $eggConfigurationService,
private EggConfigurationService $eggConfigurationService
) {
}
@ -90,7 +90,7 @@ class ServerDetailsController extends Controller
->get();
$this->connection->transaction(function () use ($node, $servers) {
/** @var Server $server */
/** @var \Pterodactyl\Models\Server $server */
foreach ($servers as $server) {
/** @var \Pterodactyl\Models\ActivityLog|null $activity */
$activity = $server->activity->first();
@ -100,7 +100,7 @@ class ServerDetailsController extends Controller
// so that power actions, file management, and backups can resume as normal.
Activity::event('server:backup.restore-failed')
->subject($server, $subject->subject)
->property('name', $subject->subject->name) // @phpstan-ignore property.notFound
->property('name', $subject->subject->name)
->log();
}
}

View File

@ -22,7 +22,7 @@ class ServerTransferController extends Controller
public function __construct(
private ConnectionInterface $connection,
private ServerRepository $repository,
private DaemonServerRepository $daemonServerRepository,
private DaemonServerRepository $daemonServerRepository
) {
}

View File

@ -160,6 +160,6 @@ class SftpAuthenticationController extends Controller
{
$username = explode('.', strrev($request->input('username', '')));
return strtolower(strrev($username[0] ?? '') . '|' . $request->ip()); // @phpstan-ignore nullCoalesce.offset
return strtolower(strrev($username[0] ?? '') . '|' . $request->ip());
}
}

View File

@ -49,11 +49,9 @@ abstract class AbstractLoginController extends Controller
/**
* Get the failed login response instance.
*
* @return never-return
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
protected function sendFailedLoginResponse(Request $request, ?Authenticatable $user = null, ?string $message = null)
protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null, string $message = null)
{
$this->incrementLoginAttempts($request);
$this->fireFailedLoginEvent($user, [
@ -93,7 +91,7 @@ abstract class AbstractLoginController extends Controller
/**
* Determine if the user is logging in using an email or username.
*/
protected function getField(?string $input = null): string
protected function getField(string $input = null): string
{
return ($input && str_contains($input, '@')) ? 'email' : 'username';
}
@ -101,7 +99,7 @@ abstract class AbstractLoginController extends Controller
/**
* Fire a failed login event.
*/
protected function fireFailedLoginEvent(?Authenticatable $user = null, array $credentials = [])
protected function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = [])
{
Event::dispatch(new Failed('auth', $user, $credentials));
}

View File

@ -2,7 +2,6 @@
namespace Pterodactyl\Http\Controllers\Auth;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Pterodactyl\Models\User;
@ -25,7 +24,7 @@ class LoginCheckpointController extends AbstractLoginController
public function __construct(
private Encrypter $encrypter,
private Google2FA $google2FA,
private ValidationFactory $validation,
private ValidationFactory $validation
) {
parent::__construct();
}
@ -57,6 +56,7 @@ class LoginCheckpointController extends AbstractLoginController
}
try {
/** @var \Pterodactyl\Models\User $user */
$user = User::query()->findOrFail($details['user_id']);
} catch (ModelNotFoundException) {
$this->sendFailedLoginResponse($request, null, self::TOKEN_EXPIRED_MESSAGE);
@ -71,20 +71,8 @@ class LoginCheckpointController extends AbstractLoginController
}
} else {
$decrypted = $this->encrypter->decrypt($user->totp_secret);
$oldTimestamp = $user->totp_authenticated_at
? (int) floor($user->totp_authenticated_at->unix() / $this->google2FA->getKeyRegeneration())
: null;
$verified = $this->google2FA->verifyKeyNewer(
$decrypted,
$request->input('authentication_code') ?? '',
$oldTimestamp,
config('pterodactyl.auth.2fa.window') ?? 1,
);
if ($verified !== false) {
$user->update(['totp_authenticated_at' => Carbon::now()]);
if ($this->google2FA->verifyKey($decrypted, (string) $request->input('authentication_code') ?? '', config('pterodactyl.auth.2fa.window'))) {
Event::dispatch(new ProvidedAuthenticationToken($user));
return $this->sendLoginResponse($user, $request);
@ -118,9 +106,9 @@ class LoginCheckpointController extends AbstractLoginController
* will return false if the data is invalid, or if more time has passed than
* was configured when the session was written.
*/
protected function hasValidSessionData(?array $data): bool
protected function hasValidSessionData(array $data): bool
{
$validator = $this->validation->make($data ?? [], [
$validator = $this->validation->make($data, [
'user_id' => 'required|integer|min:1',
'token_value' => 'required|string',
'expires_at' => 'required',

View File

@ -9,10 +9,19 @@ 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
@ -20,7 +29,7 @@ class LoginController extends AbstractLoginController
*/
public function index(): View
{
return view('templates/auth.core');
return $this->view->make('templates/auth.core');
}
/**
@ -39,7 +48,7 @@ class LoginController extends AbstractLoginController
try {
$username = $request->input('user');
/** @var User $user */
/** @var \Pterodactyl\Models\User $user */
$user = User::query()->where($this->getField($username), $username)->firstOrFail();
} catch (ModelNotFoundException) {
$this->sendFailedLoginResponse($request);

Some files were not shown because too many files have changed in this diff Show More