From 548affba84f3478186fb926dd122d9060dc3bcbd Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Sat, 29 Oct 2022 19:58:55 -0400 Subject: [PATCH 01/95] Fix linting (#4504) --- app/Http/Controllers/Admin/Servers/ServerTransferController.php | 2 +- app/Models/Model.php | 2 +- app/Models/UserSSHKey.php | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Admin/Servers/ServerTransferController.php b/app/Http/Controllers/Admin/Servers/ServerTransferController.php index cdb2bb5579..0962174186 100644 --- a/app/Http/Controllers/Admin/Servers/ServerTransferController.php +++ b/app/Http/Controllers/Admin/Servers/ServerTransferController.php @@ -4,8 +4,8 @@ use Illuminate\Http\Request; use Pterodactyl\Models\Server; -use Prologue\Alerts\AlertsMessageBag; use Illuminate\Http\RedirectResponse; +use Prologue\Alerts\AlertsMessageBag; use Pterodactyl\Models\ServerTransfer; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Services\Servers\TransferService; diff --git a/app/Models/Model.php b/app/Models/Model.php index c5bc666d15..2e371d9d9c 100644 --- a/app/Models/Model.php +++ b/app/Models/Model.php @@ -2,9 +2,9 @@ namespace Pterodactyl\Models; +use Carbon\CarbonImmutable; use Illuminate\Support\Arr; use Illuminate\Support\Str; -use Carbon\CarbonImmutable; use Illuminate\Support\Carbon; use Illuminate\Validation\Rule; use Illuminate\Container\Container; diff --git a/app/Models/UserSSHKey.php b/app/Models/UserSSHKey.php index 0aac52310e..fb3c8f395d 100644 --- a/app/Models/UserSSHKey.php +++ b/app/Models/UserSSHKey.php @@ -32,6 +32,7 @@ * @method static \Illuminate\Database\Eloquent\Builder|UserSSHKey whereUserId($value) * @method static \Illuminate\Database\Query\Builder|UserSSHKey withTrashed() * @method static \Illuminate\Database\Query\Builder|UserSSHKey withoutTrashed() + * * @mixin \Eloquent * * @method static \Database\Factories\UserSSHKeyFactory factory(...$parameters) From b1abae8106e40858b3b97f9b4a8a325c59d01131 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 30 Oct 2022 13:58:29 -0600 Subject: [PATCH 02/95] Update README.md --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9ac8a0d15e..79efc44e6d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ ![GitHub contributors](https://img.shields.io/github/contributors/pterodactyl/panel?style=for-the-badge) # Pterodactyl Panel + Pterodactyl® is a free, open-source game server management panel built with PHP, React, and Go. Designed with security in mind, Pterodactyl runs all game servers in isolated Docker containers while exposing a beautiful and intuitive UI to end users. @@ -15,30 +16,31 @@ Stop settling for less. Make game servers a first class citizen on your platform ![Image](https://cdn.pterodactyl.io/site-assets/pterodactyl_v1_demo.gif) ## Documentation + * [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html) * [Wings Documentation](https://pterodactyl.io/wings/1.0/installing.html) * [Community Guides](https://pterodactyl.io/community/about.html) * Or, get additional help [via Discord](https://discord.gg/pterodactyl) ## Sponsors + I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's developement. [Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi) -| Company | About | -| ------- | ----- | -| [**WISP**](https://wisp.gg) | Extra features. | -| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. | -| [**Fragnet**](https://fragnet.net) | Providing low latency, high-end game hosting solutions to gamers, game studios and eSports platforms. | -| [**Tempest**](https://tempest.net/) | Tempest Hosting is a subsidiary of Path Network, Inc. offering unmetered DDoS protected 10Gbps dedicated servers, starting at just $80/month. Full anycast, tons of filters. | -| [**Bloom.host**](https://bloom.host) | Bloom.host offers dedicated core VPS and Minecraft hosting with Ryzen 9 processors. With owned-hardware, we offer truly unbeatable prices on high-performance hosting. | -| [**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! | -| [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | -| [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. | -| [**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. | -| [**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.| -| [**Gamenodes**](https://gamenodes.nl) | Gamenodes love quality. For Minecraft, Discord Bots and other services, among others. With our own programmers, we provide just that little bit of extra service! | +| Company | About | +|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [**WISP**](https://wisp.gg) | Extra features. | +| [**Fragnet**](https://fragnet.net) | Providing low latency, high-end game hosting solutions to gamers, game studios and eSports platforms. | +| [**RocketNode**](https://rocketnode.com/) | Innovative game server hosting combined with a straightforward control panel, affordable prices, and Rocket-Fast support. | +| [**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! | +| [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | +| [**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. | +| [**Pterodactyl Market**](https://pterodactylmarket.com/) | Pterodactyl Market is a one-and-stop shop for Pterodactyl. In our market, you can find Add-ons, Themes, Eggs, and more for Pterodactyl. | ### Supported Games + Pterodactyl supports a wide variety of games by utilizing Docker containers to isolate each instance. This gives you the power to run game servers without bloating machines with a host of additional dependencies. @@ -67,6 +69,7 @@ and there are plenty more games available provided by the community. Some of the * [and many more...](https://github.com/parkervcp/eggs) ## License + Pterodactyl® Copyright © 2015 - 2022 Dane Everitt and contributors. Code released under the [MIT License](./LICENSE.md). From f2095e815ed93cb958e857d6dc3f93510845b5f2 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Mon, 31 Oct 2022 17:20:53 +0100 Subject: [PATCH 03/95] Allow users to change the server description (#4420) --- .../Api/Client/Servers/SettingsController.php | 1 + .../Servers/Settings/RenameServerRequest.php | 3 ++- app/Models/Permission.php | 2 +- resources/lang/en/activity.php | 1 + resources/scripts/api/server/renameServer.ts | 4 ++-- .../server/settings/RenameServerBox.tsx | 22 ++++++++++++++----- .../Client/Server/SettingsControllerTest.php | 5 +++++ 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/SettingsController.php b/app/Http/Controllers/Api/Client/Servers/SettingsController.php index 7f1946a212..d1377d67a3 100644 --- a/app/Http/Controllers/Api/Client/Servers/SettingsController.php +++ b/app/Http/Controllers/Api/Client/Servers/SettingsController.php @@ -36,6 +36,7 @@ public function rename(RenameServerRequest $request, Server $server): JsonRespon { $this->repository->update($server->id, [ 'name' => $request->input('name'), + 'description' => $request->input('description') ?? '', ]); if ($server->name !== $request->input('name')) { diff --git a/app/Http/Requests/Api/Client/Servers/Settings/RenameServerRequest.php b/app/Http/Requests/Api/Client/Servers/Settings/RenameServerRequest.php index 8cb5efa35c..4a74f0a0e6 100644 --- a/app/Http/Requests/Api/Client/Servers/Settings/RenameServerRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Settings/RenameServerRequest.php @@ -11,7 +11,7 @@ class RenameServerRequest extends ClientApiRequest implements ClientPermissionsR { /** * Returns the permissions string indicating which permission should be used to - * validate that the authenticated user has permission to perform this action aganist + * validate that the authenticated user has permission to perform this action against * the given resource (server). */ public function permission(): string @@ -26,6 +26,7 @@ public function rules(): array { return [ 'name' => Server::getRules()['name'], + 'description' => 'string|nullable', ]; } } diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 6d27a90d30..b2e00070e7 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -195,7 +195,7 @@ class Permission extends Model 'settings' => [ 'description' => 'Permissions that control a user\'s access to the settings for this server.', 'keys' => [ - 'rename' => 'Allows a user to rename this server.', + 'rename' => 'Allows a user to rename this server and change the description of it.', 'reinstall' => 'Allows a user to trigger a reinstall of this server.', ], ], diff --git a/resources/lang/en/activity.php b/resources/lang/en/activity.php index afa6558065..501a1dcde6 100644 --- a/resources/lang/en/activity.php +++ b/resources/lang/en/activity.php @@ -115,6 +115,7 @@ ], 'settings' => [ 'rename' => 'Renamed the server from :old to :new', + 'description' => 'Changed the server description from :old to :new', ], 'startup' => [ 'edit' => 'Changed the :variable variable from ":old" to ":new"', diff --git a/resources/scripts/api/server/renameServer.ts b/resources/scripts/api/server/renameServer.ts index a4a95141e2..4622f9d4d7 100644 --- a/resources/scripts/api/server/renameServer.ts +++ b/resources/scripts/api/server/renameServer.ts @@ -1,8 +1,8 @@ import http from '@/api/http'; -export default (uuid: string, name: string): Promise => { +export default (uuid: string, name: string, description?: string): Promise => { return new Promise((resolve, reject) => { - http.post(`/api/client/servers/${uuid}/settings/rename`, { name }) + http.post(`/api/client/servers/${uuid}/settings/rename`, { name, description }) .then(() => resolve()) .catch(reject); }); diff --git a/resources/scripts/components/server/settings/RenameServerBox.tsx b/resources/scripts/components/server/settings/RenameServerBox.tsx index 98be2ef9f9..9cb290a1ad 100644 --- a/resources/scripts/components/server/settings/RenameServerBox.tsx +++ b/resources/scripts/components/server/settings/RenameServerBox.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { ServerContext } from '@/state/server'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; -import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; +import { Field as FormikField, Form, Formik, FormikHelpers, useFormikContext } from 'formik'; import { Actions, useStoreActions } from 'easy-peasy'; import renameServer from '@/api/server/renameServer'; import Field from '@/components/elements/Field'; @@ -11,19 +11,29 @@ import { ApplicationStore } from '@/state'; import { httpErrorToHuman } from '@/api/http'; import { Button } from '@/components/elements/button/index'; import tw from 'twin.macro'; +import Label from '@/components/elements/Label'; +import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper'; +import { Textarea } from '@/components/elements/Input'; interface Values { name: string; + description: string; } const RenameServerBox = () => { const { isSubmitting } = useFormikContext(); return ( - +
+
+ + + + +
@@ -37,10 +47,10 @@ export default () => { const setServer = ServerContext.useStoreActions((actions) => actions.server.setServer); const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); - const submit = ({ name }: Values, { setSubmitting }: FormikHelpers) => { + const submit = ({ name, description }: Values, { setSubmitting }: FormikHelpers) => { clearFlashes('settings'); - renameServer(server.uuid, name) - .then(() => setServer({ ...server, name })) + renameServer(server.uuid, name, description) + .then(() => setServer({ ...server, name, description })) .catch((error) => { console.error(error); addError({ key: 'settings', message: httpErrorToHuman(error) }); @@ -53,9 +63,11 @@ export default () => { onSubmit={submit} initialValues={{ name: server.name, + description: server.description, }} validationSchema={object().shape({ name: string().required().min(1), + description: string().nullable(), })} > diff --git a/tests/Integration/Api/Client/Server/SettingsControllerTest.php b/tests/Integration/Api/Client/Server/SettingsControllerTest.php index c92754cfa1..882314bea1 100644 --- a/tests/Integration/Api/Client/Server/SettingsControllerTest.php +++ b/tests/Integration/Api/Client/Server/SettingsControllerTest.php @@ -21,9 +21,11 @@ public function testServerNameCanBeChanged(array $permissions) /** @var \Pterodactyl\Models\Server $server */ [$user, $server] = $this->generateTestAccount($permissions); $originalName = $server->name; + $originalDescription = $server->description; $response = $this->actingAs($user)->postJson("/api/client/servers/$server->uuid/settings/rename", [ 'name' => '', + 'description' => '', ]); $response->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY); @@ -31,15 +33,18 @@ public function testServerNameCanBeChanged(array $permissions) $server = $server->refresh(); $this->assertSame($originalName, $server->name); + $this->assertSame($originalDescription, $server->description); $this->actingAs($user) ->postJson("/api/client/servers/$server->uuid/settings/rename", [ 'name' => 'Test Server Name', + 'description' => 'This is a test server.', ]) ->assertStatus(Response::HTTP_NO_CONTENT); $server = $server->refresh(); $this->assertSame('Test Server Name', $server->name); + $this->assertSame('This is a test server.', $server->description); } /** From 16c2b606b44e0420dedb50a0098f4c8bb86e6f46 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Mon, 31 Oct 2022 12:29:10 -0400 Subject: [PATCH 04/95] Add ManifestDoesNotExistException and Solution (#4455) Co-authored-by: Matthew Penner --- .../ManifestDoesNotExistException.php | 15 +++++++++++ .../ManifestDoesNotExistSolution.php | 25 +++++++++++++++++++ app/Services/Helpers/AssetHashService.php | 8 +++++- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 app/Exceptions/ManifestDoesNotExistException.php create mode 100644 app/Exceptions/Solutions/ManifestDoesNotExistSolution.php diff --git a/app/Exceptions/ManifestDoesNotExistException.php b/app/Exceptions/ManifestDoesNotExistException.php new file mode 100644 index 0000000000..2769cd03c3 --- /dev/null +++ b/app/Exceptions/ManifestDoesNotExistException.php @@ -0,0 +1,15 @@ + 'https://github.com/pterodactyl/panel/blob/develop/package.json', + ]; + } +} diff --git a/app/Services/Helpers/AssetHashService.php b/app/Services/Helpers/AssetHashService.php index 2e1c1aaa65..725a566692 100644 --- a/app/Services/Helpers/AssetHashService.php +++ b/app/Services/Helpers/AssetHashService.php @@ -5,6 +5,7 @@ use Illuminate\Support\Arr; use Illuminate\Filesystem\FilesystemManager; use Illuminate\Contracts\Filesystem\Filesystem; +use Pterodactyl\Exceptions\ManifestDoesNotExistException; class AssetHashService { @@ -106,6 +107,11 @@ protected function manifest(): array ); } - return static::$manifest; + $manifest = static::$manifest; + if ($manifest === null) { + throw new ManifestDoesNotExistException(); + } + + return $manifest; } } From e21aab2537afa349a2ee8d41b7145d44bc8cd1a4 Mon Sep 17 00:00:00 2001 From: Quinten <67589015+QuintenQVD0@users.noreply.github.com> Date: Mon, 31 Oct 2022 17:29:29 +0100 Subject: [PATCH 05/95] Update mumble egg (#4437) --- .../eggs/voice-servers/egg-mumble-server.json | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/database/Seeders/eggs/voice-servers/egg-mumble-server.json b/database/Seeders/eggs/voice-servers/egg-mumble-server.json index 27f60e7173..feac4dc1a8 100644 --- a/database/Seeders/eggs/voice-servers/egg-mumble-server.json +++ b/database/Seeders/eggs/voice-servers/egg-mumble-server.json @@ -1,28 +1,28 @@ { "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", "meta": { - "version": "PTDL_v1", + "version": "PTDL_v2", "update_url": null }, - "exported_at": "2021-06-15T16:54:54-04:00", + "exported_at": "2022-10-15T12:38:18+02:00", "name": "Mumble Server", "author": "support@pterodactyl.io", "description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.", "features": null, - "images": [ - "ghcr.io\/pterodactyl\/yolks:alpine" - ], + "docker_images": { + "Mumble": "ghcr.io\/parkervcp\/yolks:voice_mumble" + }, "file_denylist": [], - "startup": ".\/murmur.x86 -fg", + "startup": "mumble-server -fg -ini murmur.ini", "config": { - "files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"logfile\": \"murmur.log\",\r\n \"port\": \"{{server.build.default.port}}\",\r\n \"host\": \"0.0.0.0\",\r\n \"users\": \"{{server.build.env.MAX_USERS}}\"\r\n }\r\n }\r\n}", + "files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"database\": \"\/home\/container\/murmur.sqlite\",\r\n \"logfile\": \"\/home\/container\/murmur.log\",\r\n \"port\": \"{{server.build.default.port}}\",\r\n \"host\": \"0.0.0.0\",\r\n \"users\": \"{{server.build.env.MAX_USERS}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \"Server listening on\"\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/murmur.log\"\r\n}", + "logs": "{}", "stop": "^C" }, "scripts": { "installation": { - "script": "#!\/bin\/ash\r\n# Mumble Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\nGITHUB_PACKAGE=mumble-voip\/mumble\r\nMATCH=murmur-static\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nif [ -z \"${GITHUB_USER}\" ] && [ -z \"${GITHUB_OAUTH_TOKEN}\" ] ; then\r\n echo -e \"using anon api call\"\r\nelse\r\n echo -e \"user and oauth token set\"\r\n alias curl='curl -u ${GITHUB_USER}:${GITHUB_OAUTH_TOKEN} '\r\nfi\r\n\r\n## get release info and download links\r\nLATEST_JSON=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\/latest\")\r\nRELEASES=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\")\r\n\r\nif [ -z \"${VERSION}\" ] || [ \"${VERSION}\" == \"latest\" ]; then\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url | grep -m 1 -i ${MATCH})\r\nelse\r\n VERSION_CHECK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .tag_name')\r\n if [ \"${VERSION}\" == \"${VERSION_CHECK}\" ]; then\r\n DOWNLOAD_LINK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .assets[].browser_download_url' | grep -m 1 -i ${MATCH})\r\n else\r\n echo -e \"defaulting to latest release\"\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url)\r\n fi\r\nfi\r\n\r\ncurl -L ${DOWNLOAD_LINK} | tar xjv --strip-components=1", + "script": "#!\/bin\/ash\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nFILE=\/mnt\/server\/murmur.ini\r\nif [ -f \"$FILE\" ]; then\r\n echo \"Config file already exists.\"\r\nelse \r\n echo \"Downloading the config file.\"\r\n apk add --no-cache murmur\r\n cp \/etc\/murmur.ini \/mnt\/server\/murmur.ini\r\n apk del murmur\r\nfi\r\necho \"done\"", "container": "ghcr.io\/pterodactyl\/installers:alpine", "entrypoint": "ash" } @@ -35,16 +35,8 @@ "default_value": "100", "user_viewable": true, "user_editable": false, - "rules": "required|numeric|digits_between:1,5" - }, - { - "name": "Server Version", - "description": "Version of Mumble Server to download and use.", - "env_variable": "MUMBLE_VERSION", - "default_value": "latest", - "user_viewable": true, - "user_editable": true, - "rules": "required|string" + "rules": "required|numeric|digits_between:1,5", + "field_type": "text" } ] -} \ No newline at end of file +} From 1b780302910b1451c119b303239da33106c6bc00 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 31 Oct 2022 10:58:32 -0600 Subject: [PATCH 06/95] Fix compatibility with old queue names --- config/mail.php | 4 ++-- config/queue.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/mail.php b/config/mail.php index 0f9639291d..5e910befec 100644 --- a/config/mail.php +++ b/config/mail.php @@ -91,8 +91,8 @@ */ 'from' => [ - 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), - 'name' => env('MAIL_FROM_NAME', 'Example'), + 'address' => env('MAIL_FROM_ADDRESS', env('MAIL_FROM')), + 'name' => env('MAIL_FROM_NAME', 'Pterodactyl Panel'), ], /* diff --git a/config/queue.php b/config/queue.php index 35585df0e8..39b93eefb2 100644 --- a/config/queue.php +++ b/config/queue.php @@ -33,7 +33,7 @@ 'database' => [ 'driver' => 'database', 'table' => 'jobs', - 'queue' => 'default', + 'queue' => env('QUEUE_STANDARD', 'default'), 'retry_after' => 90, 'after_commit' => false, ], @@ -43,7 +43,7 @@ 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), - 'queue' => env('SQS_QUEUE', 'default'), + 'queue' => env('SQS_QUEUE', env('QUEUE_STANDARD', 'default')), 'suffix' => env('SQS_SUFFIX'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'after_commit' => false, @@ -52,7 +52,7 @@ 'redis' => [ 'driver' => 'redis', 'connection' => 'default', - 'queue' => env('REDIS_QUEUE', 'default'), + 'queue' => env('REDIS_QUEUE', env('QUEUE_STANDARD', 'default')), 'retry_after' => 90, 'block_for' => null, 'after_commit' => false, From 846ff7644f60018af6a79b64174d8799ea2c169c Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 31 Oct 2022 11:02:34 -0600 Subject: [PATCH 07/95] Fix missing email sender --- config/mail.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/mail.php b/config/mail.php index 5e910befec..95c6f303cf 100644 --- a/config/mail.php +++ b/config/mail.php @@ -91,7 +91,7 @@ */ 'from' => [ - 'address' => env('MAIL_FROM_ADDRESS', env('MAIL_FROM')), + 'address' => env('MAIL_FROM_ADDRESS', env('MAIL_FROM', 'hello@example.com')), 'name' => env('MAIL_FROM_NAME', 'Pterodactyl Panel'), ], From d466934103f2dc062498dc7ffaeb82f1d1b46867 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 31 Oct 2022 11:38:56 -0600 Subject: [PATCH 08/95] Laravel Sail (#4508) --- .env.ci | 2 +- .github/workflows/ci.yaml | 2 +- bootstrap/cache/.gitignore | 0 bootstrap/tests.php | 3 +- composer.json | 3 +- composer.lock | 158 +++++++++++++++++++++++++------------ phpunit.xml | 2 +- 7 files changed, 116 insertions(+), 54 deletions(-) mode change 100755 => 100644 bootstrap/cache/.gitignore diff --git a/.env.ci b/.env.ci index e893452d44..1a9e848e3b 100644 --- a/.env.ci +++ b/.env.ci @@ -8,7 +8,7 @@ APP_ENVIRONMENT_ONLY=true DB_CONNECTION=mysql DB_HOST=127.0.0.1 -DB_DATABASE=panel_test +DB_DATABASE=testing DB_USERNAME=root DB_PASSWORD= diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0e6f93b66f..4da19b6ed2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,7 +24,7 @@ jobs: image: ${{ matrix.database }} env: MYSQL_ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: panel_test + MYSQL_DATABASE: testing ports: - 3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100755 new mode 100644 diff --git a/bootstrap/tests.php b/bootstrap/tests.php index 35479a0cd6..5b54493554 100644 --- a/bootstrap/tests.php +++ b/bootstrap/tests.php @@ -1,5 +1,6 @@ writeln(PHP_EOL . 'Cannot run test process against non-testing database.'); $output->writeln(PHP_EOL . 'Environment is currently pointed at: "' . config("$prefix.database") . '".'); exit(1); diff --git a/composer.json b/composer.json index 2d40267819..160537b6bb 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ } ], "require": { - "php": "^8.0.2 || ^8.1", + "php": "^8.0.2 || ^8.1 || ^8.2", "ext-json": "*", "ext-mbstring": "*", "ext-pdo": "*", @@ -58,6 +58,7 @@ "fakerphp/faker": "~1.20", "friendsofphp/php-cs-fixer": "~3.11", "itsgoingd/clockwork": "~5.1", + "laravel/sail": "~1.16", "mockery/mockery": "~1.5", "nunomaduro/collision": "~6.3", "php-mock/php-mock-phpunit": "~2.6", diff --git a/composer.lock b/composer.lock index a506d4edc2..9f4b73f6f9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "80a0f1016b1ba9e0b31d6cfe0f27f8c1", + "content-hash": "ae61e7d6e405e3a59c8a54f3eefa2c50", "packages": [ { "name": "aws/aws-crt-php", @@ -376,16 +376,16 @@ }, { "name": "doctrine/dbal", - "version": "3.4.5", + "version": "3.4.6", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "a5a58773109c0abb13e658c8ccd92aeec8d07f9e" + "reference": "3ce132f7c0b83d33b26ab6ed308e9e9260699bc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/a5a58773109c0abb13e658c8ccd92aeec8d07f9e", - "reference": "a5a58773109c0abb13e658c8ccd92aeec8d07f9e", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/3ce132f7c0b83d33b26ab6ed308e9e9260699bc4", + "reference": "3ce132f7c0b83d33b26ab6ed308e9e9260699bc4", "shasum": "" }, "require": { @@ -400,14 +400,14 @@ "require-dev": { "doctrine/coding-standard": "10.0.0", "jetbrains/phpstorm-stubs": "2022.2", - "phpstan/phpstan": "1.8.3", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "9.5.24", + "phpstan/phpstan": "1.8.10", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "9.5.25", "psalm/plugin-phpunit": "0.17.0", "squizlabs/php_codesniffer": "3.7.1", "symfony/cache": "^5.4|^6.0", "symfony/console": "^4.4|^5.4|^6.0", - "vimeo/psalm": "4.27.0" + "vimeo/psalm": "4.29.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -467,7 +467,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.4.5" + "source": "https://github.com/doctrine/dbal/tree/3.4.6" }, "funding": [ { @@ -483,7 +483,7 @@ "type": "tidelift" } ], - "time": "2022-09-23T17:48:57+00:00" + "time": "2022-10-21T14:38:43+00:00" }, { "name": "doctrine/deprecations", @@ -622,23 +622,23 @@ }, { "name": "doctrine/inflector", - "version": "2.0.5", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "ade2b3bbfb776f27f0558e26eed43b5d9fe1b392" + "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/ade2b3bbfb776f27f0558e26eed43b5d9fe1b392", - "reference": "ade2b3bbfb776f27f0558e26eed43b5d9fe1b392", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", + "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^10", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.3", @@ -693,7 +693,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.5" + "source": "https://github.com/doctrine/inflector/tree/2.0.6" }, "funding": [ { @@ -709,7 +709,7 @@ "type": "tidelift" } ], - "time": "2022-09-07T09:01:28+00:00" + "time": "2022-10-20T09:10:12+00:00" }, { "name": "doctrine/lexer", @@ -1263,16 +1263,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.4.1", + "version": "2.4.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379" + "reference": "3148458748274be1546f8f2809a6c09fe66f44aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/69568e4293f4fa993f3b0e51c9723e1e17c41379", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/3148458748274be1546f8f2809a6c09fe66f44aa", + "reference": "3148458748274be1546f8f2809a6c09fe66f44aa", "shasum": "" }, "require": { @@ -1362,7 +1362,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.1" + "source": "https://github.com/guzzle/psr7/tree/2.4.2" }, "funding": [ { @@ -1378,7 +1378,7 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:45:39+00:00" + "time": "2022-10-25T13:49:28+00:00" }, { "name": "hashids/hashids", @@ -1513,16 +1513,16 @@ }, { "name": "laravel/framework", - "version": "v9.36.3", + "version": "v9.37.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "80ba0561b3682b96743e1c152fde0698bbdb2412" + "reference": "0c9675abf6d966e834b2ebeca3319f524e07a330" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/80ba0561b3682b96743e1c152fde0698bbdb2412", - "reference": "80ba0561b3682b96743e1c152fde0698bbdb2412", + "url": "https://api.github.com/repos/laravel/framework/zipball/0c9675abf6d966e834b2ebeca3319f524e07a330", + "reference": "0c9675abf6d966e834b2ebeca3319f524e07a330", "shasum": "" }, "require": { @@ -1534,7 +1534,7 @@ "fruitcake/php-cors": "^1.2", "laravel/serializable-closure": "^1.2.2", "league/commonmark": "^2.2", - "league/flysystem": "^3.0.16", + "league/flysystem": "^3.8.0", "monolog/monolog": "^2.0", "nesbot/carbon": "^2.62.1", "nunomaduro/termwind": "^1.13", @@ -1695,7 +1695,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-10-19T13:23:53+00:00" + "time": "2022-10-25T15:43:46+00:00" }, { "name": "laravel/helpers", @@ -2332,16 +2332,16 @@ }, { "name": "league/flysystem", - "version": "3.9.0", + "version": "3.10.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "60f3760352fe08e918bc3b1acae4e91af092ebe1" + "reference": "b9bd194b016114d6ff6765c09d40c7d427e4e3f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/60f3760352fe08e918bc3b1acae4e91af092ebe1", - "reference": "60f3760352fe08e918bc3b1acae4e91af092ebe1", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b9bd194b016114d6ff6765c09d40c7d427e4e3f6", + "reference": "b9bd194b016114d6ff6765c09d40c7d427e4e3f6", "shasum": "" }, "require": { @@ -2357,7 +2357,7 @@ }, "require-dev": { "async-aws/s3": "^1.5", - "async-aws/simple-s3": "^1.0", + "async-aws/simple-s3": "^1.1", "aws/aws-sdk-php": "^3.198.1", "composer/semver": "^3.0", "ext-fileinfo": "*", @@ -2403,7 +2403,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.9.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.10.2" }, "funding": [ { @@ -2419,7 +2419,7 @@ "type": "tidelift" } ], - "time": "2022-10-18T21:02:43+00:00" + "time": "2022-10-25T07:01:47+00:00" }, { "name": "league/flysystem-aws-s3-v3", @@ -3476,16 +3476,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.16", + "version": "3.0.17", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "7181378909ed8890be4db53d289faac5b77f8b05" + "reference": "dbc2307d5c69aeb22db136c52e91130d7f2ca761" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7181378909ed8890be4db53d289faac5b77f8b05", - "reference": "7181378909ed8890be4db53d289faac5b77f8b05", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/dbc2307d5c69aeb22db136c52e91130d7f2ca761", + "reference": "dbc2307d5c69aeb22db136c52e91130d7f2ca761", "shasum": "" }, "require": { @@ -3566,7 +3566,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.16" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.17" }, "funding": [ { @@ -3582,7 +3582,7 @@ "type": "tidelift" } ], - "time": "2022-09-05T18:03:08+00:00" + "time": "2022-10-24T10:51:50+00:00" }, { "name": "pragmarx/google2fa", @@ -8725,6 +8725,66 @@ ], "time": "2022-10-19T21:46:50+00:00" }, + { + "name": "laravel/sail", + "version": "v1.16.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "7d1ed5f856ec8b9708712e3fc0708fcabe114659" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/7d1ed5f856ec8b9708712e3fc0708fcabe114659", + "reference": "7d1ed5f856ec8b9708712e3fc0708fcabe114659", + "shasum": "" + }, + "require": { + "illuminate/console": "^8.0|^9.0", + "illuminate/contracts": "^8.0|^9.0", + "illuminate/support": "^8.0|^9.0", + "php": "^7.3|^8.0" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2022-09-28T13:13:22+00:00" + }, { "name": "mockery/mockery", "version": "1.5.1", @@ -10954,16 +11014,16 @@ }, { "name": "spatie/laravel-ignition", - "version": "1.5.2", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "f2336fc79d99aab5cf27fa4aebe5e9c9ecf3808a" + "reference": "c21309ebf6657e0c38083afac8af9baa12885676" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/f2336fc79d99aab5cf27fa4aebe5e9c9ecf3808a", - "reference": "f2336fc79d99aab5cf27fa4aebe5e9c9ecf3808a", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/c21309ebf6657e0c38083afac8af9baa12885676", + "reference": "c21309ebf6657e0c38083afac8af9baa12885676", "shasum": "" }, "require": { @@ -11040,7 +11100,7 @@ "type": "github" } ], - "time": "2022-10-14T12:24:21+00:00" + "time": "2022-10-25T08:38:04+00:00" }, { "name": "symfony/filesystem", @@ -11291,7 +11351,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.0.2 || ^8.1", + "php": "^8.0.2 || ^8.1 || ^8.2", "ext-json": "*", "ext-mbstring": "*", "ext-pdo": "*", diff --git a/phpunit.xml b/phpunit.xml index d02831cf4c..efc42d687c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -22,7 +22,7 @@ - + From c068f57e4e3d8fb6e25d504edec87404fb525330 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 7 Nov 2022 00:15:12 +0200 Subject: [PATCH 09/95] fix(forge): validate only input and not length (#4528) Only allows digits, dots and dashes in the input, which is what Forge versions consists of. Removes arbitrary char limit. --- database/Seeders/eggs/minecraft/egg-forge-minecraft.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json index fbb097f068..189cafad98 100644 --- a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json +++ b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json @@ -4,7 +4,7 @@ "version": "PTDL_v2", "update_url": null }, - "exported_at": "2022-06-17T08:11:14+03:00", + "exported_at": "2022-11-06T06:33:01-05:00", "name": "Forge Minecraft", "author": "support@pterodactyl.io", "description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.", @@ -67,12 +67,12 @@ }, { "name": "Forge Version", - "description": "Gets an exact version.\r\n\r\nEx. 1.15.2-31.2.4\r\n\r\nOverrides MC_VERSION and BUILD_TYPE. If it fails to download the server files it will fail to install.", + "description": "The full exact version.\r\n\r\nEx. 1.15.2-31.2.4\r\n\r\nOverrides MC_VERSION and BUILD_TYPE. If it fails to download the server files it will fail to install.", "env_variable": "FORGE_VERSION", "default_value": "", "user_viewable": true, "user_editable": true, - "rules": "nullable|string|max:25", + "rules": "nullable|regex:\/^[0-9\\.\\-]+$\/", "field_type": "text" } ] From 9b218f219062fedf4ff318ebe966da10e8421f79 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Sun, 6 Nov 2022 17:24:33 -0500 Subject: [PATCH 10/95] URL encode password in JDBC connection string (#4527) --- resources/scripts/components/server/databases/DatabaseRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/scripts/components/server/databases/DatabaseRow.tsx b/resources/scripts/components/server/databases/DatabaseRow.tsx index 1b486959ed..9d2c05a73c 100644 --- a/resources/scripts/components/server/databases/DatabaseRow.tsx +++ b/resources/scripts/components/server/databases/DatabaseRow.tsx @@ -35,7 +35,7 @@ export default ({ database, className }: Props) => { const removeDatabase = ServerContext.useStoreActions((actions) => actions.databases.removeDatabase); const jdbcConnectionString = `jdbc:mysql://${database.username}${ - database.password ? `:${database.password}` : '' + database.password ? `:${encodeURIComponent(database.password)}` : '' }@${database.connectionString}/${database.name}`; const schema = object().shape({ From 4032481a4f2f62e34b12f2e33f462559f73776de Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Sun, 6 Nov 2022 17:42:48 -0500 Subject: [PATCH 11/95] Update validation rules for remote activity logs (#4526) --- app/Http/Requests/Api/Remote/ActivityEventRequest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Requests/Api/Remote/ActivityEventRequest.php b/app/Http/Requests/Api/Remote/ActivityEventRequest.php index 32ad11ad3f..43f527dfa6 100644 --- a/app/Http/Requests/Api/Remote/ActivityEventRequest.php +++ b/app/Http/Requests/Api/Remote/ActivityEventRequest.php @@ -17,11 +17,11 @@ public function rules(): array return [ 'data' => ['required', 'array'], 'data.*' => ['array'], - 'data.*.user' => ['present', 'uuid'], + 'data.*.user' => ['sometimes', 'nullable', 'uuid'], 'data.*.server' => ['required', 'uuid'], 'data.*.event' => ['required', 'string'], 'data.*.metadata' => ['present', 'nullable', 'array'], - 'data.*.ip' => ['present', 'ip'], + 'data.*.ip' => ['sometimes', 'nullable', 'ip'], 'data.*.timestamp' => ['required', 'string'], ]; } From 032e4f2e31a9de60be734702d1f158ef8d07ebf6 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Mon, 7 Nov 2022 00:02:30 +0100 Subject: [PATCH 12/95] Apply node maintenance mode to servers (#4421) --- .../Http/Server/ServerStateConflictException.php | 2 ++ .../Api/Client/Server/AuthenticateServerAccess.php | 2 +- .../Requests/Api/Application/Nodes/StoreNodeRequest.php | 1 + app/Models/Node.php | 5 +++++ app/Models/Server.php | 1 + app/Transformers/Api/Client/ServerTransformer.php | 1 + resources/scripts/api/server/getServer.ts | 2 ++ .../scripts/components/server/ConflictStateRenderer.tsx | 9 +++++++++ .../components/server/console/ServerConsoleContainer.tsx | 7 +++++-- resources/scripts/state/server/index.ts | 2 +- resources/views/admin/nodes/view/index.blade.php | 2 +- 11 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/Exceptions/Http/Server/ServerStateConflictException.php b/app/Exceptions/Http/Server/ServerStateConflictException.php index f0eb096b16..ea6a60a559 100644 --- a/app/Exceptions/Http/Server/ServerStateConflictException.php +++ b/app/Exceptions/Http/Server/ServerStateConflictException.php @@ -17,6 +17,8 @@ public function __construct(Server $server, Throwable $previous = null) $message = 'This server is currently in an unsupported state, please try again later.'; if ($server->isSuspended()) { $message = 'This server is currently suspended and the functionality requested is unavailable.'; + } elseif ($server->node->isUnderMaintenance()) { + $message = 'The node of this server is currently under maintenance and the functionality requested is unavailable.'; } elseif (!$server->isInstalled()) { $message = 'This server has not yet completed its installation process, please try again later.'; } elseif ($server->status === Server::STATUS_RESTORING_BACKUP) { diff --git a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php index 40a4d0cf1c..358fb6d572 100644 --- a/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php +++ b/app/Http/Middleware/Api/Client/Server/AuthenticateServerAccess.php @@ -53,7 +53,7 @@ public function handle(Request $request, Closure $next): mixed // Still allow users to get information about their server if it is installing or // being transferred. if (!$request->routeIs('api:client:server.view')) { - if ($server->isSuspended() && !$request->routeIs('api:client:server.resources')) { + if (($server->isSuspended() || $server->node->isUnderMaintenance()) && !$request->routeIs('api:client:server.resources')) { throw $exception; } if (!$user->root_admin || !$request->routeIs($this->except)) { diff --git a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php index bc559083e5..4fe7054485 100644 --- a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php @@ -24,6 +24,7 @@ public function rules(array $rules = null): array 'fqdn', 'scheme', 'behind_proxy', + 'maintenance_mode', 'memory', 'memory_overallocate', 'disk', diff --git a/app/Models/Node.php b/app/Models/Node.php index 37aec760d6..62ec828712 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -186,6 +186,11 @@ public function getDecryptedKey(): string ); } + public function isUnderMaintenance(): bool + { + return $this->maintenance_mode; + } + public function mounts(): HasManyThrough { return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id'); diff --git a/app/Models/Server.php b/app/Models/Server.php index 5ad99151a3..d7cc649c09 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -354,6 +354,7 @@ public function validateCurrentState() { if ( $this->isSuspended() || + $this->node->isUnderMaintenance() || !$this->isInstalled() || $this->status === self::STATUS_RESTORING_BACKUP || !is_null($this->transfer) diff --git a/app/Transformers/Api/Client/ServerTransformer.php b/app/Transformers/Api/Client/ServerTransformer.php index 8ae4ed4a6a..9f7bce958a 100644 --- a/app/Transformers/Api/Client/ServerTransformer.php +++ b/app/Transformers/Api/Client/ServerTransformer.php @@ -43,6 +43,7 @@ public function transform(Server $server): array 'uuid' => $server->uuid, 'name' => $server->name, 'node' => $server->node->name, + 'is_node_under_maintenance' => $server->node->isUnderMaintenance(), 'sftp_details' => [ 'ip' => $server->node->fqdn, 'port' => $server->node->daemonSFTP, diff --git a/resources/scripts/api/server/getServer.ts b/resources/scripts/api/server/getServer.ts index 5ed92a4270..d2aa2e0543 100644 --- a/resources/scripts/api/server/getServer.ts +++ b/resources/scripts/api/server/getServer.ts @@ -17,6 +17,7 @@ export interface Server { uuid: string; name: string; node: string; + isNodeUnderMaintenance: boolean; status: ServerStatus; sftpDetails: { ip: string; @@ -50,6 +51,7 @@ export const rawDataToServerObject = ({ attributes: data }: FractalResponseData) uuid: data.uuid, name: data.name, node: data.node, + isNodeUnderMaintenance: data.is_node_under_maintenance, status: data.status, invocation: data.invocation, dockerImage: data.docker_image, diff --git a/resources/scripts/components/server/ConflictStateRenderer.tsx b/resources/scripts/components/server/ConflictStateRenderer.tsx index 70475a4eef..95e70bbaa3 100644 --- a/resources/scripts/components/server/ConflictStateRenderer.tsx +++ b/resources/scripts/components/server/ConflictStateRenderer.tsx @@ -8,6 +8,9 @@ import ServerRestoreSvg from '@/assets/images/server_restore.svg'; export default () => { const status = ServerContext.useStoreState((state) => state.server.data?.status || null); const isTransferring = ServerContext.useStoreState((state) => state.server.data?.isTransferring || false); + const isNodeUnderMaintenance = ServerContext.useStoreState( + (state) => state.server.data?.isNodeUnderMaintenance || false + ); return status === 'installing' || status === 'install_failed' ? ( { image={ServerErrorSvg} message={'This server is suspended and cannot be accessed.'} /> + ) : isNodeUnderMaintenance ? ( + ) : ( { const isInstalling = ServerContext.useStoreState((state) => state.server.isInstalling); const isTransferring = ServerContext.useStoreState((state) => state.server.data!.isTransferring); const eggFeatures = ServerContext.useStoreState((state) => state.server.data!.eggFeatures, isEqual); + const isNodeUnderMaintenance = ServerContext.useStoreState((state) => state.server.data!.isNodeUnderMaintenance); return ( - {(isInstalling || isTransferring) && ( + {(isNodeUnderMaintenance || isInstalling || isTransferring) && ( - {isInstalling + {isNodeUnderMaintenance + ? 'The node of this server is currently under maintenance and all actions are unavailable.' + : isInstalling ? 'This server is currently running its installation process and most actions are unavailable.' : 'This server is currently being transferred to another node and all actions are unavailable.'} diff --git a/resources/scripts/state/server/index.ts b/resources/scripts/state/server/index.ts index 8d8eba749f..f9806b6498 100644 --- a/resources/scripts/state/server/index.ts +++ b/resources/scripts/state/server/index.ts @@ -30,7 +30,7 @@ const server: ServerDataStore = { return false; } - return state.data.status !== null || state.data.isTransferring; + return state.data.status !== null || state.data.isTransferring || state.data.isNodeUnderMaintenance; }), isInstalling: computed((state) => { diff --git a/resources/views/admin/nodes/view/index.blade.php b/resources/views/admin/nodes/view/index.blade.php index 9ef461076a..ce90b30b2d 100644 --- a/resources/views/admin/nodes/view/index.blade.php +++ b/resources/views/admin/nodes/view/index.blade.php @@ -93,7 +93,7 @@
@if($node->maintenance_mode)
-
+
This node is under From df2402b54f8db10e8319b4250a2960c97e22b296 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 14 Nov 2022 18:25:07 -0700 Subject: [PATCH 13/95] Streaming Transfers (#4548) --- .../Servers/ServerTransferController.php | 42 ++++++++++------ .../Servers/ServerTransferController.php | 48 +------------------ .../Wings/DaemonTransferRepository.php | 12 ++--- app/Services/Servers/TransferService.php | 27 ----------- .../components/server/TransferListener.tsx | 8 ++-- .../components/server/console/Console.tsx | 7 --- 6 files changed, 38 insertions(+), 106 deletions(-) delete mode 100644 app/Services/Servers/TransferService.php diff --git a/app/Http/Controllers/Admin/Servers/ServerTransferController.php b/app/Http/Controllers/Admin/Servers/ServerTransferController.php index 0962174186..8941ce10c5 100644 --- a/app/Http/Controllers/Admin/Servers/ServerTransferController.php +++ b/app/Http/Controllers/Admin/Servers/ServerTransferController.php @@ -2,15 +2,17 @@ namespace Pterodactyl\Http\Controllers\Admin\Servers; +use Carbon\CarbonImmutable; use Illuminate\Http\Request; use Pterodactyl\Models\Server; use Illuminate\Http\RedirectResponse; use Prologue\Alerts\AlertsMessageBag; use Pterodactyl\Models\ServerTransfer; +use Illuminate\Database\ConnectionInterface; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Servers\TransferService; +use Pterodactyl\Services\Nodes\NodeJWTService; use Pterodactyl\Repositories\Eloquent\NodeRepository; -use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository; +use Pterodactyl\Repositories\Wings\DaemonTransferRepository; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; class ServerTransferController extends Controller @@ -21,9 +23,10 @@ class ServerTransferController extends Controller public function __construct( private AlertsMessageBag $alert, private AllocationRepositoryInterface $allocationRepository, - private NodeRepository $nodeRepository, - private TransferService $transferService, - private DaemonConfigurationRepository $daemonConfigurationRepository + private ConnectionInterface $connection, + private DaemonTransferRepository $daemonTransferRepository, + private NodeJWTService $nodeJWTService, + private NodeRepository $nodeRepository ) { } @@ -46,12 +49,15 @@ public function transfer(Request $request, Server $server): RedirectResponse // Check if the node is viable for the transfer. $node = $this->nodeRepository->getNodeWithResourceUsage($node_id); - if ($node->isViable($server->memory, $server->disk)) { - // Check if the selected daemon is online. - $this->daemonConfigurationRepository->setNode($node)->getSystemInformation(); + if (!$node->isViable($server->memory, $server->disk)) { + $this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash(); + + return redirect()->route('admin.servers.view.manage', $server->id); + } - $server->validateTransferState(); + $server->validateTransferState(); + $this->connection->transaction(function () use ($server, $node_id, $allocation_id, $additional_allocations) { // Create a new ServerTransfer entry. $transfer = new ServerTransfer(); @@ -68,13 +74,19 @@ public function transfer(Request $request, Server $server): RedirectResponse // Add the allocations to the server, so they cannot be automatically assigned while the transfer is in progress. $this->assignAllocationsToServer($server, $node_id, $allocation_id, $additional_allocations); - // Request an archive from the server's current daemon. (this also checks if the daemon is online) - $this->transferService->requestArchive($server); + // Generate a token for the destination node that the source node can use to authenticate with. + $token = $this->nodeJWTService + ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setSubject($server->uuid) + ->handle($transfer->newNode, $server->uuid, 'sha256'); - $this->alert->success(trans('admin/server.alerts.transfer_started'))->flash(); - } else { - $this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash(); - } + // Notify the source node of the pending outgoing transfer. + $this->daemonTransferRepository->setServer($server)->notify($transfer->newNode, $token); + + return $transfer; + }); + + $this->alert->success(trans('admin/server.alerts.transfer_started'))->flash(); return redirect()->route('admin.servers.view.manage', $server->id); } diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php index 6f49d66e01..72153bf252 100644 --- a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php +++ b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php @@ -2,8 +2,6 @@ namespace Pterodactyl\Http\Controllers\Api\Remote\Servers; -use Carbon\CarbonImmutable; -use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Http\JsonResponse; use Pterodactyl\Models\Allocation; @@ -11,10 +9,8 @@ use Pterodactyl\Models\ServerTransfer; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Nodes\NodeJWTService; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Wings\DaemonServerRepository; -use Pterodactyl\Repositories\Wings\DaemonTransferRepository; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class ServerTransferController extends Controller @@ -25,52 +21,10 @@ class ServerTransferController extends Controller public function __construct( private ConnectionInterface $connection, private ServerRepository $repository, - private DaemonServerRepository $daemonServerRepository, - private DaemonTransferRepository $daemonTransferRepository, - private NodeJWTService $jwtService + private DaemonServerRepository $daemonServerRepository ) { } - /** - * The daemon notifies us about the archive status. - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Throwable - */ - public function archive(Request $request, string $uuid): JsonResponse - { - $server = $this->repository->getByUuid($uuid); - - // Unsuspend the server and don't continue the transfer. - if (!$request->input('successful')) { - return $this->processFailedTransfer($server->transfer); - } - - $this->connection->transaction(function () use ($server) { - // This token is used by the new node the server is being transferred to. It allows - // that node to communicate with the old node during the process to initiate the - // actual file transfer. - $token = $this->jwtService - ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) - ->setSubject($server->uuid) - ->handle($server->node, $server->uuid, 'sha256'); - - // Update the archived field on the transfer to make clients connect to the websocket - // on the new node to be able to receive transfer logs. - $server->transfer->forceFill(['archived' => true])->saveOrFail(); - - // On the daemon transfer repository, make sure to set the node after the server - // because setServer() tells the repository to use the server's node and not the one - // we want to specify. - $this->daemonTransferRepository - ->setServer($server) - ->setNode($server->transfer->newNode) - ->notify($server, $token); - }); - - return new JsonResponse([], Response::HTTP_NO_CONTENT); - } - /** * The daemon notifies us about a transfer failure. * diff --git a/app/Repositories/Wings/DaemonTransferRepository.php b/app/Repositories/Wings/DaemonTransferRepository.php index 3939a47cd1..9c8745232b 100644 --- a/app/Repositories/Wings/DaemonTransferRepository.php +++ b/app/Repositories/Wings/DaemonTransferRepository.php @@ -2,8 +2,8 @@ namespace Pterodactyl\Repositories\Wings; +use Pterodactyl\Models\Node; use Lcobucci\JWT\Token\Plain; -use Pterodactyl\Models\Server; use GuzzleHttp\Exception\GuzzleException; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; @@ -12,16 +12,16 @@ class DaemonTransferRepository extends DaemonRepository /** * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ - public function notify(Server $server, Plain $token): void + public function notify(Node $targetNode, Plain $token): void { try { - $this->getHttpClient()->post('/api/transfer', [ + $this->getHttpClient()->post(sprintf('/api/servers/%s/transfer', $this->server->uuid), [ 'json' => [ - 'server_id' => $server->uuid, - 'url' => $server->node->getConnectionAddress() . sprintf('/api/servers/%s/archive', $server->uuid), + 'server_id' => $this->server->uuid, + 'url' => $targetNode->getConnectionAddress() . '/api/transfers', 'token' => 'Bearer ' . $token->toString(), 'server' => [ - 'uuid' => $server->uuid, + 'uuid' => $this->server->uuid, 'start_on_completion' => false, ], ], diff --git a/app/Services/Servers/TransferService.php b/app/Services/Servers/TransferService.php deleted file mode 100644 index 24ef0a5881..0000000000 --- a/app/Services/Servers/TransferService.php +++ /dev/null @@ -1,27 +0,0 @@ -daemonServerRepository->setServer($server)->requestArchive(); - } -} diff --git a/resources/scripts/components/server/TransferListener.tsx b/resources/scripts/components/server/TransferListener.tsx index 64460aa699..4d94217453 100644 --- a/resources/scripts/components/server/TransferListener.tsx +++ b/resources/scripts/components/server/TransferListener.tsx @@ -7,19 +7,19 @@ const TransferListener = () => { const getServer = ServerContext.useStoreActions((actions) => actions.server.getServer); const setServerFromState = ServerContext.useStoreActions((actions) => actions.server.setServerFromState); - // Listen for the transfer status event so we can update the state of the server. + // Listen for the transfer status event, so we can update the state of the server. useWebsocketEvent(SocketEvent.TRANSFER_STATUS, (status: string) => { - if (status === 'starting') { + if (status === 'pending' || status === 'processing') { setServerFromState((s) => ({ ...s, isTransferring: true })); return; } - if (status === 'failure') { + if (status === 'failed') { setServerFromState((s) => ({ ...s, isTransferring: false })); return; } - if (status !== 'success') { + if (status !== 'completed') { return; } diff --git a/resources/scripts/components/server/console/Console.tsx b/resources/scripts/components/server/console/Console.tsx index 9468d0a56a..e3cb43ab26 100644 --- a/resources/scripts/components/server/console/Console.tsx +++ b/resources/scripts/components/server/console/Console.tsx @@ -76,13 +76,6 @@ export default () => { case 'failure': terminal.writeln(TERMINAL_PRELUDE + 'Transfer has failed.\u001b[0m'); return; - - // Sent by the source node whenever the server was archived successfully. - case 'archive': - terminal.writeln( - TERMINAL_PRELUDE + - 'Server has been archived successfully, attempting connection to target node..\u001b[0m' - ); } }; From 897ca48ded10743febb0e1f3895ae7429a17ff8d Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 14 Nov 2022 18:28:02 -0700 Subject: [PATCH 14/95] github: re-enable blank issues --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 09011627c5..fee5ad9487 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: false +blank_issues_enabled: true contact_links: - name: Installation Help url: https://discord.gg/pterodactyl From c1584d9a5bc8e8a284ddd7d3de7e38015401651b Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 14 Nov 2022 20:31:03 -0700 Subject: [PATCH 15/95] Update CHANGELOG.md --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf74334192..371916bc96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,17 @@ 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. -## [Unreleased] +## v1.11.0-rc.1 ### Changed * Changed minimum PHP version is now 8.0 instead of `7.4`. * Upgraded from Laravel 8 to Laravel 9. +* This release requires Wings v1.11.x in order for Server Transfers to work. + +### Fixed +* Node maintenance mode now properly blocks access to servers. +* Fixed the length validation on the Minecraft Forge egg. +* Fixed the password in the JDBC string not being properly URL encoded. +* Fixed an issue where Wings would throw a validation error while attempting to upload activity logs. ## v1.10.4 ### Fixed From 2f4a60c9615af0f06b025c3936ade89dc87ba974 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 21 Nov 2022 13:10:00 -0700 Subject: [PATCH 16/95] ui(admin): change `MB` suffixes to `MiB` Closes #4542 --- resources/views/admin/nodes/index.blade.php | 4 ++-- resources/views/admin/nodes/new.blade.php | 4 ++-- resources/views/admin/nodes/view/index.blade.php | 4 ++-- resources/views/admin/nodes/view/settings.blade.php | 6 +++--- resources/views/admin/servers/new.blade.php | 6 +++--- resources/views/admin/servers/view/build.blade.php | 6 +++--- resources/views/admin/servers/view/index.blade.php | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/views/admin/nodes/index.blade.php b/resources/views/admin/nodes/index.blade.php index a4747b3fcd..3b3a3fa48f 100644 --- a/resources/views/admin/nodes/index.blade.php +++ b/resources/views/admin/nodes/index.blade.php @@ -53,8 +53,8 @@ {!! $node->maintenance_mode ? ' ' : '' !!}{{ $node->name }} {{ $node->location->short }} - {{ $node->memory }} MB - {{ $node->disk }} MB + {{ $node->memory }} MiB + {{ $node->disk }} MiB {{ $node->servers_count }} diff --git a/resources/views/admin/nodes/new.blade.php b/resources/views/admin/nodes/new.blade.php index 3e10be5a14..b10d08a762 100644 --- a/resources/views/admin/nodes/new.blade.php +++ b/resources/views/admin/nodes/new.blade.php @@ -110,7 +110,7 @@
- MB + MiB
@@ -129,7 +129,7 @@
- MB + MiB
diff --git a/resources/views/admin/nodes/view/index.blade.php b/resources/views/admin/nodes/view/index.blade.php index ce90b30b2d..2d0bb32874 100644 --- a/resources/views/admin/nodes/view/index.blade.php +++ b/resources/views/admin/nodes/view/index.blade.php @@ -107,7 +107,7 @@
Disk Space Allocated - {{ $stats['disk']['value'] }} / {{ $stats['disk']['max'] }} MB + {{ $stats['disk']['value'] }} / {{ $stats['disk']['max'] }} MiB
@@ -119,7 +119,7 @@
Memory Allocated - {{ $stats['memory']['value'] }} / {{ $stats['memory']['max'] }} MB + {{ $stats['memory']['value'] }} / {{ $stats['memory']['max'] }} MiB
diff --git a/resources/views/admin/nodes/view/settings.blade.php b/resources/views/admin/nodes/view/settings.blade.php index e4848aca34..bfc9d8caf7 100644 --- a/resources/views/admin/nodes/view/settings.blade.php +++ b/resources/views/admin/nodes/view/settings.blade.php @@ -132,7 +132,7 @@
- MB + MiB
@@ -151,7 +151,7 @@
- MB + MiB
@@ -177,7 +177,7 @@
- MB + MiB

Enter the maximum size of files that can be uploaded through the web-based file manager.

diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index bbe779ce6c..4198634f1e 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -170,7 +170,7 @@
- MB + MiB

The maximum amount of memory allowed for this container. Setting this to 0 will allow unlimited memory in a container.

@@ -181,7 +181,7 @@
- MB + MiB

Setting this to 0 will disable swap space on this server. Setting to -1 will allow unlimited swap.

@@ -194,7 +194,7 @@
- MB + MiB

This server will not be allowed to boot if it is using more than this amount of space. If a server goes over this limit while running it will be safely stopped and locked until enough space is available. Set to 0 to allow unlimited disk usage.

diff --git a/resources/views/admin/servers/view/build.blade.php b/resources/views/admin/servers/view/build.blade.php index f7ba9a2d2a..655ea36af3 100644 --- a/resources/views/admin/servers/view/build.blade.php +++ b/resources/views/admin/servers/view/build.blade.php @@ -43,7 +43,7 @@
- MB + MiB

The maximum amount of memory allowed for this container. Setting this to 0 will allow unlimited memory in a container.

@@ -51,7 +51,7 @@
- MB + MiB

Setting this to 0 will disable swap space on this server. Setting to -1 will allow unlimited swap.

@@ -59,7 +59,7 @@
- MB + MiB

This server will not be allowed to boot if it is using more than this amount of space. If a server goes over this limit while running it will be safely stopped and locked until enough space is available. Set to 0 to allow unlimited disk usage.

diff --git a/resources/views/admin/servers/view/index.blade.php b/resources/views/admin/servers/view/index.blade.php index e9b95f9856..f94c6d42fc 100644 --- a/resources/views/admin/servers/view/index.blade.php +++ b/resources/views/admin/servers/view/index.blade.php @@ -78,7 +78,7 @@ @if($server->memory === 0) Unlimited @else - {{ $server->memory }}MB + {{ $server->memory }}MiB @endif / @if($server->swap === 0) @@ -86,7 +86,7 @@ @elseif($server->swap === -1) Unlimited @else - {{ $server->swap }}MB + {{ $server->swap }}MiB @endif From c3521e022161606c115d8a900040cf2cae190a96 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 21 Nov 2022 13:15:49 -0700 Subject: [PATCH 17/95] api(server): fix undefined header --- app/Repositories/Wings/DaemonFileRepository.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Repositories/Wings/DaemonFileRepository.php b/app/Repositories/Wings/DaemonFileRepository.php index eb36496412..3bd8978813 100644 --- a/app/Repositories/Wings/DaemonFileRepository.php +++ b/app/Repositories/Wings/DaemonFileRepository.php @@ -2,6 +2,7 @@ namespace Pterodactyl\Repositories\Wings; +use Illuminate\Support\Arr; use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Psr\Http\Message\ResponseInterface; @@ -35,8 +36,7 @@ public function getContent(string $path, int $notLargerThan = null): string throw new DaemonConnectionException($exception); } - $length = (int) $response->getHeader('Content-Length')[0] ?? 0; - + $length = (int) Arr::get($response->getHeader('Content-Length'), 0, 0); if ($notLargerThan && $length > $notLargerThan) { throw new FileSizeTooLargeException(); } From 634c9353e309c603064530824dfbb3c870a1f7b6 Mon Sep 17 00:00:00 2001 From: Devonte W Date: Mon, 21 Nov 2022 20:28:46 +0000 Subject: [PATCH 18/95] fix(transformers): force object type for properties (#4544) --- app/Transformers/Api/Client/ActivityLogTransformer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Transformers/Api/Client/ActivityLogTransformer.php b/app/Transformers/Api/Client/ActivityLogTransformer.php index 849fdc865b..57c8ac30ed 100644 --- a/app/Transformers/Api/Client/ActivityLogTransformer.php +++ b/app/Transformers/Api/Client/ActivityLogTransformer.php @@ -47,10 +47,10 @@ public function includeActor(ActivityLog $model) * Transforms any array values in the properties into a countable field for easier * use within the translation outputs. */ - protected function properties(ActivityLog $model): array + protected function properties(ActivityLog $model): object { if (!$model->properties || $model->properties->isEmpty()) { - return []; + return (object) []; } $properties = $model->properties @@ -76,7 +76,7 @@ protected function properties(ActivityLog $model): array $properties = $properties->merge(['count' => $properties->get($keys[0])])->except($keys[0]); } - return $properties->toArray(); + return (object) $properties->toArray(); } /** From 039ad4abf04586011de484ce2942429d0ecbad75 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 21 Nov 2022 13:41:26 -0700 Subject: [PATCH 19/95] api(server): log activity when server description is changed --- .../Api/Client/Servers/SettingsController.php | 16 ++++++++++++---- app/Services/Activity/ActivityLogService.php | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/SettingsController.php b/app/Http/Controllers/Api/Client/Servers/SettingsController.php index d1377d67a3..6e64518368 100644 --- a/app/Http/Controllers/Api/Client/Servers/SettingsController.php +++ b/app/Http/Controllers/Api/Client/Servers/SettingsController.php @@ -34,14 +34,22 @@ public function __construct( */ public function rename(RenameServerRequest $request, Server $server): JsonResponse { + $name = $request->input('name'); + $description = $request->input('description') ?? ''; $this->repository->update($server->id, [ - 'name' => $request->input('name'), - 'description' => $request->input('description') ?? '', + 'name' => $name, + 'description' => $description, ]); - if ($server->name !== $request->input('name')) { + if ($server->name !== $name) { Activity::event('server:settings.rename') - ->property(['old' => $server->name, 'new' => $request->input('name')]) + ->property(['old' => $server->name, 'new' => $name]) + ->log(); + } + + if ($server->description !== $description) { + Activity::event('server:settings.description') + ->property(['old' => $server->description, 'new' => $description]) ->log(); } diff --git a/app/Services/Activity/ActivityLogService.php b/app/Services/Activity/ActivityLogService.php index fa4f469369..f863852149 100644 --- a/app/Services/Activity/ActivityLogService.php +++ b/app/Services/Activity/ActivityLogService.php @@ -99,7 +99,7 @@ public function actor(Model $actor): self } /** - * Sets a custom property on the activty log instance. + * Sets a custom property on the activity log instance. * * @param string|array $key * @param mixed $value @@ -115,7 +115,7 @@ public function property($key, $value = null): self } /** - * Attachs the instance request metadata to the activity log event. + * Attaches the instance request metadata to the activity log event. */ public function withRequestMetadata(): self { From a4f6870518f10855a063f19fc4dc69b9e83c070f Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Mon, 21 Nov 2022 15:53:54 -0500 Subject: [PATCH 20/95] server: track reinstall failures differently from initial install failures (#4531) --- .../Api/Remote/Servers/ServerInstallController.php | 12 +++++++++++- .../Requests/Api/Remote/InstallationDataRequest.php | 1 + app/Models/Server.php | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php b/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php index e788437631..8fcfbe0643 100644 --- a/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php +++ b/app/Http/Controllers/Api/Remote/Servers/ServerInstallController.php @@ -48,8 +48,18 @@ public function index(Request $request, string $uuid): JsonResponse public function store(InstallationDataRequest $request, string $uuid): JsonResponse { $server = $this->repository->getByUuid($uuid); + $status = null; - $status = $request->boolean('successful') ? null : Server::STATUS_INSTALL_FAILED; + // Make sure the type of failure is accurate + if (!$request->boolean('successful')) { + $status = Server::STATUS_INSTALL_FAILED; + + if ($request->boolean('reinstall')) { + $status = Server::STATUS_REINSTALL_FAILED; + } + } + + // Keep the server suspended if it's already suspended if ($server->status === Server::STATUS_SUSPENDED) { $status = Server::STATUS_SUSPENDED; } diff --git a/app/Http/Requests/Api/Remote/InstallationDataRequest.php b/app/Http/Requests/Api/Remote/InstallationDataRequest.php index c5d1973ec4..13b3e77d7a 100644 --- a/app/Http/Requests/Api/Remote/InstallationDataRequest.php +++ b/app/Http/Requests/Api/Remote/InstallationDataRequest.php @@ -15,6 +15,7 @@ public function rules(): array { return [ 'successful' => 'present|boolean', + 'reinstall' => 'sometimes|boolean', ]; } } diff --git a/app/Models/Server.php b/app/Models/Server.php index d7cc649c09..5f2d6a49ef 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -115,6 +115,7 @@ class Server extends Model public const STATUS_INSTALLING = 'installing'; public const STATUS_INSTALL_FAILED = 'install_failed'; + public const STATUS_REINSTALL_FAILED = 'reinstall_failed'; public const STATUS_SUSPENDED = 'suspended'; public const STATUS_RESTORING_BACKUP = 'restoring_backup'; From df9a7f71f99a7afcd93de02b6b96fdf2ecc7dabf Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 21 Nov 2022 12:58:55 -0800 Subject: [PATCH 21/95] Support canceling file uploads (#4441) Closes #4440 --- package.json | 1 + .../components/elements/dialog/Dialog.tsx | 2 +- .../server/files/FileManagerStatus.tsx | 41 +++++++++++------ .../components/server/files/UploadButton.tsx | 45 +++++++++---------- resources/scripts/state/server/files.ts | 35 ++++++++++----- yarn.lock | 18 ++++++++ 6 files changed, 92 insertions(+), 50 deletions(-) diff --git a/package.json b/package.json index 562c22f7e3..e716485cb0 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@headlessui/react": "^1.6.4", "@heroicons/react": "^1.0.6", "@hot-loader/react-dom": "^16.14.0", + "@preact/signals-react": "^1.2.1", "@tailwindcss/forms": "^0.5.2", "@tailwindcss/line-clamp": "^0.4.0", "axios": "^0.27.2", diff --git a/resources/scripts/components/elements/dialog/Dialog.tsx b/resources/scripts/components/elements/dialog/Dialog.tsx index b280f09448..bb35fe8fb8 100644 --- a/resources/scripts/components/elements/dialog/Dialog.tsx +++ b/resources/scripts/components/elements/dialog/Dialog.tsx @@ -90,7 +90,7 @@ export default ({ >
{iconPosition === 'container' && icon} -
+
{iconPosition !== 'container' && icon}
diff --git a/resources/scripts/components/server/files/FileManagerStatus.tsx b/resources/scripts/components/server/files/FileManagerStatus.tsx index 2e4144d0fd..620e067c17 100644 --- a/resources/scripts/components/server/files/FileManagerStatus.tsx +++ b/resources/scripts/components/server/files/FileManagerStatus.tsx @@ -1,11 +1,12 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect } from 'react'; import { ServerContext } from '@/state/server'; -import { CloudUploadIcon } from '@heroicons/react/solid'; +import { CloudUploadIcon, XIcon } from '@heroicons/react/solid'; import asDialog from '@/hoc/asDialog'; import { Dialog, DialogWrapperContext } from '@/components/elements/dialog'; import { Button } from '@/components/elements/button/index'; import Tooltip from '@/components/elements/tooltip/Tooltip'; import Code from '@/components/elements/Code'; +import { useSignal } from '@preact/signals-react'; const svgProps = { cx: 16, @@ -31,23 +32,34 @@ const Spinner = ({ progress, className }: { progress: number; className?: string const FileUploadList = () => { const { close } = useContext(DialogWrapperContext); + const removeFileUpload = ServerContext.useStoreActions((actions) => actions.files.removeFileUpload); + const clearFileUploads = ServerContext.useStoreActions((actions) => actions.files.clearFileUploads); const uploads = ServerContext.useStoreState((state) => - state.files.uploads.sort((a, b) => a.name.localeCompare(b.name)) + Object.entries(state.files.uploads).sort(([a], [b]) => a.localeCompare(b)) ); return (
- {uploads.map((file) => ( -
+ {uploads.map(([name, file]) => ( +
- {file.name} + {name} +
))} + clearFileUploads()}> + Cancel Uploads + Close
@@ -60,17 +72,17 @@ const FileUploadListDialog = asDialog({ })(FileUploadList); export default () => { - const [open, setOpen] = useState(false); + const open = useSignal(false); - const count = ServerContext.useStoreState((state) => state.files.uploads.length); + const count = ServerContext.useStoreState((state) => Object.keys(state.files.uploads).length); const progress = ServerContext.useStoreState((state) => ({ - uploaded: state.files.uploads.reduce((count, file) => count + file.loaded, 0), - total: state.files.uploads.reduce((count, file) => count + file.total, 0), + uploaded: Object.values(state.files.uploads).reduce((count, file) => count + file.loaded, 0), + total: Object.values(state.files.uploads).reduce((count, file) => count + file.total, 0), })); useEffect(() => { if (count === 0) { - setOpen(false); + open.value = false; } }, [count]); @@ -78,13 +90,16 @@ export default () => { <> {count > 0 && ( - )} - + (open.value = false)} /> ); }; diff --git a/resources/scripts/components/server/files/UploadButton.tsx b/resources/scripts/components/server/files/UploadButton.tsx index 25277456d1..c5cac0e29b 100644 --- a/resources/scripts/components/server/files/UploadButton.tsx +++ b/resources/scripts/components/server/files/UploadButton.tsx @@ -2,7 +2,7 @@ import axios from 'axios'; import getFileUploadUrl from '@/api/server/files/getFileUploadUrl'; import tw from 'twin.macro'; import { Button } from '@/components/elements/button/index'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef } from 'react'; import { ModalMask } from '@/components/elements/Modal'; import Fade from '@/components/elements/Fade'; import useEventListener from '@/plugins/useEventListener'; @@ -12,6 +12,7 @@ import { ServerContext } from '@/state/server'; import { WithClassname } from '@/components/types'; import Portal from '@/components/elements/Portal'; import { CloudUploadIcon } from '@heroicons/react/outline'; +import { useSignal } from '@preact/signals-react'; function isFileOrDirectory(event: DragEvent): boolean { if (!event.dataTransfer?.types) { @@ -23,14 +24,16 @@ function isFileOrDirectory(event: DragEvent): boolean { export default ({ className }: WithClassname) => { const fileUploadInput = useRef(null); - const [timeouts, setTimeouts] = useState([]); - const [visible, setVisible] = useState(false); + + const visible = useSignal(false); + const timeouts = useSignal([]); + const { mutate } = useFileManagerSwr(); const { addError, clearAndAddHttpError } = useFlashKey('files'); const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid); const directory = ServerContext.useStoreState((state) => state.files.directory); - const { clearFileUploads, appendFileUpload, removeFileUpload } = ServerContext.useStoreActions( + const { clearFileUploads, removeFileUpload, pushFileUpload, setUploadProgress } = ServerContext.useStoreActions( (actions) => actions.files ); @@ -40,27 +43,24 @@ export default ({ className }: WithClassname) => { e.preventDefault(); e.stopPropagation(); if (isFileOrDirectory(e)) { - return setVisible(true); + visible.value = true; } }, { capture: true } ); - useEventListener('dragexit', () => setVisible(false), { capture: true }); + useEventListener('dragexit', () => (visible.value = false), { capture: true }); - useEventListener('keydown', () => { - visible && setVisible(false); - }); + useEventListener('keydown', () => (visible.value = false)); useEffect(() => { - return () => timeouts.forEach(clearTimeout); + return () => timeouts.value.forEach(clearTimeout); }, []); const onUploadProgress = (data: ProgressEvent, name: string) => { - appendFileUpload({ name, loaded: data.loaded, total: data.total }); + setUploadProgress({ name, loaded: data.loaded }); if (data.loaded >= data.total) { - const timeout = setTimeout(() => removeFileUpload(name), 500); - setTimeouts((t) => [...t, timeout]); + timeouts.value.push(setTimeout(() => removeFileUpload(name), 500)); } }; @@ -71,23 +71,20 @@ export default ({ className }: WithClassname) => { return addError('Folder uploads are not supported at this time.', 'Error'); } - if (!list.length) { - return; - } - const uploads = list.map((file) => { - appendFileUpload({ name: file.name, loaded: 0, total: file.size }); + const controller = new AbortController(); + pushFileUpload({ name: file.name, data: { abort: controller, loaded: 0, total: file.size } }); + return () => getFileUploadUrl(uuid).then((url) => axios.post( url, { files: file }, { + signal: controller.signal, headers: { 'Content-Type': 'multipart/form-data' }, params: { directory }, - onUploadProgress: (data) => { - onUploadProgress(data, file.name); - }, + onUploadProgress: (data) => onUploadProgress(data, file.name), } ) ); @@ -104,15 +101,15 @@ export default ({ className }: WithClassname) => { return ( <> - + setVisible(false)} + onClick={() => (visible.value = false)} onDragOver={(e) => e.preventDefault()} onDrop={(e) => { e.preventDefault(); e.stopPropagation(); - setVisible(false); + visible.value = false; if (!e.dataTransfer?.files.length) return; onFileSubmission(e.dataTransfer.files); diff --git a/resources/scripts/state/server/files.ts b/resources/scripts/state/server/files.ts index 4a4b7fb93a..7e4786ae4c 100644 --- a/resources/scripts/state/server/files.ts +++ b/resources/scripts/state/server/files.ts @@ -1,31 +1,32 @@ import { action, Action } from 'easy-peasy'; import { cleanDirectoryPath } from '@/helpers'; -export interface FileUpload { - name: string; +export interface FileUploadData { loaded: number; + readonly abort: AbortController; readonly total: number; } export interface ServerFileStore { directory: string; selectedFiles: string[]; - uploads: FileUpload[]; + uploads: Record; setDirectory: Action; setSelectedFiles: Action; appendSelectedFile: Action; removeSelectedFile: Action; + pushFileUpload: Action; + setUploadProgress: Action; clearFileUploads: Action; - appendFileUpload: Action; removeFileUpload: Action; } const files: ServerFileStore = { directory: '/', selectedFiles: [], - uploads: [], + uploads: {}, setDirectory: action((state, payload) => { state.directory = cleanDirectoryPath(payload); @@ -44,19 +45,29 @@ const files: ServerFileStore = { }), clearFileUploads: action((state) => { - state.uploads = []; + Object.values(state.uploads).forEach((upload) => upload.abort.abort()); + + state.uploads = {}; + }), + + pushFileUpload: action((state, payload) => { + state.uploads[payload.name] = payload.data; }), - appendFileUpload: action((state, payload) => { - if (!state.uploads.some(({ name }) => name === payload.name)) { - state.uploads = [...state.uploads, payload]; - } else { - state.uploads = state.uploads.map((file) => (file.name === payload.name ? payload : file)); + setUploadProgress: action((state, { name, loaded }) => { + if (state.uploads[name]) { + state.uploads[name].loaded = loaded; } }), removeFileUpload: action((state, payload) => { - state.uploads = state.uploads.filter(({ name }) => name !== payload); + if (state.uploads[payload]) { + // Abort the request if it is still in flight. If it already completed this is + // a no-op. + state.uploads[payload].abort.abort(); + + delete state.uploads[payload]; + } }), }; diff --git a/yarn.lock b/yarn.lock index e29b8781d7..3142ac7558 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1493,6 +1493,19 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@preact/signals-core@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@preact/signals-core/-/signals-core-1.2.2.tgz#279dcc5ab249de2f2e8f6e6779b1958256ba843e" + integrity sha512-z3/bCj7rRA21RJb4FeJ4guCrD1CQbaURHkCTunUWQpxUMAFOPXCD8tSFqERyGrrcSb4T3Hrmdc1OAl0LXBHwiw== + +"@preact/signals-react@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@preact/signals-react/-/signals-react-1.2.1.tgz#6d5d305ebdb38c879043acebc65c0d9351e663c1" + integrity sha512-73J8sL1Eru7Ot4yBYOCPj1izEZjzCEXlembRgk6C7PkwsqoAVbCxMlDOFfCLoPFuJ6qeGatrJzRkcycXppMqVQ== + dependencies: + "@preact/signals-core" "^1.2.2" + use-sync-external-store "^1.2.0" + "@sinclair/typebox@^0.23.3": version "0.23.5" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" @@ -9121,6 +9134,11 @@ use-memo-one@^1.1.1: resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c" integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ== +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + use@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" From ee033d6d08b68b710c5401741de0238f75435c0b Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 22 Nov 2022 13:39:43 -0700 Subject: [PATCH 22/95] Telemetry (#4564) --- app/Console/Commands/TelemetryCommand.php | 34 ++++ app/Console/Kernel.php | 32 ++++ .../Wings/DaemonConfigurationRepository.php | 4 +- .../Telemetry/TelemetryCollectionService.php | 175 ++++++++++++++++++ config/pterodactyl.php | 12 ++ 5 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 app/Console/Commands/TelemetryCommand.php create mode 100644 app/Services/Telemetry/TelemetryCollectionService.php diff --git a/app/Console/Commands/TelemetryCommand.php b/app/Console/Commands/TelemetryCommand.php new file mode 100644 index 0000000000..3e1b0c2f87 --- /dev/null +++ b/app/Console/Commands/TelemetryCommand.php @@ -0,0 +1,34 @@ +output->info('Collecting telemetry data, this may take a while...'); + + VarDumper::dump($this->telemetryCollectionService->collect()); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 17b3d4de44..a00b17b6d6 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -2,10 +2,13 @@ namespace Pterodactyl\Console; +use Ramsey\Uuid\Uuid; use Pterodactyl\Models\ActivityLog; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Database\Console\PruneCommand; +use Pterodactyl\Repositories\Eloquent\SettingsRepository; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; +use Pterodactyl\Services\Telemetry\TelemetryCollectionService; use Pterodactyl\Console\Commands\Schedule\ProcessRunnableCommand; use Pterodactyl\Console\Commands\Maintenance\PruneOrphanedBackupsCommand; use Pterodactyl\Console\Commands\Maintenance\CleanServiceBackupFilesCommand; @@ -37,5 +40,34 @@ protected function schedule(Schedule $schedule) if (config('activity.prune_days')) { $schedule->command(PruneCommand::class, ['--model' => [ActivityLog::class]])->daily(); } + + if (config('pterodactyl.telemetry.enabled')) { + $this->registerTelemetry($schedule); + } + } + + /** + * I wonder what this does. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + private function registerTelemetry(Schedule $schedule): void + { + $settingsRepository = app()->make(SettingsRepository::class); + + $uuid = $settingsRepository->get('app:uuid'); + if (is_null($uuid)) { + $uuid = Uuid::uuid4()->toString(); + $settingsRepository->set('app:uuid', $uuid); + } + + // Calculate a fixed time to run the data push at, this will be the same time every day. + $time = hexdec(str_replace('-', '', substr($uuid, 27))) % 1440; + $hour = floor($time / 60); + $minute = $time % 60; + + // Run the telemetry collector. + $schedule->call(app()->make(TelemetryCollectionService::class))->description('Collect Telemetry')->dailyAt("$hour:$minute"); } } diff --git a/app/Repositories/Wings/DaemonConfigurationRepository.php b/app/Repositories/Wings/DaemonConfigurationRepository.php index d24fb7e501..5580f9a8be 100644 --- a/app/Repositories/Wings/DaemonConfigurationRepository.php +++ b/app/Repositories/Wings/DaemonConfigurationRepository.php @@ -14,10 +14,10 @@ class DaemonConfigurationRepository extends DaemonRepository * * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ - public function getSystemInformation(): array + public function getSystemInformation(?int $version = null): array { try { - $response = $this->getHttpClient()->get('/api/system'); + $response = $this->getHttpClient()->get('/api/system' . (!is_null($version) ? '?v=' . $version : '')); } catch (TransferException $exception) { throw new DaemonConnectionException($exception); } diff --git a/app/Services/Telemetry/TelemetryCollectionService.php b/app/Services/Telemetry/TelemetryCollectionService.php new file mode 100644 index 0000000000..c23f41dc05 --- /dev/null +++ b/app/Services/Telemetry/TelemetryCollectionService.php @@ -0,0 +1,175 @@ +collect(); + } catch (Exception) { + return; + } + + Http::post('https://telemetry.pterodactyl.io', $data); + } + + /** + * Collects telemetry data and returns it as an array. + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function collect(): array + { + $uuid = $this->settingsRepository->get('app:uuid'); + if (is_null($uuid)) { + $uuid = Uuid::uuid4()->toString(); + $this->settingsRepository->set('app:uuid', $uuid); + } + + $nodes = Node::all()->map(function ($node) { + try { + $info = $this->daemonConfigurationRepository->setNode($node)->getSystemInformation(2); + } catch (Exception) { + return null; + } + + return [ + 'id' => $node->uuid, + 'version' => Arr::get($info, 'version', ''), + + 'docker' => [ + 'version' => Arr::get($info, 'docker.version', ''), + + 'cgroups' => [ + 'driver' => Arr::get($info, 'docker.cgroups.driver', ''), + 'version' => Arr::get($info, 'docker.cgroups.version', ''), + ], + + 'containers' => [ + 'total' => Arr::get($info, 'docker.containers.total', -1), + 'running' => Arr::get($info, 'docker.containers.running', -1), + 'paused' => Arr::get($info, 'docker.containers.paused', -1), + 'stopped' => Arr::get($info, 'docker.containers.stopped', -1), + ], + + 'storage' => [ + 'driver' => Arr::get($info, 'docker.storage.driver', ''), + 'filesystem' => Arr::get($info, 'docker.storage.filesystem', ''), + ], + + 'runc' => [ + 'version' => Arr::get($info, 'docker.runc.version', ''), + ], + ], + + 'system' => [ + 'architecture' => Arr::get($info, 'system.architecture', ''), + 'cpuThreads' => Arr::get($info, 'system.cpu_threads', ''), + 'memoryBytes' => Arr::get($info, 'system.memory_bytes', ''), + 'kernelVersion' => Arr::get($info, 'system.kernel_version', ''), + 'os' => Arr::get($info, 'system.os', ''), + 'osType' => Arr::get($info, 'system.os_type', ''), + ], + ]; + })->filter(fn ($node) => !is_null($node))->toArray(); + + return [ + 'id' => $uuid, + + 'panel' => [ + 'version' => config('app.version'), + 'phpVersion' => phpversion(), + + 'drivers' => [ + 'backup' => [ + 'type' => config('backups.default'), + ], + 'cache' => [ + 'type' => config('cache.default'), + ], + 'database' => [ + 'type' => config('database.default'), + 'version' => DB::getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), + ], + ], + ], + + 'resources' => [ + 'allocations' => [ + 'count' => Allocation::count(), + 'used' => Allocation::whereNotNull('server_id')->count(), + ], + + 'backups' => [ + 'count' => Backup::count(), + 'bytes' => Backup::sum('bytes'), + ], + + 'eggs' => [ + 'count' => Egg::count(), + 'ids' => Egg::pluck('uuid')->toArray(), + ], + + 'locations' => [ + 'count' => Location::count(), + ], + + 'mounts' => [ + 'count' => Mount::count(), + ], + + 'nests' => [ + 'count' => Nest::count(), + ], + + 'nodes' => [ + 'count' => Node::count(), + ], + + 'servers' => [ + 'count' => Server::count(), + 'suspended' => Server::where('status', Server::STATUS_SUSPENDED)->count(), + ], + + 'users' => [ + 'count' => User::count(), + 'admins' => User::where('root_admin', true)->count(), + ], + ], + + 'nodes' => $nodes, + ]; + } +} diff --git a/config/pterodactyl.php b/config/pterodactyl.php index 58c58bc9eb..e5ceb35256 100644 --- a/config/pterodactyl.php +++ b/config/pterodactyl.php @@ -177,4 +177,16 @@ // Should an email be sent to a server owner whenever their server is reinstalled? 'send_reinstall_notification' => env('PTERODACTYL_SEND_REINSTALL_NOTIFICATION', true), ], + + /* + |-------------------------------------------------------------------------- + | Telemetry Settings + |-------------------------------------------------------------------------- + | + | This section controls the telemetry sent by Pterodactyl. + */ + + 'telemetry' => [ + 'enabled' => env('PTERODACTYL_TELEMETRY_ENABLED', false), + ], ]; From 5b36313c579ed0fe5fa151c16ce0c6f2be32580f Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 22 Nov 2022 13:37:46 -0700 Subject: [PATCH 23/95] Update README.md --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 79efc44e6d..9163c49627 100644 --- a/README.md +++ b/README.md @@ -27,17 +27,18 @@ Stop settling for less. Make game servers a first class citizen on your platform I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's developement. [Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi) -| Company | About | -|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [**WISP**](https://wisp.gg) | Extra features. | -| [**Fragnet**](https://fragnet.net) | Providing low latency, high-end game hosting solutions to gamers, game studios and eSports platforms. | -| [**RocketNode**](https://rocketnode.com/) | Innovative game server hosting combined with a straightforward control panel, affordable prices, and Rocket-Fast support. | -| [**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! | -| [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | -| [**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. | -| [**Pterodactyl Market**](https://pterodactylmarket.com/) | Pterodactyl Market is a one-and-stop shop for Pterodactyl. In our market, you can find Add-ons, Themes, Eggs, and more for Pterodactyl. | +| Company | About | +|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [**WISP**](https://wisp.gg) | Extra features. | +| [**Fragnet**](https://fragnet.net) | Providing low latency, high-end game hosting solutions to gamers, game studios and eSports platforms. | +| [**RocketNode**](https://rocketnode.com/) | Innovative game server hosting combined with a straightforward control panel, affordable prices, and Rocket-Fast support. | +| [**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! | +| [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | +| [**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. | +| [**Pterodactyl Market**](https://pterodactylmarket.com/) | Pterodactyl Market is a one-and-stop shop for Pterodactyl. In our market, you can find Add-ons, Themes, Eggs, and more for Pterodactyl. | +| [**UltraServers**](https://ultraservers.com/) | Deploy premium games hosting with the click of a button. Manage and swap games with ease and let us take care of the rest. We currently support Minecraft, Rust, ARK, 7 Days to Die, Garys MOD, CS:GO, Satisfactory and others. | ### Supported Games From 1bb1b13f6d1247a051f249016c5b8fe985e64817 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 22 Nov 2022 13:40:58 -0700 Subject: [PATCH 24/95] Update CHANGELOG.md --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 371916bc96..6ad1ebca5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ 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.0-rc.2 +### Changed +* `MB` byte suffix are now `MiB` to more accurately reflect the actual value. +* Server reinstallation failures are tracked independently of the initial installation process. + +### Fixed +* Properly handle a missing `Content-Length` header in the response from the daemon. +* Ensure activity log properties are always returned as an object instead of an empty array. + +### Added +* Added the `server:settings.description` activity log event for when a server description is changed. +* Added the ability to cancel file uploads in the file manager for a server. +* Added a telemetry service to collect anonymous metrics from the panel, this feature is disabled by default and can be toggled using the `PTERODACTYL_TELEMETRY_ENABLED` environment variable. + ## v1.11.0-rc.1 ### Changed * Changed minimum PHP version is now 8.0 instead of `7.4`. From a4e547dc676529018ef6404c597b3bd84dddd099 Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Sun, 27 Nov 2022 21:38:28 -0500 Subject: [PATCH 25/95] ui(server): fix console searchbar z-index (#4587) --- resources/scripts/components/server/console/Console.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/scripts/components/server/console/Console.tsx b/resources/scripts/components/server/console/Console.tsx index e3cb43ab26..25f07439f5 100644 --- a/resources/scripts/components/server/console/Console.tsx +++ b/resources/scripts/components/server/console/Console.tsx @@ -66,6 +66,11 @@ export default () => { const isTransferring = ServerContext.useStoreState((state) => state.server.data!.isTransferring); const [history, setHistory] = usePersistedState(`${serverId}:command_history`, []); const [historyIndex, setHistoryIndex] = useState(-1); + // SearchBarAddon has hardcoded z-index: 999 :( + const zIndex = ` + .xterm-search-bar__addon { + z-index: 10; + }`; const handleConsoleOutput = (line: string, prelude = false) => terminal.writeln((prelude ? TERMINAL_PRELUDE : '') + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m'); @@ -126,6 +131,7 @@ export default () => { terminal.open(ref.current); fitAddon.fit(); + searchBar.addNewStyle(zIndex); // Add support for capturing keys terminal.attachCustomKeyEventHandler((e: KeyboardEvent) => { From 75f36839df4a5dbd01591394891500586df5c02a Mon Sep 17 00:00:00 2001 From: TaktischerSpeck Date: Mon, 28 Nov 2022 19:59:48 +0100 Subject: [PATCH 26/95] egg(teamspeak): port and argument updates (#4546) --- .../voice-servers/egg-teamspeak3-server.json | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json index 2212a3c087..0282c2dd7b 100644 --- a/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json +++ b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json @@ -13,7 +13,7 @@ "ghcr.io\/pterodactyl\/yolks:debian" ], "file_denylist": [], - "startup": ".\/ts3server default_voice_port={{SERVER_PORT}} query_port={{QUERY_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} license_accepted=1", + "startup": ".\/ts3server default_voice_port={{SERVER_PORT}} query_port={{QUERY_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} query_http_port={{QUERY_HTTP}} query_ssh_port={{QUERY_SSH}} query_protocols={{QUERY_PROTOCOLS_VAR}} license_accepted=1", "config": { "files": "{}", "startup": "{\r\n \"done\": \"listening on 0.0.0.0:\"\r\n}", @@ -35,7 +35,8 @@ "default_value": "latest", "user_viewable": true, "user_editable": true, - "rules": "required|string|max:6" + "rules": "required|string|max:6", + "field_type": "text" }, { "name": "File Transfer Port", @@ -44,7 +45,8 @@ "default_value": "30033", "user_viewable": true, "user_editable": false, - "rules": "required|integer|between:1,65535" + "rules": "required|integer|between:1025,65535", + "field_type": "text" }, { "name": "Query Port", @@ -53,7 +55,38 @@ "default_value": "10011", "user_viewable": true, "user_editable": false, - "rules": "required|integer|between:1,65535" + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query Protocols", + "description": "Comma separated list of protocols that can be used to connect to the ServerQuery | \r\nPossible values are raw, ssh and http | \r\nE.g.: raw,ssh,http", + "env_variable": "QUERY_PROTOCOLS_VAR", + "default_value": "raw,http,ssh", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:12", + "field_type": "text" + }, + { + "name": "Query SSH Port", + "description": "TCP Port opened for ServerQuery connections using SSH", + "env_variable": "QUERY_SSH", + "default_value": "10022", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" + }, + { + "name": "Query HTTP Port", + "description": "TCP Port opened for ServerQuery connections using http", + "env_variable": "QUERY_HTTP", + "default_value": "10080", + "user_viewable": true, + "user_editable": false, + "rules": "required|integer|between:1025,65535", + "field_type": "text" } ] -} \ No newline at end of file +} From c8f7bdf9cbc020ce3db79fe48a7edadd391a8b61 Mon Sep 17 00:00:00 2001 From: Wunderharke <5105672+Wunderharke@users.noreply.github.com> Date: Mon, 28 Nov 2022 20:04:56 +0100 Subject: [PATCH 27/95] egg(teamspeak): fix database support (#4513) --- database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json index 0282c2dd7b..ef15f1ca06 100644 --- a/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json +++ b/database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json @@ -22,7 +22,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(curl -sSL https:\/\/teamspeak.com\/versions\/server.json | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\" \r\ncurl -L http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar -xvj --strip-components=1", + "script": "#!\/bin\/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(curl -sSL https:\/\/teamspeak.com\/versions\/server.json | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\" \r\ncurl -L http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar -xvj --strip-components=1\r\ncp .\/redist\/libmariadb.so.2 .\/", "container": "ghcr.io\/pterodactyl\/installers:alpine", "entrypoint": "ash" } From 794248d4d724d6e69f0548ea1edeb1f1acdfd38e Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 1 Dec 2022 11:49:54 -0700 Subject: [PATCH 28/95] routes: fix imports --- .gitignore | 5 +++++ config/activity.php | 2 +- routes/api-remote.php | 3 ++- routes/auth.php | 7 ++++--- routes/base.php | 1 + 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4b5d022467..2c9fda4ca6 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,8 @@ misc coverage.xml resources/lang/locales.js .phpunit.result.cache + +/public/build +/public/hot +result +docker-compose.yaml diff --git a/config/activity.php b/config/activity.php index 3491d2e2ba..6c492d3342 100644 --- a/config/activity.php +++ b/config/activity.php @@ -1,7 +1,7 @@ env('APP_ACTIVITY_PRUNE_DAYS', 90), // If set to true activity log entries generated by an admin user that is not also diff --git a/routes/api-remote.php b/routes/api-remote.php index e43c62eafd..88b2a6ea18 100644 --- a/routes/api-remote.php +++ b/routes/api-remote.php @@ -15,9 +15,10 @@ Route::get('/install', [Remote\Servers\ServerInstallController::class, 'index']); Route::post('/install', [Remote\Servers\ServerInstallController::class, 'store']); - Route::post('/archive', [Remote\Servers\ServerTransferController::class, 'archive']); Route::get('/transfer/failure', [Remote\Servers\ServerTransferController::class, 'failure']); Route::get('/transfer/success', [Remote\Servers\ServerTransferController::class, 'success']); + Route::post('/transfer/failure', [Remote\Servers\ServerTransferController::class, 'failure']); + Route::post('/transfer/success', [Remote\Servers\ServerTransferController::class, 'success']); }); Route::group(['prefix' => '/backups'], function () { diff --git a/routes/auth.php b/routes/auth.php index 7d0930f11d..36039f3a2c 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -1,5 +1,6 @@ name('auth.login'); Route::get('/password', [Auth\LoginController::class, 'index'])->name('auth.forgot-password'); @@ -38,12 +39,12 @@ // is created). Route::post('/password/reset', Auth\ResetPasswordController::class)->name('auth.reset-password'); -// Remove the guest middleware and apply the authenticated middleware to this endpoint +// Remove the guest middleware and apply the authenticated middleware to this endpoint, // so it cannot be used unless you're already logged in. Route::post('/logout', [Auth\LoginController::class, 'logout']) ->withoutMiddleware('guest') ->middleware('auth') ->name('auth.logout'); -// Catch any other combinations of routes and pass them off to the Vuejs component. +// Catch any other combinations of routes and pass them off to the React component. Route::fallback([Auth\LoginController::class, 'index']); diff --git a/routes/base.php b/routes/base.php index 1d3ad66a64..93f45b577e 100644 --- a/routes/base.php +++ b/routes/base.php @@ -1,5 +1,6 @@ Date: Thu, 1 Dec 2022 11:50:28 -0700 Subject: [PATCH 29/95] api(remote): check if transfer is present before trying to update status --- .../Api/Remote/Servers/ServerTransferController.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php index 72153bf252..4e86236296 100644 --- a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php +++ b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php @@ -11,6 +11,7 @@ use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Wings\DaemonServerRepository; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class ServerTransferController extends Controller @@ -33,6 +34,10 @@ public function __construct( public function failure(string $uuid): JsonResponse { $server = $this->repository->getByUuid($uuid); + $transfer = $server->transfer; + if (is_null($transfer)) { + throw new ConflictHttpException('Server is not being transferred.'); + } return $this->processFailedTransfer($server->transfer); } @@ -46,6 +51,9 @@ public function success(string $uuid): JsonResponse { $server = $this->repository->getByUuid($uuid); $transfer = $server->transfer; + if (is_null($transfer)) { + throw new ConflictHttpException('Server is not being transferred.'); + } /** @var \Pterodactyl\Models\Server $server */ $server = $this->connection->transaction(function () use ($server, $transfer) { From 6272bb671073021d558ac1eadb0cce7474282aed Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 1 Dec 2022 11:52:22 -0700 Subject: [PATCH 30/95] api(remote): cleanup --- .../Controllers/Api/Remote/Servers/ServerTransferController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php index 4e86236296..c14ddea97a 100644 --- a/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php +++ b/app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php @@ -39,7 +39,7 @@ public function failure(string $uuid): JsonResponse throw new ConflictHttpException('Server is not being transferred.'); } - return $this->processFailedTransfer($server->transfer); + return $this->processFailedTransfer($transfer); } /** From 250c557e2311af8a04f8bafbddb0abf049d8b601 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 1 Dec 2022 12:09:27 -0700 Subject: [PATCH 31/95] telemetry: include more detailed server egg and nest usage --- app/Services/Telemetry/TelemetryCollectionService.php | 11 ++++++++++- config/pterodactyl.php | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Services/Telemetry/TelemetryCollectionService.php b/app/Services/Telemetry/TelemetryCollectionService.php index c23f41dc05..ca157694e5 100644 --- a/app/Services/Telemetry/TelemetryCollectionService.php +++ b/app/Services/Telemetry/TelemetryCollectionService.php @@ -116,9 +116,11 @@ public function collect(): array 'backup' => [ 'type' => config('backups.default'), ], + 'cache' => [ 'type' => config('cache.default'), ], + 'database' => [ 'type' => config('database.default'), 'version' => DB::getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), @@ -139,7 +141,10 @@ public function collect(): array 'eggs' => [ 'count' => Egg::count(), - 'ids' => Egg::pluck('uuid')->toArray(), + 'server_usage' => Egg::all() + ->flatMap(fn (Egg $egg) => [$egg->uuid => $egg->servers->count()]) + ->filter(fn (int $count) => $count > 0) + ->toArray(), ], 'locations' => [ @@ -152,6 +157,10 @@ public function collect(): array 'nests' => [ 'count' => Nest::count(), + 'server_usage' => Nest::all() + ->flatMap(fn (Nest $nest) => [$nest->uuid => $nest->eggs->sum(fn (Egg $egg) => $egg->servers->count())]) + ->filter(fn (int $count) => $count > 0) + ->toArray(), ], 'nodes' => [ diff --git a/config/pterodactyl.php b/config/pterodactyl.php index e5ceb35256..43c7c57be6 100644 --- a/config/pterodactyl.php +++ b/config/pterodactyl.php @@ -187,6 +187,6 @@ */ 'telemetry' => [ - 'enabled' => env('PTERODACTYL_TELEMETRY_ENABLED', false), + 'enabled' => env('PTERODACTYL_TELEMETRY_ENABLED', true), ], ]; From fa7503816af1f7af4f8b5e2761f839260b0b7a69 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 4 Dec 2022 14:59:45 -0700 Subject: [PATCH 32/95] Add telemetry prompt in `p:environment:setup` command --- app/Console/Commands/Environment/AppSettingsCommand.php | 9 ++++++++- app/Console/Kernel.php | 4 ++-- app/Services/Telemetry/TelemetryCollectionService.php | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php index 8b9aad48e8..e3ffbcfbcc 100644 --- a/app/Console/Commands/Environment/AppSettingsCommand.php +++ b/app/Console/Commands/Environment/AppSettingsCommand.php @@ -44,7 +44,8 @@ class AppSettingsCommand extends Command {--redis-host= : Redis host to use for connections.} {--redis-pass= : Password used to connect to redis.} {--redis-port= : Port to connect to redis over.} - {--settings-ui= : Enable or disable the settings UI.}'; + {--settings-ui= : Enable or disable the settings UI.} + {--telemetry= : Enable or disable anonymous telemetry.}'; protected array $variables = []; @@ -119,6 +120,12 @@ public function handle(): int $this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm('Enable UI based settings editor?', true) ? 'false' : 'true'; } + $this->output->comment('Please reference https://pterodactyl.io/panel/1.0/additional_configuration.html#telemetry for more detailed information regarding telemetry data and collection.'); + $this->variables['PTERODACTYL_TELEMETRY_ENABLED'] = $this->option('telemetry') ?? $this->confirm( + 'Enable sending anonymous telemetry data?', + config('pterodactyl.telemetry.enabled', true) + ) ? 'true' : 'false'; + // Make sure session cookies are set as "secure" when using HTTPS if (str_starts_with($this->variables['APP_URL'], 'https://')) { $this->variables['SESSION_SECURE_COOKIE'] = 'true'; diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index a00b17b6d6..f79e6c9051 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -56,10 +56,10 @@ private function registerTelemetry(Schedule $schedule): void { $settingsRepository = app()->make(SettingsRepository::class); - $uuid = $settingsRepository->get('app:uuid'); + $uuid = $settingsRepository->get('app:telemetry:uuid'); if (is_null($uuid)) { $uuid = Uuid::uuid4()->toString(); - $settingsRepository->set('app:uuid', $uuid); + $settingsRepository->set('app:telemetry:uuid', $uuid); } // Calculate a fixed time to run the data push at, this will be the same time every day. diff --git a/app/Services/Telemetry/TelemetryCollectionService.php b/app/Services/Telemetry/TelemetryCollectionService.php index ca157694e5..6c1b527ef5 100644 --- a/app/Services/Telemetry/TelemetryCollectionService.php +++ b/app/Services/Telemetry/TelemetryCollectionService.php @@ -52,10 +52,10 @@ public function __invoke(): void */ public function collect(): array { - $uuid = $this->settingsRepository->get('app:uuid'); + $uuid = $this->settingsRepository->get('app:telemetry:uuid'); if (is_null($uuid)) { $uuid = Uuid::uuid4()->toString(); - $this->settingsRepository->set('app:uuid', $uuid); + $this->settingsRepository->set('app:telemetry:uuid', $uuid); } $nodes = Node::all()->map(function ($node) { From 73b27aea8eb8d673c0f17b500db880224bc91400 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 4 Dec 2022 15:27:21 -0700 Subject: [PATCH 33/95] Update CHANGELOG.md --- CHANGELOG.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad1ebca5b..67fd49fdf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,31 @@ 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.0 +### Changed (since 1.10.4) +* Changed minimum PHP version requirement from `7.4` to `8.0`. +* Upgraded from Laravel 8 to Laravel 9. +* This release requires Wings v1.11.x in order for Server Transfers to work. +* `MB` byte suffixes are now displayed as `MiB` to more accurately reflect the actual value. +* Server re-installation failures are tracked independently of the initial installation process. + +### Fixed (since 1.10.4) +* Node maintenance mode now properly blocks access to servers. +* Fixed the length validation on the Minecraft Forge egg. +* Fixed the password in the JDBC string not being properly URL encoded. +* Fixed an issue where Wings would throw a validation error while attempting to upload activity logs. +* Properly handle a missing `Content-Length` header in the response from the daemon. +* Ensure activity log properties are always returned as an object instead of an empty array. + +### Added (since 1.10.4) +* Added the `server:settings.description` activity log event for when a server description is changed. +* Added the ability to cancel file uploads in the file manager for a server. +* Added a telemetry service to collect anonymous metrics from the panel, this feature is *enabled* by default and can be toggled using the `PTERODACTYL_TELEMETRY_ENABLED` environment variable. + ## v1.11.0-rc.2 ### Changed -* `MB` byte suffix are now `MiB` to more accurately reflect the actual value. -* Server reinstallation failures are tracked independently of the initial installation process. +* `MB` byte suffixes are now displayed as `MiB` to more accurately reflect the actual value. +* Server re-installation failures are tracked independently of the initial installation process. ### Fixed * Properly handle a missing `Content-Length` header in the response from the daemon. @@ -19,7 +40,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. ## v1.11.0-rc.1 ### Changed -* Changed minimum PHP version is now 8.0 instead of `7.4`. +* Changed minimum PHP version requirement from `7.4` to `8.0`. * Upgraded from Laravel 8 to Laravel 9. * This release requires Wings v1.11.x in order for Server Transfers to work. From dd69652942a8397edfce4e30c3e12eb6a1ea3ce1 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 4 Dec 2022 16:09:42 -0700 Subject: [PATCH 34/95] Fix `No application encryption key has been specified` error while trying to generate said key --- app/Providers/AppServiceProvider.php | 4 ++-- app/Services/Nodes/NodeCreationService.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index d4ffdadbbd..6a805bad38 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,12 +2,12 @@ namespace Pterodactyl\Providers; -use View; -use Cache; use Pterodactyl\Models; use Illuminate\Support\Str; use Illuminate\Support\Facades\URL; use Illuminate\Pagination\Paginator; +use Illuminate\Support\Facades\View; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; use Pterodactyl\Extensions\Themes\Theme; diff --git a/app/Services/Nodes/NodeCreationService.php b/app/Services/Nodes/NodeCreationService.php index e4946bc209..b21589c4a3 100644 --- a/app/Services/Nodes/NodeCreationService.php +++ b/app/Services/Nodes/NodeCreationService.php @@ -13,7 +13,7 @@ class NodeCreationService /** * NodeCreationService constructor. */ - public function __construct(private Encrypter $encrypter, protected NodeRepositoryInterface $repository) + public function __construct(protected NodeRepositoryInterface $repository) { } @@ -25,7 +25,7 @@ public function __construct(private Encrypter $encrypter, protected NodeReposito public function handle(array $data): Node { $data['uuid'] = Uuid::uuid4()->toString(); - $data['daemon_token'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token'] = app(Encrypter::class)->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); return $this->repository->create($data, true, true); From 158facd53460e9d697efa29eb6d39259b6a9570e Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 4 Dec 2022 16:32:15 -0700 Subject: [PATCH 35/95] eslint: fix prettier config --- .eslintrc.js | 25 +++++++------------------ .prettierrc.json | 9 +++++++++ 2 files changed, 16 insertions(+), 18 deletions(-) create mode 100644 .prettierrc.json diff --git a/.eslintrc.js b/.eslintrc.js index 77547d8799..a0bb5e9ebb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,9 +1,3 @@ -const prettier = { - singleQuote: true, - jsxSingleQuote: true, - printWidth: 120, -}; - /** @type {import('eslint').Linter.Config} */ module.exports = { parser: '@typescript-eslint/parser', @@ -21,20 +15,15 @@ module.exports = { version: 'detect', }, linkComponents: [ - {name: 'Link', linkAttribute: 'to'}, - {name: 'NavLink', linkAttribute: 'to'}, + { name: 'Link', linkAttribute: 'to' }, + { name: 'NavLink', linkAttribute: 'to' }, ], }, env: { browser: true, es6: true, }, - plugins: [ - 'react', - 'react-hooks', - 'prettier', - '@typescript-eslint', - ], + plugins: ['react', 'react-hooks', 'prettier', '@typescript-eslint'], extends: [ // 'standard', 'eslint:recommended', @@ -44,7 +33,7 @@ module.exports = { ], rules: { eqeqeq: 'error', - 'prettier/prettier': ['error', prettier], + 'prettier/prettier': ['error', {}, { usePrettierrc: true }], // TypeScript can infer this significantly better than eslint ever can. 'react/prop-types': 0, 'react/display-name': 0, @@ -56,7 +45,7 @@ module.exports = { // @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md#how-to-use 'no-use-before-define': 0, '@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'}], - } + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + '@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }], + }, }; diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000000..d000a2241e --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "printWidth": 120, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "endOfLine": "lf" +} From 598c956e4e516441a31dc848c850ff256ef4bb23 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 4 Dec 2022 16:36:53 -0700 Subject: [PATCH 36/95] ui(server): fix file uploads being canceled instead of completed --- .../server/files/FileManagerStatus.tsx | 4 +-- .../components/server/files/UploadButton.tsx | 30 ++++++++++--------- resources/scripts/state/server/files.ts | 7 +++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/resources/scripts/components/server/files/FileManagerStatus.tsx b/resources/scripts/components/server/files/FileManagerStatus.tsx index 620e067c17..3e1c2df24e 100644 --- a/resources/scripts/components/server/files/FileManagerStatus.tsx +++ b/resources/scripts/components/server/files/FileManagerStatus.tsx @@ -32,7 +32,7 @@ const Spinner = ({ progress, className }: { progress: number; className?: string const FileUploadList = () => { const { close } = useContext(DialogWrapperContext); - const removeFileUpload = ServerContext.useStoreActions((actions) => actions.files.removeFileUpload); + const cancelFileUpload = ServerContext.useStoreActions((actions) => actions.files.cancelFileUpload); const clearFileUploads = ServerContext.useStoreActions((actions) => actions.files.clearFileUploads); const uploads = ServerContext.useStoreState((state) => Object.entries(state.files.uploads).sort(([a], [b]) => a.localeCompare(b)) @@ -49,7 +49,7 @@ const FileUploadList = () => { {name} From 51cee7688a7585d2830e34d260137e466d3b9226 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Wed, 23 Aug 2023 03:17:18 +0200 Subject: [PATCH 87/95] app: update prune-backup command description (#4754) --- .../Commands/Maintenance/PruneOrphanedBackupsCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php index 1ddd8cea3f..b7a04f8eee 100644 --- a/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php +++ b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php @@ -10,7 +10,7 @@ class PruneOrphanedBackupsCommand extends Command { protected $signature = 'p:maintenance:prune-backups {--prune-age=}'; - protected $description = 'Marks all backups that have not completed in the last "n" minutes as being failed.'; + protected $description = 'Marks all backups older than "n" minutes that have not yet completed as being failed.'; /** * PruneOrphanedBackupsCommand constructor. @@ -38,7 +38,7 @@ public function handle() return; } - $this->warn("Marking $count backups that have not been marked as completed in the last $since minutes as failed."); + $this->warn("Marking $count uncompleted backups that are older than $since minutes as failed."); $query->update([ 'is_successful' => false, From 5a417e9adbf9e92a5ff3cf2b873ed5268ad70b41 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Wed, 23 Aug 2023 03:18:05 +0200 Subject: [PATCH 88/95] app(setup): replace `mail` with `sendmail` driver (#4750) --- app/Console/Commands/Environment/EmailSettingsCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Console/Commands/Environment/EmailSettingsCommand.php b/app/Console/Commands/Environment/EmailSettingsCommand.php index d5efbc90ac..3a211394c9 100644 --- a/app/Console/Commands/Environment/EmailSettingsCommand.php +++ b/app/Console/Commands/Environment/EmailSettingsCommand.php @@ -44,7 +44,7 @@ public function handle() trans('command/messages.environment.mail.ask_driver'), [ 'smtp' => 'SMTP Server', - 'mail' => 'PHP\'s Internal Mail Function', + 'sendmail' => 'sendmail Binary', 'mailgun' => 'Mailgun Transactional Email', 'mandrill' => 'Mandrill Transactional Email', 'postmark' => 'Postmark Transactional Email', From 7fa0c26d80dfea135d70e58dc5cc1722f05d60c7 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 2 Oct 2023 17:06:05 -0600 Subject: [PATCH 89/95] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5fa9d8f8e5..e5158d03fa 100644 --- a/README.md +++ b/README.md @@ -24,17 +24,17 @@ 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 developement. +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) | Company | About | |-----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [**WISP**](https://wisp.gg) | Extra features. | | [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. | +| [**WemX**](https://wemx.net/) | WemX helps automate your hosting company or SaaS business by automating billing, user management, authentication, and much more. | | [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. | | [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! | | [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa. | -| [**Pterodactyl Market**](https://pterodactylmarket.com/) | Pterodactyl Market is a one-and-stop shop for Pterodactyl. In our market, you can find Add-ons, Themes, Eggs, and more for Pterodactyl. | | [**DutchIS**](https://dutchis.net?ref=pterodactyl) | DutchIS provides instant infrastructure such as pay per use VPS hosting. Start your game hosting journey on DutchIS. | | [**Skoali**](https://skoali.com/) | Skoali is a French company that hosts game servers and other types of services (VPS, WEB, Dedicated servers, ...). We also have a free plan for Minecraft and Garry's Mod. | | [**Rabbit Computing**](https://www.rabbitcomputing.com/link.php?id=5) | Rabbit Computing offers powerful VPS servers, highly available game hosting, and fully unlimited web hosting. Use code README for 20% off your first three months! | From 35159b3715285ec93cdd1973c820b0fe466cdedd Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 10 Oct 2023 13:13:00 -0600 Subject: [PATCH 90/95] Update CHANGELOG.md --- CHANGELOG.md | 18 +++ flake.lock | 334 ++++++++++++++++++--------------------------------- flake.nix | 8 +- shell.nix | 6 +- 4 files changed, 146 insertions(+), 220 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 938a003975..6f662116bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,24 @@ 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.4 +### Added +* Added support for the `server.queryport` option on the Rust egg. +* Added support for the Carbon modding framework to the Rust egg. + +### Changed +* Upgraded to Laravel 10. +* Sensitive data is no longer shown in the CopyOnClick toast notification. + +### Fixed +* Allow SVGs to be edited in the server's file manager. +* Properly validate the request body when creating a backup. +* Fixed issue with schedules running at the wrong time when the panel utilized a timezone with non-hour offsets (such as `Australia/Darwin`). +* Fixes the log directory when running the Panel in a container. +* Fixes the permission name used to check if a user has permission to read files/folders. +* Fixes the ability to unset a server's description through the client API. +* Fixed the MassActionBar on the server's file manager blocking elements below it, preventing them from being interacted with. + ## v1.11.3 ### Changed * When updating a server's description through the client API, if no value is specified, the description will now remain unchanged. diff --git a/flake.lock b/flake.lock index b2ee205086..f0b2fa2a71 100644 --- a/flake.lock +++ b/flake.lock @@ -1,61 +1,5 @@ { "nodes": { - "alejandra": { - "inputs": { - "fenix": "fenix", - "flakeCompat": "flakeCompat", - "nixpkgs": [ - "dream2nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1658427149, - "narHash": "sha256-ToD/1z/q5VHsLMrS2h96vjJoLho59eNRtknOUd19ey8=", - "owner": "kamadorueda", - "repo": "alejandra", - "rev": "f5a22afd2adfb249b4e68e0b33aa1f0fb73fb1be", - "type": "github" - }, - "original": { - "owner": "kamadorueda", - "repo": "alejandra", - "type": "github" - } - }, - "all-cabal-json": { - "flake": false, - "locked": { - "lastModified": 1665552503, - "narHash": "sha256-r14RmRSwzv5c+bWKUDaze6pXM7nOsiz1H8nvFHJvufc=", - "owner": "nix-community", - "repo": "all-cabal-json", - "rev": "d7c0434eebffb305071404edcf9d5cd99703878e", - "type": "github" - }, - "original": { - "owner": "nix-community", - "ref": "hackage", - "repo": "all-cabal-json", - "type": "github" - } - }, - "crane": { - "flake": false, - "locked": { - "lastModified": 1670900067, - "narHash": "sha256-VXVa+KBfukhmWizaiGiHRVX/fuk66P8dgSFfkVN4/MY=", - "owner": "ipetkov", - "repo": "crane", - "rev": "59b31b41a589c0a65e4a1f86b0e5eac68081468b", - "type": "github" - }, - "original": { - "owner": "ipetkov", - "repo": "crane", - "type": "github" - } - }, "devshell": { "flake": false, "locked": { @@ -74,28 +18,21 @@ }, "dream2nix": { "inputs": { - "alejandra": "alejandra", - "all-cabal-json": "all-cabal-json", - "crane": "crane", "devshell": "devshell", + "flake-compat": "flake-compat", "flake-parts": "flake-parts", - "flake-utils-pre-commit": "flake-utils-pre-commit", - "ghc-utils": "ghc-utils", - "gomod2nix": "gomod2nix", - "mach-nix": "mach-nix", - "nix-pypi-fetcher": "nix-pypi-fetcher", + "nix-unit": "nix-unit", "nixpkgs": [ "nixpkgs" ], - "poetry2nix": "poetry2nix", "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1674572627, - "narHash": "sha256-PpySmULdSrQ+NpVXmbMU6a7qo6apih99IMVyLH4qh2E=", + "lastModified": 1695717405, + "narHash": "sha256-MvHrU3h0Bw57s2p+wCUnSZliR4wvvPi3xkW+MRWB5HU=", "owner": "nix-community", "repo": "dream2nix", - "rev": "029dcc6358840143d5b63978c603f374d5a19583", + "rev": "6dbd59e4a47bd916a655c4425a3e730c6aeae033", "type": "github" }, "original": { @@ -104,39 +41,35 @@ "type": "github" } }, - "fenix": { - "inputs": { - "nixpkgs": [ - "dream2nix", - "alejandra", - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, + "flake-compat": { + "flake": false, "locked": { - "lastModified": 1657607339, - "narHash": "sha256-HaqoAwlbVVZH2n4P3jN2FFPMpVuhxDy1poNOR7kzODc=", - "owner": "nix-community", - "repo": "fenix", - "rev": "b814c83d9e6aa5a28d0cf356ecfdafb2505ad37d", + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", "type": "github" }, "original": { - "owner": "nix-community", - "repo": "fenix", + "owner": "edolstra", + "repo": "flake-compat", "type": "github" } }, "flake-parts": { "inputs": { - "nixpkgs-lib": "nixpkgs-lib" + "nixpkgs-lib": [ + "dream2nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1668450977, - "narHash": "sha256-cfLhMhnvXn6x1vPm+Jow3RiFAUSCw/l1utktCw5rVA4=", + "lastModified": 1675933616, + "narHash": "sha256-/rczJkJHtx16IFxMmAWu5nNYcSXNg1YYXTHoGjLrLUA=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "d591857e9d7dd9ddbfba0ea02b43b927c3c0f1fa", + "rev": "47478a4a003e745402acf63be7f9a092d51b83d7", "type": "github" }, "original": { @@ -146,12 +79,15 @@ } }, "flake-utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", "owner": "numtide", "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", "type": "github" }, "original": { @@ -160,13 +96,16 @@ "type": "github" } }, - "flake-utils-pre-commit": { + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, "locked": { - "lastModified": 1644229661, - "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "owner": "numtide", "repo": "flake-utils", - "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { @@ -175,69 +114,6 @@ "type": "github" } }, - "flakeCompat": { - "flake": false, - "locked": { - "lastModified": 1650374568, - "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "b4a34015c698c7793d592d66adbab377907a2be8", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "ghc-utils": { - "flake": false, - "locked": { - "lastModified": 1662774800, - "narHash": "sha256-1Rd2eohGUw/s1tfvkepeYpg8kCEXiIot0RijapUjAkE=", - "ref": "refs/heads/master", - "rev": "bb3a2d3dc52ff0253fb9c2812bd7aa2da03e0fea", - "revCount": 1072, - "type": "git", - "url": "https://gitlab.haskell.org/bgamari/ghc-utils" - }, - "original": { - "type": "git", - "url": "https://gitlab.haskell.org/bgamari/ghc-utils" - } - }, - "gomod2nix": { - "flake": false, - "locked": { - "lastModified": 1627572165, - "narHash": "sha256-MFpwnkvQpauj799b4QTBJQFEddbD02+Ln5k92QyHOSk=", - "owner": "tweag", - "repo": "gomod2nix", - "rev": "67f22dd738d092c6ba88e420350ada0ed4992ae8", - "type": "github" - }, - "original": { - "owner": "tweag", - "repo": "gomod2nix", - "type": "github" - } - }, - "mach-nix": { - "flake": false, - "locked": { - "lastModified": 1634711045, - "narHash": "sha256-m5A2Ty88NChLyFhXucECj6+AuiMZPHXNbw+9Kcs7F6Y=", - "owner": "DavHau", - "repo": "mach-nix", - "rev": "4433f74a97b94b596fa6cd9b9c0402104aceef5d", - "type": "github" - }, - "original": { - "id": "mach-nix", - "type": "indirect" - } - }, "mk-node-package": { "inputs": { "flake-utils": [ @@ -263,50 +139,65 @@ "type": "github" } }, - "nix-pypi-fetcher": { - "flake": false, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "dream2nix", + "nix-unit", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1669065297, - "narHash": "sha256-UStjXjNIuIm7SzMOWvuYWIHBkPUKQ8Id63BMJjnIDoA=", - "owner": "DavHau", - "repo": "nix-pypi-fetcher", - "rev": "a9885ac6a091576b5195d547ac743d45a2a615ac", + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", "type": "github" }, "original": { - "owner": "DavHau", - "repo": "nix-pypi-fetcher", + "owner": "nix-community", + "repo": "nix-github-actions", "type": "github" } }, - "nixpkgs": { + "nix-unit": { + "inputs": { + "flake-parts": [ + "dream2nix", + "flake-parts" + ], + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "dream2nix", + "nixpkgs" + ], + "treefmt-nix": "treefmt-nix" + }, "locked": { - "lastModified": 1674459583, - "narHash": "sha256-L0UZl/u2H3HGsrhN+by42c5kNYeKtdmJiPzIRvEVeiM=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "1b1f50645af2a70dc93eae18bfd88d330bfbcf7f", + "lastModified": 1690289081, + "narHash": "sha256-PCXQAQt8+i2pkUym9P1JY4JGoeZJLzzxWBhprHDdItM=", + "owner": "adisbladis", + "repo": "nix-unit", + "rev": "a9d6f33e50d4dcd9cfc0c92253340437bbae282b", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "adisbladis", + "repo": "nix-unit", "type": "github" } }, - "nixpkgs-lib": { + "nixpkgs": { "locked": { - "dir": "lib", - "lastModified": 1665349835, - "narHash": "sha256-UK4urM3iN80UXQ7EaOappDzcisYIuEURFRoGQ/yPkug=", + "lastModified": 1695644571, + "narHash": "sha256-asS9dCCdlt1lPq0DLwkVBbVoEKuEuz+Zi3DG7pR/RxA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "34c5293a71ffdb2fe054eb5288adc1882c1eb0b1", + "rev": "6500b4580c2a1f3d0f980d32d285739d8e156d92", "type": "github" }, "original": { - "dir": "lib", "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", @@ -347,29 +238,9 @@ "type": "github" } }, - "poetry2nix": { - "flake": false, - "locked": { - "lastModified": 1666918719, - "narHash": "sha256-BkK42fjAku+2WgCOv2/1NrPa754eQPV7gPBmoKQBWlc=", - "owner": "nix-community", - "repo": "poetry2nix", - "rev": "289efb187123656a116b915206e66852f038720e", - "type": "github" - }, - "original": { - "owner": "nix-community", - "ref": "1.36.0", - "repo": "poetry2nix", - "type": "github" - } - }, "pre-commit-hooks": { "inputs": { - "flake-utils": [ - "dream2nix", - "flake-utils-pre-commit" - ], + "flake-utils": "flake-utils", "nixpkgs": [ "dream2nix", "nixpkgs" @@ -392,25 +263,60 @@ "root": { "inputs": { "dream2nix": "dream2nix", - "flake-utils": "flake-utils", + "flake-utils": "flake-utils_2", "mk-node-package": "mk-node-package", "nixpkgs": "nixpkgs" } }, - "rust-analyzer-src": { - "flake": false, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "dream2nix", + "nix-unit", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1657557289, - "narHash": "sha256-PRW+nUwuqNTRAEa83SfX+7g+g8nQ+2MMbasQ9nt6+UM=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "caf23f29144b371035b864a1017dbc32573ad56d", + "lastModified": 1689620039, + "narHash": "sha256-BtNwghr05z7k5YMdq+6nbue+nEalvDepuA7qdQMAKoQ=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "719c2977f958c41fa60a928e2fbc50af14844114", "type": "github" }, "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", + "owner": "numtide", + "repo": "treefmt-nix", "type": "github" } } diff --git a/flake.nix b/flake.nix index 3bbbfb233c..e61dad5117 100644 --- a/flake.nix +++ b/flake.nix @@ -172,7 +172,7 @@ buildInputs = []; buildPhase = '' - yarn run build + yarn run build:production ''; installPhase = '' @@ -214,6 +214,7 @@ copyToRoot = pkgs.buildEnv { name = "image-root"; paths = [ + bash dockerTools.fakeNss caCertificates caddy @@ -221,12 +222,9 @@ configs coreutils mysql80 - nodejs-18_x - nodePackages.npm - nodePackages.pnpm + nodejs_18 nodePackages.yarn php81WithExtensions - postgresql_14 ]; pathsToLink = ["/bin" "/etc"]; }; diff --git a/shell.nix b/shell.nix index 7ac24da455..6eee625ab0 100644 --- a/shell.nix +++ b/shell.nix @@ -8,8 +8,12 @@ with pkgs; buildInputs = [ alejandra composer - nodejs-18_x + nodejs_18 nodePackages.yarn php81WithExtensions ]; + + shellHook = '' + PATH="$PATH:${pkgs.docker-compose}/libexec/docker/cli-plugins" + ''; } From a62e8b1a675d8aa2ac4964b71b2a581bf58d5b18 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 12 Oct 2023 13:54:42 -0600 Subject: [PATCH 91/95] egg(rust): use yolk image instead of the deprecated one --- database/Seeders/eggs/rust/egg-rust.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/Seeders/eggs/rust/egg-rust.json b/database/Seeders/eggs/rust/egg-rust.json index 2ec4bee079..74cc27c323 100644 --- a/database/Seeders/eggs/rust/egg-rust.json +++ b/database/Seeders/eggs/rust/egg-rust.json @@ -12,7 +12,7 @@ "steam_disk_space" ], "docker_images": { - "quay.io\/pterodactyl\/core:rust": "quay.io\/pterodactyl\/core:rust" + "ghcr.io\/pterodactyl\/games:rust": "ghcr.io\/pterodactyl\/games:rust" }, "file_denylist": [], "startup": ".\/RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.queryport {{QUERY_PORT}} +server.identity \"rust\" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \\\"{{HOSTNAME}}\\\" +server.level \\\"{{LEVEL}}\\\" +server.description \\\"{{DESCRIPTION}}\\\" +server.url \\\"{{SERVER_URL}}\\\" +server.headerimage \\\"{{SERVER_IMG}}\\\" +server.logoimage \\\"{{SERVER_LOGO}}\\\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \\\"{{RCON_PASS}}\\\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s \"+server.worldsize \\\"{{WORLD_SIZE}}\\\" +server.seed \\\"{{WORLD_SEED}}\\\"\" || printf %s \"+server.levelurl {{MAP_URL}}\" ) {{ADDITIONAL_ARGS}}", From 742e352c671610ab35b3bcc191fce12fcb3c8770 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 12 Oct 2023 13:55:53 -0600 Subject: [PATCH 92/95] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f662116bb..3e9e805eb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ 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.5 +### Fixed +* Rust egg using the wrong Docker image, breaking Rust modding frameworks. + ## v1.11.4 ### Added * Added support for the `server.queryport` option on the Rust egg. From 341eda7855dd71460755526405351f729ca87ae3 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 20 Oct 2023 19:29:21 -0600 Subject: [PATCH 93/95] egg(rust): change ordering of mod frameworks --- database/Seeders/eggs/rust/egg-rust.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/Seeders/eggs/rust/egg-rust.json b/database/Seeders/eggs/rust/egg-rust.json index 74cc27c323..35f543c75a 100644 --- a/database/Seeders/eggs/rust/egg-rust.json +++ b/database/Seeders/eggs/rust/egg-rust.json @@ -47,7 +47,7 @@ "default_value": "vanilla", "user_viewable": true, "user_editable": true, - "rules": "required|in:carbon,oxide,vanilla", + "rules": "required|in:vanilla,oxide,carbon", "field_type": "text" }, { From 8abf2d810666c360320cc25808167d08963bb9be Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 20 Oct 2023 19:30:25 -0600 Subject: [PATCH 94/95] ui(server): fix defaultValue not being used with VariableBox select --- resources/scripts/api/server/types.d.ts | 2 +- .../components/server/startup/VariableBox.tsx | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/resources/scripts/api/server/types.d.ts b/resources/scripts/api/server/types.d.ts index 0816d7e817..c4a01e9211 100644 --- a/resources/scripts/api/server/types.d.ts +++ b/resources/scripts/api/server/types.d.ts @@ -23,7 +23,7 @@ export interface ServerEggVariable { description: string; envVariable: string; defaultValue: string; - serverValue: string; + serverValue: string | null; isEditable: boolean; rules: string[]; } diff --git a/resources/scripts/components/server/startup/VariableBox.tsx b/resources/scripts/components/server/startup/VariableBox.tsx index ad73615c64..ba3a4fe47e 100644 --- a/resources/scripts/components/server/startup/VariableBox.tsx +++ b/resources/scripts/components/server/startup/VariableBox.tsx @@ -5,7 +5,6 @@ import { usePermissions } from '@/plugins/usePermissions'; import InputSpinner from '@/components/elements/InputSpinner'; import Input from '@/components/elements/Input'; import Switch from '@/components/elements/Switch'; -import tw from 'twin.macro'; import { debounce } from 'debounce'; import updateStartupVariable from '@/api/server/updateStartupVariable'; import useFlash from '@/plugins/useFlash'; @@ -61,15 +60,15 @@ const VariableBox = ({ variable }: Props) => { return ( +

{!variable.isEditable && ( - Read Only + Read Only )} {variable.name}

} > - + {useSwitch ? ( <> @@ -97,7 +96,7 @@ const VariableBox = ({ variable }: Props) => {