Compare commits

..

74 Commits

Author SHA1 Message Date
Dane Everitt
d648a63e13
Update i18n to resolve issues rendering unclosed div tags (#5547) 2026-02-01 13:19:41 -08:00
Dawid Jaworski
a1f7d287ac
feat: Auth Required modal for hytale (#5526)
This PR add modal related to requiring auth to download or update hytale
server.

Egg feature to use: `hytale_oauth`

Preview:


https://github.com/user-attachments/assets/77bd4f16-ba5b-4652-88b1-7abfd4ab45b8
2026-01-12 12:22:49 -06:00
cesarmr-github
676b64562f
Remove <strong> tags in admin area notifications (#5520) 2026-01-10 16:39:10 -08:00
AndyIsHereBoi
232ccce061
Update node/view/servers title to be consistent (#5514) 2026-01-10 10:02:54 -08:00
Sam Schumacher
d4580076c2
Cast presigned URL lifespan to integer (#5515) 2026-01-10 10:01:57 -08:00
DaneEveritt
5f1bfd109d
Don't include v in the version identifier 2026-01-05 17:18:54 -08:00
Всеволод Мельник
09caa0d499
Merge commit from fork
* Add throttling to resource creation endpoints

* Fix middleware registration for the throttlers

* Lock the server's resource models when adding new ones

* Throttle subusers even more

---------

Co-authored-by: DaneEveritt <dane@daneeveritt.com>
2026-01-05 16:05:38 -08:00
DaneEveritt
82f22cd7ab
update release.yaml for testing 2026-01-04 17:28:44 -08:00
Gio
3dd206ccf7
Clear allocation notes on server deletion (#5157) 2026-01-04 16:44:46 -08:00
Dane Everitt
5e2e827d48
Update CHANGELOG.md (#5502) 2026-01-04 16:32:01 -08:00
Alan Escarcha
ace4c025e9
Update Docker container for Forge installation script (#5424) 2026-01-04 15:49:24 -08:00
Mackenzie Molloy
238d371352
Feature: Sort Users on Admin/Users by Administrators first (#5098) 2026-01-04 11:12:55 -08:00
Mackenzie Molloy
38b7b4bee8
Cast old() returned value to an Integer (#5163) 2026-01-04 11:12:23 -08:00
Dane Everitt
d2949eabd6
Cleanup issues template (#5490) 2026-01-04 11:08:17 -08:00
Anthony
14d666de65
Update contact email for reporting vulnerabilities (#5489) 2026-01-04 12:31:46 -06:00
Dane Everitt
032bf076d9
Ensure that TOTP tokens cannot be reused (#5481) 2025-12-30 12:27:11 -08:00
Dane Everitt
1570ff2509
Don't render raw HTML returned by the alert bag (#5475)
ref: https://github.com/pterodactyl/panel/security/advisories/GHSA-mgr9-6c2j-jxrq
2025-12-26 18:27:23 -08:00
Dane Everitt
2bd9d8badd
Disconnect SFTP/Websocket when a user is removed as a subuser (#5472) 2025-12-26 17:51:25 -08:00
Dane Everitt
ca4e123c25
Fix missing evt parameter in websocket logic (#5470) 2025-12-26 13:08:06 -08:00
Dane Everitt
1fdaf008b4
don't endlessly poll websocket when disconnected due to suspension (#5464) 2025-12-26 09:51:54 -08:00
cesarmr-github
8c1d1b12a5
Fix admin server manage boxes misalignment when suspending a server (#5461) 2025-12-26 09:51:42 -08:00
Dane Everitt
e9558328dd
Use local certs for webpack serve (#5460) 2025-12-24 17:39:26 -08:00
Dane Everitt
bbb1294267
Support zero-byte file uploads (#5459) 2025-12-24 17:32:53 -08:00
Dane Everitt
a215f6d534
Don't be strict about upload size, so long as it is a positive integer (#5458) 2025-12-24 16:45:48 -08:00
Dane Everitt
895adb6e6f
Ensure that a node description can be set, add additional test coverage (#5457) 2025-12-24 16:43:00 -08:00
Dane Everitt
0917e60a3b
Return correct error message when deleting self, add test coverage (#5456) 2025-12-24 16:13:31 -08:00
Sergey Serpichenko
a7c1882edf
FIX: Renamed 'batch_uuid' field to 'batch' in ActivityLogService (#5396)
The field was renamed to match the column name in the database and to maintain consistency across the codebase.
2025-12-24 14:27:38 -08:00
Dane Everitt
4b97363d35
Update build workflows (#5449) 2025-12-20 16:43:23 -08:00
Dane Everitt
ab093344e7
Don't delete the initial content when using "Ctrl+Z" to undo (#5448)
Resolves https://github.com/pterodactyl/panel/issues/5263
2025-12-20 16:38:10 -08:00
Dane Everitt
f1ea7ec90d
Load IBM Plex Sans from code and export as part of bundle (#5447)
Removes the need to load any external resources for fonts. Resolves https://github.com/pterodactyl/panel/issues/5343
2025-12-20 16:37:54 -08:00
Anthony
8c62e90143
Fix sponsor link in README.md 2025-12-20 18:03:15 -06:00
Anthony
3a5e69ddd7
Update FUNDING.yml 2025-12-20 17:56:13 -06:00
Dane Everitt
a264791fd4
Update PHP and JS dependencies to latest versions and modernize codebase (#5446) 2025-12-20 15:55:13 -08:00
Fallen_Breath
9b703fb40f
fix: timezone issue in Schedule::getNextRunDate (#5381) 2025-11-08 23:32:06 -07:00
Matthew Penner
01fd763fe9
fix: add additional input validation
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-06-18 12:21:26 -06:00
Daniel Barton
370820a477
docker: switch to PHP 8.3 (#5173) 2025-06-13 11:07:46 -06:00
Matthew Penner
30af8e6220
ci: switch to ubuntu-24.04 runner
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-06-12 12:03:19 -06:00
Red Banana
043c02c69c
eggs: update Ark Survival Evolved (#5361) 2025-06-12 11:55:25 -06:00
Daniel Barton
79d99c4ed5
chore: update link to additional eggs in README (#5316) 2025-03-23 19:18:56 -06:00
Matthew Penner
81bccc4645
chore: allow overriding Laravel storage path using $APP_STORAGE_PATH
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-10 00:56:05 -06:00
Matthew Penner
0fa09a675a
nix: add yarn to devShell
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:10:08 -06:00
Matthew Penner
871ef0c564
fix: EnvironmentWriterTrait not allowing null values
Fixes https://github.com/pterodactyl/panel/issues/5108

Signed-off-by: Matthew Penner <me@matthewp.io>
2025-02-14 13:22:59 -07:00
Matthew Penner
bc07f8e1b1
nix: update flake.lock
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06)
  → 'github:hercules-ci/flake-parts/32ea77a06711b758da0ad9bd6a844c5740a87abd?narHash=sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm%2BzmZ7vxbJdo%3D' (2025-02-01)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/130595eba61081acde9001f43de3248d8888ac4a?narHash=sha256-Xb8mke6UCYjge9kPR9o4P1nVrhk7QBbKv3xQ9cj7h2s%3D' (2025-01-10)
  → 'github:NixOS/nixpkgs/2ff53fe64443980e139eaa286017f53f88336dd0?narHash=sha256-%2B/bYK3DbPxMIvSL4zArkMX0LQvS7rzBKXnDXLfKyRVc%3D' (2025-02-13)
2025-02-14 13:21:00 -07:00
Matthew Penner
d1dd5b1f02
fix: db schema dump not importing on older MariaDB versions
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-02-14 13:20:40 -07:00
Matthew Penner
9cac00cd55
nix: update flake.lock
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a?narHash=sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U%3D' (2024-09-12)
  → 'github:hercules-ci/flake-parts/b905f6fc23a9051a6e1b741e1438dbfc0634c6de?narHash=sha256-%2Bhu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU%3D' (2025-01-06)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/99dc8785f6a0adac95f5e2ab05cc2e1bf666d172?narHash=sha256-gI9kkaH0ZjakJOKrdjaI/VbaMEo9qBbSUl93DnU7f4c%3D' (2024-09-16)
  → 'github:NixOS/nixpkgs/130595eba61081acde9001f43de3248d8888ac4a?narHash=sha256-Xb8mke6UCYjge9kPR9o4P1nVrhk7QBbKv3xQ9cj7h2s%3D' (2025-01-10)
2025-01-12 14:56:50 -07:00
Matthew Penner
90ae588721
Update CHANGELOG.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-11-14 19:26:24 -07:00
Matthew Penner
955dd2796d
chore: update laravel 11.28.1 -> 11.31.0
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-11-14 19:03:51 -07:00
Matthew Penner
de3b1efb69
Update CHANGELOG.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-27 13:50:37 -06:00
Matthew Penner
706db62c64
ci: use GITHUB_TOKEN for authenticating against ghcr.io
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-24 12:04:30 -06:00
Matthew Penner
caab056256
fix: TwoFactorControllerTest, again
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-24 11:42:25 -06:00
Matthew Penner
abd36ad982
fix: TwoFactorControllerTest
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-24 11:36:19 -06:00
Matthew Penner
1d056c1652
Update CHANGELOG.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-24 11:32:43 -06:00
Matthew Penner
8be2b892c3
fix!: use POST instead of DELETE when disabling 2FA
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-24 11:27:42 -06:00
Matthew Penner
8ca098940a
chore: update composer dependencies (#5198)
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-21 19:18:20 -06:00
Matthew Penner
fbc24d27ee
Update README.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-21 19:11:17 -06:00
Matthew Penner
f9efaa86b1
chore: add laravel database schema dump
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-21 17:21:12 -06:00
Matthew Penner
acaa6c9ac4
chore: update SPONSORS.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-08-28 19:26:39 -06:00
Dawid Jaworski
2dcd39c9c7
fix: nullable egg features (#5135) 2024-07-05 13:22:47 -06:00
Dawid Jaworski
8ae76c3889
feat: add egg features editor in Admin UI (#5130) 2024-06-29 12:59:05 -06:00
Daniel Barton
953575ff5c
fix: remove special handling for ^C stop command (#5132) 2024-06-29 12:31:10 -06:00
Daniel Barton
2611cdf426
egg(teamspeak): add server admin password variable (#5099)
Co-authored-by: Panda260 <Panda260@NeverStopGaming.net>
2024-06-29 12:24:43 -06:00
Daniel Barton
844537df38
fix: CIDR_MAX_BITS restricting to /27 instead of /25 (#5111) 2024-06-29 12:24:10 -06:00
BeastGamer81
466dd61856
chore: fix "Manager User" typo in admin page title (#5114) 2024-06-29 12:22:11 -06:00
Ivy
512cfad6bf
chore: change "Github" to "GitHub" for consistency (#4946) 2024-06-29 12:20:35 -06:00
Matthew Penner
c86564042c
Update README.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-06-22 19:18:54 -06:00
Daniel Barton
371c7a671d
api: fix docker_image validation for local images (#5103) 2024-05-21 13:29:31 -06:00
Daniel Barton
37055fe5b4
ui(admin): fix maintenance mode defaulting to the wrong value (#4927) 2024-05-21 13:08:01 -06:00
Matthew Penner
f8dfef04c4
api(remote): fix oops in BackupStatusController, yet again 2024-05-08 18:08:18 -06:00
Matthew Penner
3a0b7d13a9
Update CHANGELOG.md 2024-05-07 22:13:13 -06:00
Matthew Penner
a96d87cf23
Update README.md 2024-05-07 22:12:15 -06:00
Daniel Barton
049ad838e3
egg(minecraft): add Java 21 to image list (#5093) 2024-05-07 21:52:33 -06:00
Daniel Barton
dbd7f31c41
Update Minecraft Eula link (#5090)
Co-authored-by: Alan Escarcha <alanescarcha11@gmail.com>
2024-05-06 18:50:06 -06:00
Daniel Barton
2ffe019675
ui(server): support for decompressing .7z files (#5016) 2024-05-04 17:16:00 -06:00
Matthew Penner
7bfc265a7e
api(remote): fix use of missing node_id field
Fixes #5088
2024-05-04 16:06:13 -06:00
570 changed files with 12508 additions and 9983 deletions

View File

@ -47,5 +47,6 @@ 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: [matthewpi]
github: [pterodactyl]

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

@ -0,0 +1,82 @@
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

18
.github/ISSUE_TEMPLATE/2-approved.yml vendored Normal file
View File

@ -0,0 +1,18 @@
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

View File

@ -1,87 +0,0 @@
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: true
blank_issues_enabled: false
contact_links:
- name: Installation Help
- 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
url: https://discord.gg/pterodactyl
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.
about: For quicker support with installations issues or running ideas by the community.

View File

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

View File

@ -1,24 +1,27 @@
name: Tests
on:
push:
branches:
- "develop"
- "1.0-develop"
pull_request:
branches:
- "develop"
- "1.0-develop"
jobs:
tests:
name: Tests
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
permissions:
contents: read
strategy:
fail-fast: false
matrix:
php: [8.1, 8.2]
database: ["mariadb:10.2", "mysql:8"]
php:
- 8.2
- 8.3
database:
- mariadb:10
- mariadb:11
- mysql:8
- mysql:9
services:
database:
image: ${{ matrix.database }}
@ -27,46 +30,30 @@ jobs:
MYSQL_DATABASE: testing
ports:
- 3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- name: Code Checkout
uses: actions/checkout@v3
- name: Get cache directory
id: composer-cache
- uses: actions/checkout@v4
- id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache
uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-${{ matrix.php }}-
- name: Setup PHP
uses: shivammathur/setup-php@v2
- 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
- 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() }}
- 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
env:
DB_HOST: UNIT_NO_DB
- name: Integration tests
run: vendor/bin/phpunit tests/Integration
- run: vendor/bin/phpunit tests/Integration
env:
DB_PORT: ${{ job.services.database.ports[3306] }}
DB_USERNAME: root

View File

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

View File

@ -1,36 +0,0 @@
name: Lint
on:
push:
branches:
- "develop"
- "1.0-develop"
pull_request:
branches:
- "develop"
- "1.0-develop"
jobs:
lint:
name: Lint
runs-on: ubuntu-20.04
steps:
- name: Code Checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: 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,89 +1,49 @@
name: Release
on:
push:
tags:
- "v*"
jobs:
release:
name: Release
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Code checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
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
node-version: 22
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn tsc
- run: yarn build:production
- name: create release branch and bump version
env:
REF: ${{ github.ref }}
VERSION: ${{ github.ref_name }}
run: |
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
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
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 CODE_OF_CONDUCT.md CONTRIBUTING.md flake.lock flake.nix phpunit.xml shell.nix
rm -rf node_modules tests 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: Extract changelog
env:
REF: ${{ github.ref }}
- name: write changelog
run: |
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
sed -n "/^## ${{ github.ref_name }}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG
- uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
draft: true
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
prerelease: ${{ contains(github.ref_name, 'rc') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }}
body_path: ./RELEASE_CHANGELOG
- 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
files: |
panel.tar.gz

5
.gitignore vendored
View File

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

View File

@ -2,20 +2,25 @@
use PhpCsFixer\Config;
use PhpCsFixer\Finder;
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
$finder = (new Finder())
->in(__DIR__)
->exclude([
'vendor',
'node_modules',
'storage',
'bootstrap/cache',
])
->notName(['_ide_helper*']);
->name('*.php')
->ignoreVCSIgnored(true)
->exclude([__DIR__ . '/bootstrap/cache'])
->in([
__DIR__ . '/app',
__DIR__ . '/bootstrap',
__DIR__ . '/config',
__DIR__ . '/database',
__DIR__ . '/routes',
__DIR__ . '/tests',
]);
return (new Config())
->setRiskyAllowed(true)
->setFinder($finder)
->setUsingCache(true)
->setParallelConfig(ParallelConfigFactory::detect())
->setRules([
'@Symfony' => true,
'@PSR1' => true,
@ -25,8 +30,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',
@ -42,7 +47,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,15 +3,64 @@ 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.11.6
## 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

View File

@ -1,74 +0,0 @@
# 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,31 +1,33 @@
# Contributing
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.
**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.
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.
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.
### Responsible Disclosure
## AI Assistance
This is a fairly in-depth project and makes use of a lot of parts. We strive to keep everything as secure as possible
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
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 without contacting us first by email.
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.
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
## 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. We also accept feature requests here as well.
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.
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 mhart/alpine-node:14
FROM --platform=$TARGETOS/$TARGETARCH node:22-alpine
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.1-fpm-alpine
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
WORKDIR /app
COPY . ./
COPY --from=0 /app/public/assets ./public/assets

View File

@ -25,17 +25,16 @@ 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/matthewpi)
[Interested in becoming a sponsor?](https://github.com/sponsors/pterodactyl)
| 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! |
| [**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. |
| [**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/?pterodactyl=true) | 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 |
|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**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. |
### Supported Games
@ -64,7 +63,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://github.com/parkervcp/eggs)
* [and many more...](https://pterodactyleggs.com)
## License

View File

@ -2,18 +2,23 @@
## Supported Versions
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.11.x | wings@1.11.x | :white_check_mark: |
| 0.7.x | daemon@0.6.x | :x: |
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.
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 reach out directly to any project team member on Discord when reporting a security vulnerability, or you can email `matthew@pterodactyl.io`.
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.
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 ($lastModified->diffInMinutes(Carbon::now()) > self::BACKUP_THRESHOLD_MINUTES) {
if ((int) $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 \Pterodactyl\Models\Node $node */
/** @var 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() ?? 0;
return parent::handle();
}
}

View File

@ -2,7 +2,6 @@
namespace Pterodactyl\Console\Commands\Schedule;
use Exception;
use Illuminate\Console\Command;
use Pterodactyl\Models\Schedule;
use Illuminate\Support\Facades\Log;
@ -67,7 +66,7 @@ class ProcessRunnableCommand extends Command
'schedule' => $schedule->name,
'hash' => $schedule->hashid,
]));
} catch (\Throwable|\Exception $exception) {
} catch (\Throwable $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 \Illuminate\Validation\ValidationException
* @throws 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)) {
} elseif (!empty($nodes) && empty($servers)) { // @phpstan-ignore empty.variable
$instance->whereIn('node_id', $nodes);
}

View File

@ -39,8 +39,8 @@ class UpgradeCommand extends Command
$this->line($this->getUrl());
}
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 . '].');
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 . '].');
}
$user = 'www-data';
@ -133,7 +133,7 @@ class UpgradeCommand extends Command
/** @var \Illuminate\Foundation\Application $app */
$app = require __DIR__ . '/../../../bootstrap/app.php';
/** @var \Pterodactyl\Console\Kernel $kernel */
/** @var 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

@ -0,0 +1,65 @@
<?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 static(Container::getInstance()))->shouldReport($exception);
return (new self(Container::getInstance()))->shouldReport($exception);
}
/**
@ -262,7 +262,7 @@ class Handler extends ExceptionHandler
{
$previous = [];
while ($value = $e->getPrevious()) {
if (!$value instanceof \Throwable) {
if (!$value instanceof \Throwable) { // @phpstan-ignore instanceof.alwaysTrue
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,21 +2,16 @@
namespace Pterodactyl\Extensions;
use Illuminate\Support\Arr;
use Hashids\Hashids as VendorHashids;
use Pterodactyl\Contracts\Extensions\HashidsInterface;
class Hashids extends VendorHashids implements HashidsInterface
{
/**
* {@inheritdoc}
*/
public function decodeFirst(string $encoded, string $default = null): mixed
public function decodeFirst(string $encoded, ?string $default = null): mixed
{
$result = $this->decode($encoded);
if (!is_array($result)) {
return $default;
}
return array_first($result, null, $default);
return Arr::first($result, null, $default);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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 $this->view->make('admin.locations.index', [
return view('admin.locations.index', [
'locations' => $this->repository->getAllWithDetails(),
]);
}
@ -47,7 +47,7 @@ class LocationController extends Controller
*/
public function view(int $id): View
{
return $this->view->make('admin.locations.view', [
return view('admin.locations.view', [
'location' => $this->repository->getWithNodes($id),
]);
}
@ -86,7 +86,7 @@ class LocationController extends Controller
* Delete a location from the system.
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws 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 $this->view->make('admin.mounts.index', [
return view('admin.mounts.index', [
'mounts' => $this->repository->getAllWithDetails(),
]);
}
@ -52,7 +52,7 @@ class MountController extends Controller
$nests = Nest::query()->with('eggs')->get();
$locations = Location::query()->with('nodes')->get();
return $this->view->make('admin.mounts.view', [
return view('admin.mounts.view', [
'mount' => $this->repository->getWithRelations($id),
'nests' => $nests,
'locations' => $locations,

View File

@ -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 $this->view->make('admin.eggs.new', ['nests' => $nests]);
return view('admin.eggs.new', ['nests' => $nests]);
}
/**
@ -66,7 +66,7 @@ class EggController extends Controller
*/
public function view(Egg $egg): View
{
return $this->view->make('admin.eggs.view', [
return view('admin.eggs.view', [
'egg' => $egg,
'images' => array_map(
fn ($key, $value) => $key === $value ? $value : "$key|$value",
@ -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 $this->view->make('admin.eggs.scripts', [
return view('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 $this->view->make('admin.eggs.variables', ['egg' => $egg]);
return view('admin.eggs.variables', ['egg' => $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 $this->view->make('admin.nests.index', [
return view('admin.nests.index', [
'nests' => $this->repository->getWithCounts(),
]);
}
@ -45,7 +45,7 @@ class NestController extends Controller
*/
public function create(): View
{
return $this->view->make('admin.nests.new');
return view('admin.nests.new');
}
/**
@ -68,7 +68,7 @@ class NestController extends Controller
*/
public function view(int $nest): View
{
return $this->view->make('admin.nests.view', [
return view('admin.nests.view', [
'nest' => $this->repository->getWithEggServers($nest),
]);
}

View File

@ -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 \Pterodactyl\Models\ApiKey|null $key */
/** @var ApiKey|null $key */
$key = $this->repository->getApplicationKeys($request->user())
->filter(function (ApiKey $key) {
foreach ($key->getAttributes() as $permission => $value) {

View File

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

View File

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

View File

@ -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 $this->view->make('admin.nodes.new', ['locations' => $locations]);
return view('admin.nodes.new', ['locations' => $locations]);
}
/**

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

View File

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

View File

@ -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');
$transfer->old_additional_allocations = $server->allocations->where('id', '!=', $server->allocation_id)->pluck('id')->values()->toArray();
$transfer->new_additional_allocations = $additional_allocations;
$transfer->save();

View File

@ -9,11 +9,9 @@ use Pterodactyl\Models\Server;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Servers\EnvironmentService;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Pterodactyl\Repositories\Eloquent\NestRepository;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Eloquent\MountRepository;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
@ -31,9 +29,7 @@ class ServerViewController extends Controller
private MountRepository $mountRepository,
private NestRepository $nestRepository,
private NodeRepository $nodeRepository,
private ServerRepository $repository,
private EnvironmentService $environmentService,
private ViewFactory $view
) {
}
@ -42,7 +38,7 @@ class ServerViewController extends Controller
*/
public function index(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.index', compact('server'));
return view('admin.servers.view.index', compact('server'));
}
/**
@ -50,7 +46,7 @@ class ServerViewController extends Controller
*/
public function details(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.details', compact('server'));
return view('admin.servers.view.details', compact('server'));
}
/**
@ -60,7 +56,7 @@ class ServerViewController extends Controller
{
$allocations = $server->node->allocations->toBase();
return $this->view->make('admin.servers.view.build', [
return view('admin.servers.view.build', [
'server' => $server,
'assigned' => $allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
'unassigned' => $allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
@ -87,7 +83,7 @@ class ServerViewController extends Controller
})->keyBy('id'),
]);
return $this->view->make('admin.servers.view.startup', compact('server', 'nests'));
return view('admin.servers.view.startup', compact('server', 'nests'));
}
/**
@ -95,7 +91,7 @@ class ServerViewController extends Controller
*/
public function database(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.database', [
return view('admin.servers.view.database', [
'hosts' => $this->databaseHostRepository->all(),
'server' => $server,
]);
@ -108,7 +104,7 @@ class ServerViewController extends Controller
{
$server->load('mounts');
return $this->view->make('admin.servers.view.mounts', [
return view('admin.servers.view.mounts', [
'mounts' => $this->mountRepository->getMountListForServer($server),
'server' => $server,
]);
@ -118,7 +114,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 \Pterodactyl\Exceptions\DisplayException
* @throws DisplayException
*/
public function manage(Request $request, Server $server): View
{
@ -137,7 +133,7 @@ class ServerViewController extends Controller
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
]);
return $this->view->make('admin.servers.view.manage', [
return view('admin.servers.view.manage', [
'server' => $server,
'locations' => $this->locationRepository->all(),
'canTransfer' => $canTransfer,
@ -149,6 +145,6 @@ class ServerViewController extends Controller
*/
public function delete(Request $request, Server $server): View
{
return $this->view->make('admin.servers.view.delete', compact('server'));
return view('admin.servers.view.delete', compact('server'));
}
}

View File

@ -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 \Pterodactyl\Exceptions\Model\DataValidationException
* @throws 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 \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws DisplayException
* @throws 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 \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws DisplayException
* @throws 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 \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws DisplayException
* @throws 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 \Pterodactyl\Exceptions\DisplayException
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Illuminate\Validation\ValidationException
* @throws ValidationException
*/
public function updateBuild(Request $request, Server $server): RedirectResponse
{
@ -159,7 +159,7 @@ class ServersController extends Controller
/**
* Start the server deletion process.
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws 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 \Illuminate\Validation\ValidationException
* @throws 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 \Pterodactyl\Models\Database $database */
/** @var Database $database */
$database = $server->databases()->findOrFail($request->input('database'));
$this->databasePasswordService->handle($database);

View File

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

View File

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

View File

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

View File

@ -36,7 +36,7 @@ class UserController extends Controller
protected Translator $translator,
protected UserUpdateService $updateService,
protected UserRepositoryInterface $repository,
protected ViewFactory $view
protected ViewFactory $view,
) {
}
@ -54,10 +54,11 @@ class UserController extends Controller
->groupBy('users.id')
)
->allowedFilters(['username', 'email', 'uuid'])
->defaultSort('-root_admin')
->allowedSorts(['id', 'uuid'])
->paginate(50);
return $this->view->make('admin.users.index', ['users' => $users]);
return view('admin.users.index', ['users' => $users]);
}
/**
@ -65,7 +66,7 @@ class UserController extends Controller
*/
public function create(): View
{
return $this->view->make('admin.users.new', [
return view('admin.users.new', [
'languages' => $this->getAvailableLanguages(true),
]);
}
@ -75,7 +76,7 @@ class UserController extends Controller
*/
public function view(User $user): View
{
return $this->view->make('admin.users.view', [
return view('admin.users.view', [
'user' => $user,
'languages' => $this->getAvailableLanguages(true),
]);
@ -85,12 +86,12 @@ class UserController extends Controller
* Delete a user from the system.
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws DisplayException
*/
public function delete(Request $request, User $user): RedirectResponse
{
if ($request->user()->id === $user->id) {
throw new DisplayException($this->translator->get('admin/user.exceptions.user_has_servers'));
if ($request->user()->is($user)) {
throw new DisplayException(__('admin/user.exceptions.delete_self'));
}
$this->deletionService->handle($user);
@ -139,12 +140,14 @@ 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);
Assert::subclassOf($abstract, BaseTransformer::class); // @phpstan-ignore staticMethod.alreadyNarrowedType
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')) {
if (method_exists($guard, 'logoutOtherDevices')) { // @phpstan-ignore function.alreadyNarrowedType
$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 \Pterodactyl\Exceptions\DisplayException
* @throws DisplayException
*/
public function store(StoreApiKeyRequest $request): array
{
@ -54,7 +54,7 @@ class ApiKeyController extends ClientApiController
*/
public function delete(ClientApiRequest $request, string $identifier): JsonResponse
{
/** @var \Pterodactyl\Models\ApiKey $key */
/** @var 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);
Assert::subclassOf($abstract, BaseClientTransformer::class); // @phpstan-ignore staticMethod.alreadyNarrowedType
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 \Illuminate\Auth\Access\AuthorizationException
* @throws AuthorizationException
*/
public function index(Request $request, Server $server): array
{
@ -74,15 +74,21 @@ 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((bool) $request->input('is_locked'));
$action->setIsLocked($request->boolean('is_locked'));
}
$backup = $action->handle($server, $request->input('name'));
$backup = Activity::event('server:backup.start')->transaction(function ($log) use ($action, $server, $request) {
$server->backups()->lockForUpdate();
Activity::event('server:backup.start')
->subject($backup)
->property(['name' => $backup->name, 'locked' => (bool) $request->input('is_locked')])
->log();
$backup = $action->handle($server, $request->input('name'));
$log->subject($backup)->property([
'name' => $backup->name,
'locked' => $request->boolean('is_locked'),
]);
return $backup;
});
return $this->fractal->item($backup)
->transformWith($this->getTransformer(BackupTransformer::class))
@ -93,7 +99,7 @@ class BackupController extends ClientApiController
* Toggles the lock status of a given backup for a server.
*
* @throws \Throwable
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws AuthorizationException
*/
public function toggleLock(Request $request, Server $server, Backup $backup): array
{
@ -115,7 +121,7 @@ class BackupController extends ClientApiController
/**
* Returns information about a single backup.
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws AuthorizationException
*/
public function view(Request $request, Server $server, Backup $backup): array
{
@ -156,7 +162,7 @@ class BackupController extends ClientApiController
* which the user is redirected to.
*
* @throws \Throwable
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws AuthorizationException
*/
public function download(Request $request, Server $server, Backup $backup): JsonResponse
{

View File

@ -5,7 +5,6 @@ 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;
@ -26,7 +25,7 @@ class CommandController extends ClientApiController
/**
* Send a command to a running server.
*
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
* @throws DaemonConnectionException
*/
public function index(SendCommandRequest $request, Server $server): Response
{
@ -36,10 +35,7 @@ class CommandController extends ClientApiController
$previous = $exception->getPrevious();
if ($previous instanceof BadResponseException) {
if (
$previous->getResponse() instanceof ResponseInterface
&& $previous->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY
) {
if ($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,12 +48,15 @@ class DatabaseController extends ClientApiController
*/
public function store(StoreDatabaseRequest $request, Server $server): array
{
$database = $this->deployDatabaseService->handle($server, $request->validated());
$database = Activity::event('server:database.create')->transaction(function ($log) use ($request, $server) {
$server->databases()->lockForUpdate();
Activity::event('server:database.create')
->subject($database)
->property('name', $database->database)
->log();
$database = $this->deployDatabaseService->handle($server, $request->validated());
$log->subject($database)->property('name', $database->database);
return $database;
});
return $this->fractal->item($database)
->parseIncludes(['password'])
@ -69,15 +72,16 @@ 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)
->log();
->transaction(function () use ($database) {
$database->lockForUpdate();
return $this->fractal->item($database)
$this->passwordService->handle($database);
});
return $this->fractal->item($database->refresh())
->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,6 +6,7 @@ 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;
@ -23,8 +24,9 @@ class NetworkAllocationController extends ClientApiController
* NetworkAllocationController constructor.
*/
public function __construct(
protected readonly ConnectionInterface $connection,
private FindAssignableAllocationService $assignableAllocationService,
private ServerRepository $serverRepository
private ServerRepository $serverRepository,
) {
parent::__construct();
}
@ -88,20 +90,21 @@ class NetworkAllocationController extends ClientApiController
* Set the notes for the allocation for a server.
*s.
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws DisplayException
*/
public function store(NewAllocationRequest $request, Server $server): array
{
if ($server->allocations()->count() >= $server->allocation_limit) {
throw new DisplayException('Cannot assign additional allocations to this server: limit has been reached.');
}
$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.');
}
$allocation = $this->assignableAllocationService->handle($server);
$allocation = $this->assignableAllocationService->handle($server);
Activity::event('server:allocation.create')
->subject($allocation)
->property('allocation', $allocation->toString())
->log();
$log->subject($allocation)->property('allocation', $allocation->toString());
return $allocation;
});
return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class))
@ -111,7 +114,7 @@ class NetworkAllocationController extends ClientApiController
/**
* Delete an allocation from a server.
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws 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 \Pterodactyl\Exceptions\DisplayException
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(StoreScheduleRequest $request, Server $server): array
{
/** @var \Pterodactyl\Models\Schedule $model */
/** @var 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 \Pterodactyl\Exceptions\DisplayException
* @throws 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 \Pterodactyl\Exceptions\DisplayException
* @throws 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 \Pterodactyl\Exceptions\Service\ServiceLimitExceededException
* @throws 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 \Pterodactyl\Models\Task|null $lastTask */
/** @var Task|null $lastTask */
$lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first();
/** @var \Pterodactyl\Models\Task $task */
/** @var 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,9 +52,7 @@ 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.');
@ -62,6 +60,8 @@ 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 DaemonServerRepository $serverRepository
private DaemonRevocationRepository $revocationRepository,
) {
parent::__construct();
}
@ -115,7 +115,10 @@ class SubuserController extends ClientApiController
]);
try {
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
$this->revocationRepository->setNode($server->node)->deauthorize(
$subuser->user->uuid,
[$server->uuid],
);
} 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.
@ -150,7 +153,10 @@ class SubuserController extends ClientApiController
$subuser->delete();
try {
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
$this->revocationRepository->setNode($server->node)->deauthorize(
$subuser->user->uuid,
[$server->uuid],
);
} 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 \Pterodactyl\Models\Server|null $server */
/** @var Server|null $server */
$server = $servers->get($datum['server']);
if (is_null($server) || !Str::startsWith($datum['event'], 'server:')) {
continue;

View File

@ -9,6 +9,7 @@ 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;
@ -42,15 +43,18 @@ class BackupRemoteUploadController extends Controller
throw new BadRequestHttpException('A non-empty "size" query parameter must be provided.');
}
/** @var \Pterodactyl\Models\Backup $backup */
$backup = Backup::query()
->where('node_id', $node->id)
->where('uuid', $backup)
->firstOrFail();
$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.');
}
// Prevent backups that have already been completed from trying to
// be uploaded again.
if (!is_null($backup->completed_at)) {
if (!is_null($model->completed_at)) {
throw new ConflictHttpException('This backup is already in a completed state.');
}
@ -61,7 +65,7 @@ class BackupRemoteUploadController extends Controller
}
// The path where backup will be uploaded to
$path = sprintf('%s/%s.tar.gz', $backup->server->uuid, $backup->uuid);
$path = sprintf('%s/%s.tar.gz', $model->server->uuid, $model->uuid);
// Get the S3 client
$client = $adapter->getClient();
@ -99,7 +103,7 @@ class BackupRemoteUploadController extends Controller
}
// Set the upload_id on the backup in the database.
$backup->update(['upload_id' => $params['UploadId']]);
$model->update(['upload_id' => $params['UploadId']]);
return new JsonResponse([
'parts' => $parts,

View File

@ -11,6 +11,7 @@ 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;
@ -34,12 +35,19 @@ class BackupStatusController extends Controller
/** @var \Pterodactyl\Models\Node $node */
$node = $request->attributes->get('node');
/** @var \Pterodactyl\Models\Backup $model */
/** @var Backup $model */
$model = Backup::query()
->where('node_id', $node->id)
->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.');
}
if ($model->is_successful) {
throw new BadRequestHttpException('Cannot update the status of a backup that is already marked as completed.');
}
@ -84,7 +92,7 @@ class BackupStatusController extends Controller
*/
public function restore(Request $request, string $backup): JsonResponse
{
/** @var \Pterodactyl\Models\Backup $model */
/** @var Backup $model */
$model = Backup::query()->where('uuid', $backup)->firstOrFail();
$model->server->update(['status' => null]);
@ -102,7 +110,7 @@ class BackupStatusController extends Controller
* the given backup.
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws 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 \Pterodactyl\Models\Server $server */
/** @var 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)
->property('name', $subject->subject->name) // @phpstan-ignore property.notFound
->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());
return strtolower(strrev($username[0] ?? '') . '|' . $request->ip()); // @phpstan-ignore nullCoalesce.offset
}
}

View File

@ -49,9 +49,11 @@ abstract class AbstractLoginController extends Controller
/**
* Get the failed login response instance.
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @return never-return
*
* @throws 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, [
@ -91,7 +93,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';
}
@ -99,7 +101,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,6 +2,7 @@
namespace Pterodactyl\Http\Controllers\Auth;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Pterodactyl\Models\User;
@ -24,7 +25,7 @@ class LoginCheckpointController extends AbstractLoginController
public function __construct(
private Encrypter $encrypter,
private Google2FA $google2FA,
private ValidationFactory $validation
private ValidationFactory $validation,
) {
parent::__construct();
}
@ -56,7 +57,6 @@ 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,8 +71,20 @@ 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);
@ -106,9 +118,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,19 +9,10 @@ use Pterodactyl\Models\User;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Facades\Activity;
use Illuminate\Contracts\View\View;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class LoginController extends AbstractLoginController
{
/**
* LoginController constructor.
*/
public function __construct(private ViewFactory $view)
{
parent::__construct();
}
/**
* Handle all incoming requests for the authentication routes and render the
* base authentication view component. React will take over at this point and
@ -29,7 +20,7 @@ class LoginController extends AbstractLoginController
*/
public function index(): View
{
return $this->view->make('templates/auth.core');
return view('templates/auth.core');
}
/**
@ -48,7 +39,7 @@ class LoginController extends AbstractLoginController
try {
$username = $request->input('user');
/** @var \Pterodactyl\Models\User $user */
/** @var 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