From 582521f419c529e251940aec378dd0993a35b2e8 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 12 Mar 2021 14:47:49 -0700 Subject: [PATCH 01/15] fix: backup restore delete all files --- .../Controllers/Api/Client/Servers/BackupController.php | 2 +- resources/scripts/api/server/backups/index.ts | 6 ++++-- .../components/server/backups/BackupContextMenu.tsx | 9 ++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/BackupController.php b/app/Http/Controllers/Api/Client/Servers/BackupController.php index 3457e1cc2..e56494c6d 100644 --- a/app/Http/Controllers/Api/Client/Servers/BackupController.php +++ b/app/Http/Controllers/Api/Client/Servers/BackupController.php @@ -213,7 +213,7 @@ class BackupController extends ClientApiController // actions against it via the Panel API. $server->update(['status' => Server::STATUS_RESTORING_BACKUP]); - $this->repository->setServer($server)->restore($backup, $url ?? null, $request->input('truncate') === 'true'); + $this->repository->setServer($server)->restore($backup, $url ?? null, $request->input('truncate')); }); return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); diff --git a/resources/scripts/api/server/backups/index.ts b/resources/scripts/api/server/backups/index.ts index 016836364..4f1311fdf 100644 --- a/resources/scripts/api/server/backups/index.ts +++ b/resources/scripts/api/server/backups/index.ts @@ -1,5 +1,7 @@ import http from '@/api/http'; -export const restoreServerBackup = async (uuid: string, backup: string): Promise => { - await http.post(`/api/client/servers/${uuid}/backups/${backup}/restore`); +export const restoreServerBackup = async (uuid: string, backup: string, truncate?: boolean): Promise => { + await http.post(`/api/client/servers/${uuid}/backups/${backup}/restore`, { + truncate, + }); }; diff --git a/resources/scripts/components/server/backups/BackupContextMenu.tsx b/resources/scripts/components/server/backups/BackupContextMenu.tsx index bef067319..42311d222 100644 --- a/resources/scripts/components/server/backups/BackupContextMenu.tsx +++ b/resources/scripts/components/server/backups/BackupContextMenu.tsx @@ -25,6 +25,7 @@ export default ({ backup }: Props) => { const setServerFromState = ServerContext.useStoreActions(actions => actions.server.setServerFromState); const [ modal, setModal ] = useState(''); const [ loading, setLoading ] = useState(false); + const [ truncate, setTruncate ] = useState(false); const { clearFlashes, clearAndAddHttpError } = useFlash(); const { mutate } = getServerBackups(); @@ -59,10 +60,10 @@ export default ({ backup }: Props) => { }); }; - const doRestorationAction = () => { + const doRestorationAction = (truncate: boolean) => { setLoading(true); clearFlashes('backups'); - restoreServerBackup(uuid, backup.uuid) + restoreServerBackup(uuid, backup.uuid, truncate) .then(() => setServerFromState(s => ({ ...s, status: 'restoring_backup', @@ -87,7 +88,7 @@ export default ({ backup }: Props) => { visible={modal === 'restore'} title={'Restore this backup?'} buttonText={'Restore backup'} - onConfirmed={() => doRestorationAction()} + onConfirmed={() => doRestorationAction(truncate)} onModalDismissed={() => setModal('')} >

@@ -108,6 +109,8 @@ export default ({ backup }: Props) => { css={tw`text-red-500! w-5! h-5! mr-2`} id={'restore_truncate'} value={'true'} + checked={truncate} + onChange={() => setTruncate(!truncate)} /> Remove all files and folders before restoring this backup. From 5653b03ddbf8d7a1b43d48c924518b3f9e498b19 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 12 Mar 2021 15:40:45 -0700 Subject: [PATCH 02/15] cleanup BackupContextMenu.tsx --- .../scripts/components/server/backups/BackupContextMenu.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/scripts/components/server/backups/BackupContextMenu.tsx b/resources/scripts/components/server/backups/BackupContextMenu.tsx index 42311d222..06f101ba6 100644 --- a/resources/scripts/components/server/backups/BackupContextMenu.tsx +++ b/resources/scripts/components/server/backups/BackupContextMenu.tsx @@ -60,7 +60,7 @@ export default ({ backup }: Props) => { }); }; - const doRestorationAction = (truncate: boolean) => { + const doRestorationAction = () => { setLoading(true); clearFlashes('backups'); restoreServerBackup(uuid, backup.uuid, truncate) @@ -88,7 +88,7 @@ export default ({ backup }: Props) => { visible={modal === 'restore'} title={'Restore this backup?'} buttonText={'Restore backup'} - onConfirmed={() => doRestorationAction(truncate)} + onConfirmed={() => doRestorationAction()} onModalDismissed={() => setModal('')} >

@@ -110,7 +110,7 @@ export default ({ backup }: Props) => { id={'restore_truncate'} value={'true'} checked={truncate} - onChange={() => setTruncate(!truncate)} + onChange={() => setTruncate(s => !s)} /> Remove all files and folders before restoring this backup. From 3358bea09fa8c872afac712226567eebe85437a7 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 15 Mar 2021 18:07:30 +0200 Subject: [PATCH 03/15] Fix universal jar renaming --- database/Seeders/eggs/minecraft/egg-forge-minecraft.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json index 986602c19..3793d7e55 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_v1", "update_url": null }, - "exported_at": "2021-02-22T19:08:49+04:00", + "exported_at": "2021-03-15T18:04:38+02: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.", @@ -15,6 +15,7 @@ "quay.io\/pterodactyl\/core:java", "quay.io\/pterodactyl\/core:java-11" ], + "file_denylist": [], "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "config": { "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", @@ -24,7 +25,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\n#Go into main direction\r\nif [ ! -d \/mnt\/server ]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nif [ ! -z ${FORGE_VERSION} ]; then\r\n DOWNLOAD_LINK=https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/${FORGE_VERSION}\/forge-${FORGE_VERSION}\r\n FORGE_JAR=forge-${FORGE_VERSION}.jar\r\nelse\r\n JSON_DATA=$(curl -sSL https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/promotions_slim.json)\r\n\r\n if [ \"${MC_VERSION}\" == \"latest\" ] || [ \"${MC_VERSION}\" == \"\" ] ; then\r\n echo -e \"getting latest recommended version of forge.\"\r\n MC_VERSION=$(echo -e ${JSON_DATA} | jq -r '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains(\"recommended\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n \tBUILD_TYPE=recommended\r\n fi\r\n\r\n if [ \"${BUILD_TYPE}\" != \"recommended\" ] && [ \"${BUILD_TYPE}\" != \"latest\" ]; then\r\n BUILD_TYPE=recommended\r\n fi\r\n\r\n echo -e \"minecraft version: ${MC_VERSION}\"\r\n echo -e \"build type: ${BUILD_TYPE}\"\r\n\r\n ## some variables for getting versions and things\r\n FILE_SITE=$(echo -e ${JSON_DATA} | jq -r '.homepage' | sed \"s\/http:\/https:\/g\")\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" --arg BUILD_TYPE \"${BUILD_TYPE}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains($BUILD_TYPE))')\r\n\r\n ## locating the forge version\r\n if [ \"${VERSION_KEY}\" == \"\" ] && [ \"${BUILD_TYPE}\" == \"recommended\" ]; then\r\n echo -e \"dropping back to latest from recommended due to there not being a recommended version of forge for the mc version requested.\"\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains(\"recommended\"))')\r\n fi\r\n\r\n ## Error if the mc version set wasn't valid.\r\n if [ \"${VERSION_KEY}\" == \"\" ] || [ \"${VERSION_KEY}\" == \"null\" ]; then\r\n \techo -e \"The install failed because there is no valid version of forge for the version on minecraft selected.\"\r\n \texit 1\r\n fi\r\n\r\n FORGE_VERSION=$(echo -e ${JSON_DATA} | jq -r --arg VERSION_KEY \"$VERSION_KEY\" '.promos | .[$VERSION_KEY]')\r\n\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ] || [ \"${MC_VERSION}\" == \"1.8.9\" ]; then\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}.jar\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ]; then\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}-universal.jar\r\n fi\r\n else\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}.jar\r\n fi\r\nfi\r\n\r\n\r\n#Adding .jar when not eding by SERVER_JARFILE\r\nif [[ ! $SERVER_JARFILE = *\\.jar ]]; then\r\n SERVER_JARFILE=\"$SERVER_JARFILE.jar\"\r\nfi\r\n\r\n#Downloading jars\r\necho -e \"Downloading forge version ${FORGE_VERSION}\"\r\necho -e \"Download link is ${DOWNLOAD_LINK}\"\r\nif [ ! -z \"${DOWNLOAD_LINK}\" ]; then \r\n if curl --output \/dev\/null --silent --head --fail ${DOWNLOAD_LINK}-installer.jar; then\r\n echo -e \"installer jar download link is valid.\"\r\n else\r\n echo -e \"link is invalid closing out\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link closing out\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\r\n#Checking if downloaded jars exist\r\nif [ ! -f .\/installer.jar ]; then\r\n echo \"!!! Error by downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"install failed\"; exit 4; }\r\n\r\nmv $FORGE_JAR $SERVER_JARFILE\r\n\r\n#Deleting installer.jar\r\necho -e \"Deleting installer.jar file.\\n\"\r\nrm -rf installer.jar", + "script": "#!\/bin\/bash\r\n# Forge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\n#Go into main direction\r\nif [ ! -d \/mnt\/server ]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nif [ ! -z ${FORGE_VERSION} ]; then\r\n DOWNLOAD_LINK=https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/${FORGE_VERSION}\/forge-${FORGE_VERSION}\r\n FORGE_JAR=forge-${FORGE_VERSION}*.jar\r\nelse\r\n JSON_DATA=$(curl -sSL https:\/\/files.minecraftforge.net\/maven\/net\/minecraftforge\/forge\/promotions_slim.json)\r\n\r\n if [ \"${MC_VERSION}\" == \"latest\" ] || [ \"${MC_VERSION}\" == \"\" ] ; then\r\n echo -e \"getting latest recommended version of forge.\"\r\n MC_VERSION=$(echo -e ${JSON_DATA} | jq -r '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains(\"recommended\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n \tBUILD_TYPE=recommended\r\n fi\r\n\r\n if [ \"${BUILD_TYPE}\" != \"recommended\" ] && [ \"${BUILD_TYPE}\" != \"latest\" ]; then\r\n BUILD_TYPE=recommended\r\n fi\r\n\r\n echo -e \"minecraft version: ${MC_VERSION}\"\r\n echo -e \"build type: ${BUILD_TYPE}\"\r\n\r\n ## some variables for getting versions and things\r\n FILE_SITE=$(echo -e ${JSON_DATA} | jq -r '.homepage' | sed \"s\/http:\/https:\/g\")\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" --arg BUILD_TYPE \"${BUILD_TYPE}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains($BUILD_TYPE))')\r\n\r\n ## locating the forge version\r\n if [ \"${VERSION_KEY}\" == \"\" ] && [ \"${BUILD_TYPE}\" == \"recommended\" ]; then\r\n echo -e \"dropping back to latest from recommended due to there not being a recommended version of forge for the mc version requested.\"\r\n VERSION_KEY=$(echo -e ${JSON_DATA} | jq -r --arg MC_VERSION \"${MC_VERSION}\" '.promos | del(.\"latest-1.7.10\") | del(.\"1.7.10-latest-1.7.10\") | to_entries[] | .key | select(contains($MC_VERSION)) | select(contains(\"recommended\"))')\r\n fi\r\n\r\n ## Error if the mc version set wasn't valid.\r\n if [ \"${VERSION_KEY}\" == \"\" ] || [ \"${VERSION_KEY}\" == \"null\" ]; then\r\n \techo -e \"The install failed because there is no valid version of forge for the version on minecraft selected.\"\r\n \texit 1\r\n fi\r\n\r\n FORGE_VERSION=$(echo -e ${JSON_DATA} | jq -r --arg VERSION_KEY \"$VERSION_KEY\" '.promos | .[$VERSION_KEY]')\r\n\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ] || [ \"${MC_VERSION}\" == \"1.8.9\" ]; then\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}.jar\r\n if [ \"${MC_VERSION}\" == \"1.7.10\" ]; then\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}-${MC_VERSION}-universal.jar\r\n fi\r\n else\r\n DOWNLOAD_LINK=${FILE_SITE}${MC_VERSION}-${FORGE_VERSION}\/forge-${MC_VERSION}-${FORGE_VERSION}\r\n FORGE_JAR=forge-${MC_VERSION}-${FORGE_VERSION}.jar\r\n fi\r\nfi\r\n\r\n\r\n#Adding .jar when not eding by SERVER_JARFILE\r\nif [[ ! $SERVER_JARFILE = *\\.jar ]]; then\r\n SERVER_JARFILE=\"$SERVER_JARFILE.jar\"\r\nfi\r\n\r\n#Downloading jars\r\necho -e \"Downloading forge version ${FORGE_VERSION}\"\r\necho -e \"Download link is ${DOWNLOAD_LINK}\"\r\nif [ ! -z \"${DOWNLOAD_LINK}\" ]; then \r\n if curl --output \/dev\/null --silent --head --fail ${DOWNLOAD_LINK}-installer.jar; then\r\n echo -e \"installer jar download link is valid.\"\r\n else\r\n echo -e \"link is invalid closing out\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link closing out\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\r\n#Checking if downloaded jars exist\r\nif [ ! -f .\/installer.jar ]; then\r\n echo \"!!! Error by downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"install failed\"; exit 4; }\r\n\r\nmv $FORGE_JAR $SERVER_JARFILE\r\n\r\n#Deleting installer.jar\r\necho -e \"Deleting installer.jar file.\\n\"\r\nrm -rf installer.jar", "container": "openjdk:8-jdk-slim", "entrypoint": "bash" } From 9057a4f9d878f0974f4538132512da88f52e53eb Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Wed, 17 Mar 2021 16:59:06 -0600 Subject: [PATCH 04/15] ui(server): fix keybinds not working in console --- resources/scripts/components/server/Console.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 6fd32a0ef..d46f6d7a1 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -145,10 +145,10 @@ export default () => { // Add support for capturing keys terminal.attachCustomKeyEventHandler((e: KeyboardEvent) => { - if (e.metaKey && e.key === 'c') { + if ((e.ctrlKey || e.metaKey) && e.key === 'c') { document.execCommand('copy'); return false; - } else if (e.metaKey && e.key === 'f') { + } else if ((e.ctrlKey || e.metaKey) && e.key === 'f') { e.preventDefault(); searchBar.show(); return false; From 6a664bc479317efb59848d53a09d165c066b68fd Mon Sep 17 00:00:00 2001 From: Martmists Date: Fri, 19 Mar 2021 23:45:44 +0100 Subject: [PATCH 05/15] Add a message for users to apply different versions --- database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json index cba10959e..3577816e9 100644 --- a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json +++ b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json @@ -38,7 +38,7 @@ }, { "name": "Server Version", - "description": "The version of Minecraft Vanilla to install. Use \"latest\" to install the latest version, or use \"snapshot\" to install the latest snapshot.", + "description": "The version of Minecraft Vanilla to install. Use \"latest\" to install the latest version, or use \"snapshot\" to install the latest snapshot. Go to Settings > Reinstall Server to apply.", "env_variable": "VANILLA_VERSION", "default_value": "latest", "user_viewable": true, From d34e374ce043791dd8f4644daa633d25180b38a6 Mon Sep 17 00:00:00 2001 From: Samuel L <51273077+ssh-sysadmin@users.noreply.github.com> Date: Sat, 20 Mar 2021 03:40:20 -0400 Subject: [PATCH 06/15] Change ByteAnia's link to a tracker-enabled one. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4bf5389ae..d98247f9b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f | [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. | | [**HostBend**](https://hostbend.com/) | HostBend offers a variety of solutions for developers, students, and others who have a tight budget but don't want to compromise quality and support. | | [**Capitol Hosting Solutions**](https://capitolsolutions.cloud/) | CHS is *the* budget friendly hosting company for Australian and American gamers, offering a variety of plans from Web Hosting to Game Servers; Custom Solutions too! | -| [**ByteAnia**](https://ByteAnia.com/) | ByteAnia offers the best performing and most affordable **Ryzen 5000 Series hosting** on the market for *unbeatable prices*! | +| [**ByteAnia**](https://byteania.com/?utm_source=pterodactyl) | ByteAnia offers the best performing and most affordable **Ryzen 5000 Series hosting** on the market for *unbeatable prices*! | ## Documentation * [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html) From 0e3d2f20dd0026105ea41ef106912b2fd5e3dbd9 Mon Sep 17 00:00:00 2001 From: Steve Coulter Date: Sat, 20 Mar 2021 17:03:30 -0700 Subject: [PATCH 07/15] typo --- .../scripts/components/dashboard/forms/SetupTwoFactorModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx index 57eefa680..c1a1db634 100644 --- a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx @@ -78,7 +78,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {

Two-factor authentication enabled

Two-factor authentication has been enabled on your account. Should you loose access to - this device you'll need to use on of the codes displayed below in order to access your + this device you'll need to use one of the codes displayed below in order to access your account.

From a7e190052919621d4d278b42700337f8970966df Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 21 Mar 2021 10:33:09 -0700 Subject: [PATCH 08/15] Fix UI for mobile views when showing docker images; closes #3186 --- .../scripts/components/server/startup/StartupContainer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/scripts/components/server/startup/StartupContainer.tsx b/resources/scripts/components/server/startup/StartupContainer.tsx index 4ec8c413b..b5bb1fea5 100644 --- a/resources/scripts/components/server/startup/StartupContainer.tsx +++ b/resources/scripts/components/server/startup/StartupContainer.tsx @@ -78,7 +78,7 @@ const StartupContainer = () => { /> : -

+

@@ -86,7 +86,7 @@ const StartupContainer = () => {

- + {data.dockerImages.length > 1 && !isCustomImage ? <> From 8c7d785c9eff304c7f3b0c9a2666dd17cf5b6ea0 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 21 Mar 2021 10:43:01 -0700 Subject: [PATCH 09/15] Ensure a created_at value is set on recovery tokens; closes #3163 --- .../Service/User/TwoFactorAuthenticationTokenInvalid.php | 7 +++++++ app/Http/Controllers/Api/Client/TwoFactorController.php | 3 +-- app/Services/Users/ToggleTwoFactorService.php | 5 ++++- tests/Integration/Api/Client/TwoFactorControllerTest.php | 5 +++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php b/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php index a4ea4dd09..b7b819b44 100644 --- a/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php +++ b/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php @@ -6,4 +6,11 @@ use Pterodactyl\Exceptions\DisplayException; class TwoFactorAuthenticationTokenInvalid extends DisplayException { + /** + * TwoFactorAuthenticationTokenInvalid constructor. + */ + public function __construct() + { + parent::__construct('The provided two-factor authentication token was not valid.'); + } } diff --git a/app/Http/Controllers/Api/Client/TwoFactorController.php b/app/Http/Controllers/Api/Client/TwoFactorController.php index 4f7145add..07eeb2972 100644 --- a/app/Http/Controllers/Api/Client/TwoFactorController.php +++ b/app/Http/Controllers/Api/Client/TwoFactorController.php @@ -72,12 +72,11 @@ class TwoFactorController extends ClientApiController * * @return \Illuminate\Http\JsonResponse * + * @throws \Throwable * @throws \Illuminate\Validation\ValidationException * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid */ public function store(Request $request) diff --git a/app/Services/Users/ToggleTwoFactorService.php b/app/Services/Users/ToggleTwoFactorService.php index 908fc35d6..28faff7f1 100644 --- a/app/Services/Users/ToggleTwoFactorService.php +++ b/app/Services/Users/ToggleTwoFactorService.php @@ -74,7 +74,7 @@ class ToggleTwoFactorService $isValidToken = $this->google2FA->verifyKey($secret, $token, config()->get('pterodactyl.auth.2fa.window')); if (!$isValidToken) { - throw new TwoFactorAuthenticationTokenInvalid('The token provided is not valid.'); + throw new TwoFactorAuthenticationTokenInvalid(); } return $this->connection->transaction(function () use ($user, $toggleState) { @@ -94,6 +94,9 @@ class ToggleTwoFactorService $inserts[] = [ 'user_id' => $user->id, 'token' => password_hash($token, PASSWORD_DEFAULT), + // insert() won't actually set the time on the models, so make sure we do this + // manually here. + 'created_at' => Carbon::now(), ]; $tokens[] = $token; diff --git a/tests/Integration/Api/Client/TwoFactorControllerTest.php b/tests/Integration/Api/Client/TwoFactorControllerTest.php index 8af7e31a8..903efb853 100644 --- a/tests/Integration/Api/Client/TwoFactorControllerTest.php +++ b/tests/Integration/Api/Client/TwoFactorControllerTest.php @@ -101,6 +101,11 @@ class TwoFactorControllerTest extends ClientApiIntegrationTestCase $tokens = RecoveryToken::query()->where('user_id', $user->id)->get(); $this->assertCount(10, $tokens); $this->assertStringStartsWith('$2y$10$', $tokens[0]->token); + // Ensure the recovery tokens that were created include a "created_at" timestamp + // value on them. + // + // @see https://github.com/pterodactyl/panel/issues/3163 + $this->assertNotNull($tokens[0]->created_at); $tokens = $tokens->pluck('token')->toArray(); From aa0b7977bba64fef72e9a54443ae34981910dc5b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 21 Mar 2021 10:49:23 -0700 Subject: [PATCH 10/15] Fix error spam in logs due to missing cron month --- app/Models/Schedule.php | 3 +- ...n_month_field_to_have_value_if_missing.php | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php index 432f4d5f8..240873cac 100644 --- a/app/Models/Schedule.php +++ b/app/Models/Schedule.php @@ -122,13 +122,14 @@ class Schedule extends Model * Returns the schedule's execution crontab entry as a string. * * @return \Carbon\CarbonImmutable + * @throws \Exception */ public function getNextRunDate() { $formatted = sprintf('%s %s %s %s %s', $this->cron_minute, $this->cron_hour, $this->cron_day_of_month, $this->cron_month, $this->cron_day_of_week); return CarbonImmutable::createFromTimestamp( - CronExpression::factory($formatted)->getNextRunDate()->getTimestamp() + (new CronExpression($formatted))->getNextRunDate()->getTimestamp() ); } diff --git a/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php b/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php new file mode 100644 index 000000000..63448c220 --- /dev/null +++ b/database/migrations/2021_03_21_104718_force_cron_month_field_to_have_value_if_missing.php @@ -0,0 +1,31 @@ + Date: Sun, 21 Mar 2021 10:53:27 -0700 Subject: [PATCH 11/15] Don't force enable bungeecord query port; closes #3175 --- database/Seeders/eggs/minecraft/egg-bungeecord.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/Seeders/eggs/minecraft/egg-bungeecord.json b/database/Seeders/eggs/minecraft/egg-bungeecord.json index e768c78d2..23103f54d 100644 --- a/database/Seeders/eggs/minecraft/egg-bungeecord.json +++ b/database/Seeders/eggs/minecraft/egg-bungeecord.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-11-03T04:22:56+00:00", + "exported_at": "2021-03-21T17:52:00+00:00", "name": "Bungeecord", "author": "support@pterodactyl.io", "description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.", @@ -11,7 +11,7 @@ "images": ["quay.io\/pterodactyl\/core:java", "quay.io\/pterodactyl\/core:java-11"], "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "config": { - "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_enabled\": true,\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}", + "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \"Listening on \",\r\n \"userInteraction\": [\r\n \"Listening on \/0.0.0.0:25577\"\r\n ]\r\n}", "logs": "{\r\n \"custom\": false,\r\n \"location\": \"proxy.log.0\"\r\n}", "stop": "end" From 7676f7dd662786bb7bcc589cc2a3cd3f353bb9e7 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 21 Mar 2021 11:49:42 -0700 Subject: [PATCH 12/15] Allow modification of server build settings even when node is offline --- .../Servers/BuildModificationService.php | 16 +++++++-- .../Servers/BuildModificationServiceTest.php | 33 ++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php index 6a96075c5..9a8c1c3c4 100644 --- a/app/Services/Servers/BuildModificationService.php +++ b/app/Services/Servers/BuildModificationService.php @@ -5,10 +5,12 @@ namespace Pterodactyl\Services\Servers; use Illuminate\Support\Arr; use Pterodactyl\Models\Server; use Pterodactyl\Models\Allocation; +use Illuminate\Support\Facades\Log; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Exceptions\DisplayException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Repositories\Wings\DaemonServerRepository; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class BuildModificationService { @@ -78,10 +80,18 @@ class BuildModificationService $updateData = $this->structureService->handle($server); + // Because Wings always fetches an updated configuration from the Panel when booting + // a server this type of exception can be safely "ignored" and just written to the logs. + // Ideally this request succeedes so we can apply resource modifications on the fly + // but if it fails it isn't the end of the world. if (!empty($updateData['build'])) { - $this->daemonServerRepository->setServer($server)->update([ - 'build' => $updateData['build'], - ]); + try { + $this->daemonServerRepository->setServer($server)->update([ + 'build' => $updateData['build'], + ]); + } catch (DaemonConnectionException $exception) { + Log::warning($exception, ['server_id' => $server->id]); + } } $this->connection->commit(); diff --git a/tests/Integration/Services/Servers/BuildModificationServiceTest.php b/tests/Integration/Services/Servers/BuildModificationServiceTest.php index 359dff67e..9a61caa34 100644 --- a/tests/Integration/Services/Servers/BuildModificationServiceTest.php +++ b/tests/Integration/Services/Servers/BuildModificationServiceTest.php @@ -3,12 +3,17 @@ namespace Pterodactyl\Tests\Integration\Services\Servers; use Mockery; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Response; use Pterodactyl\Models\Server; use Pterodactyl\Models\Allocation; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\TransferException; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Tests\Integration\IntegrationTestCase; use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Services\Servers\BuildModificationService; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class BuildModificationServiceTest extends IntegrationTestCase { @@ -149,6 +154,30 @@ class BuildModificationServiceTest extends IntegrationTestCase $this->assertSame(20, $response->allocation_limit); } + /** + * Test that an exception when connecting to the Wings instance is properly ignored + * when making updates. This allows for a server to be modified even when the Wings + * node is offline. + */ + public function testConnectionExceptionIsIgnoredWhenUpdatingServerSettings() + { + $server = $this->createServerModel(); + + $this->daemonServerRepository->expects('setServer->update')->andThrows( + new DaemonConnectionException( + new RequestException('Bad request', new Request('GET', '/test'), new Response()) + ) + ); + + $response = $this->getService()->handle($server, ['memory' => 256, 'disk' => 10240]); + + $this->assertInstanceOf(Server::class, $response); + $this->assertSame(256, $response->memory); + $this->assertSame(10240, $response->disk); + + $this->assertDatabaseHas('servers', ['id' => $response->id, 'memory' => 256, 'disk' => 10240]); + } + /** * Test that no exception is thrown if we are only removing an allocation. */ @@ -215,7 +244,9 @@ class BuildModificationServiceTest extends IntegrationTestCase /** * Test that any changes we made to the server or allocations are rolled back if there is an - * exception while performing any action. + * exception while performing any action. This is different than the connection exception + * test which should properly ignore connection issues. We want any other type of exception + * to properly be thrown back to the caller. */ public function testThatUpdatesAreRolledBackIfExceptionIsEncountered() { From 29b4b52397df65ce0b5b3901f53684b611caf5a5 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 21 Mar 2021 12:07:24 -0700 Subject: [PATCH 13/15] Set the current page in the dashboard URL to allow easy refreshing; closes #2993 --- .../dashboard/AccountOverviewContainer.tsx | 6 ++++-- .../dashboard/DashboardContainer.tsx | 20 ++++++++++++++++++- resources/scripts/routers/DashboardRouter.tsx | 16 +++++++++++---- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/resources/scripts/components/dashboard/AccountOverviewContainer.tsx b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx index 47161f793..f8cea7111 100644 --- a/resources/scripts/components/dashboard/AccountOverviewContainer.tsx +++ b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx @@ -7,8 +7,8 @@ import PageContentBlock from '@/components/elements/PageContentBlock'; import tw from 'twin.macro'; import { breakpoint } from '@/theme'; import styled from 'styled-components/macro'; -import { RouteComponentProps } from 'react-router'; import MessageBox from '@/components/MessageBox'; +import { useLocation } from 'react-router-dom'; const Container = styled.div` ${tw`flex flex-wrap`}; @@ -26,7 +26,9 @@ const Container = styled.div` } `; -export default ({ location: { state } }: RouteComponentProps) => { +export default () => { + const { state } = useLocation(); + return ( {state?.twoFactorRedirect && diff --git a/resources/scripts/components/dashboard/DashboardContainer.tsx b/resources/scripts/components/dashboard/DashboardContainer.tsx index 72357fd65..c04ca0820 100644 --- a/resources/scripts/components/dashboard/DashboardContainer.tsx +++ b/resources/scripts/components/dashboard/DashboardContainer.tsx @@ -12,10 +12,14 @@ import tw from 'twin.macro'; import useSWR from 'swr'; import { PaginatedResult } from '@/api/http'; import Pagination from '@/components/elements/Pagination'; +import { useLocation } from 'react-router-dom'; export default () => { + const { search } = useLocation(); + const defaultPage = Number(new URLSearchParams(search).get('page') || '1'); + + const [ page, setPage ] = useState((!isNaN(defaultPage) && defaultPage > 0) ? defaultPage : 1); const { clearFlashes, clearAndAddHttpError } = useFlash(); - const [ page, setPage ] = useState(1); const uuid = useStoreState(state => state.user.data!.uuid); const rootAdmin = useStoreState(state => state.user.data!.rootAdmin); const [ showOnlyAdmin, setShowOnlyAdmin ] = usePersistedState(`${uuid}:show_all_servers`, false); @@ -25,6 +29,20 @@ export default () => { () => getServers({ page, type: showOnlyAdmin ? 'admin' : undefined }), ); + useEffect(() => { + if (!servers) return; + if (servers.pagination.currentPage > 1 && !servers.items.length) { + setPage(1); + } + }, [ servers?.pagination.currentPage ]); + + useEffect(() => { + // Don't use react-router to handle changing this part of the URL, otherwise it + // triggers a needless re-render. We just want to track this in the URL incase the + // user refreshes the page. + window.history.replaceState(null, document.title, `/${page <= 1 ? '' : `?page=${page}`}`); + }, [ page ]); + useEffect(() => { if (error) clearAndAddHttpError({ key: 'dashboard', error }); if (!error) clearFlashes('dashboard'); diff --git a/resources/scripts/routers/DashboardRouter.tsx b/resources/scripts/routers/DashboardRouter.tsx index 933327b53..513cc3fa8 100644 --- a/resources/scripts/routers/DashboardRouter.tsx +++ b/resources/scripts/routers/DashboardRouter.tsx @@ -21,10 +21,18 @@ export default ({ location }: RouteComponentProps) => ( } - - - - + + + + + + + + + + + + From c7375f09d39be048283a22c0b02d2547af2c6f74 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 21 Mar 2021 12:11:40 -0700 Subject: [PATCH 14/15] Maybe fix issue with docker logs? ref #3045 --- docker-compose.example.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.example.yml b/docker-compose.example.yml index deaf6cade..ee2f875ea 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -59,7 +59,7 @@ services: - "/srv/pterodactyl/var/:/app/var/" - "/srv/pterodactyl/nginx/:/etc/nginx/conf.d/" - "/srv/pterodactyl/certs/:/etc/letsencrypt/" - - "/srv/pterodactyl/logs/:/var/log/" + - "/srv/pterodactyl/logs/:/app/storage/logs" environment: <<: *panel-environment <<: *mail-environment From 9b46d59045956378b3aeb275bb87c1933e0b3e6c Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 21 Mar 2021 12:29:18 -0700 Subject: [PATCH 15/15] Cache resource lookup results for 20 seconds for each server --- .../Servers/ResourceUtilizationController.php | 22 +++++++++++++++---- .../components/dashboard/ServerRow.tsx | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php b/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php index 4cf0d249b..72901c8bd 100644 --- a/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php +++ b/app/Http/Controllers/Api/Client/Servers/ResourceUtilizationController.php @@ -2,7 +2,9 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; +use Carbon\Carbon; use Pterodactyl\Models\Server; +use Illuminate\Cache\Repository; use Pterodactyl\Transformers\Api\Client\StatsTransformer; use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; @@ -13,26 +15,38 @@ class ResourceUtilizationController extends ClientApiController /** * @var \Pterodactyl\Repositories\Wings\DaemonServerRepository */ - private $repository; + private DaemonServerRepository $repository; + + /** + * @var \Illuminate\Cache\Repository + */ + private Repository $cache; /** * ResourceUtilizationController constructor. */ - public function __construct(DaemonServerRepository $repository) + public function __construct(Repository $cache, DaemonServerRepository $repository) { parent::__construct(); + $this->cache = $cache; $this->repository = $repository; } /** - * Return the current resource utilization for a server. + * Return the current resource utilization for a server. This value is cached for up to + * 20 seconds at a time to ensure that repeated requests to this endpoint do not cause + * a flood of unnecessary API calls. * * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function __invoke(GetServerRequest $request, Server $server): array { - $stats = $this->repository->setServer($server)->getDetails(); + $stats = $this->cache + ->tags(['resources']) + ->remember($server->uuid, Carbon::now()->addSeconds(20), function () use ($server) { + return $this->repository->setServer($server)->getDetails(); + }); return $this->fractal->item($stats) ->transformWith($this->getTransformer(StatsTransformer::class)) diff --git a/resources/scripts/components/dashboard/ServerRow.tsx b/resources/scripts/components/dashboard/ServerRow.tsx index 40eebefac..2b33dbab7 100644 --- a/resources/scripts/components/dashboard/ServerRow.tsx +++ b/resources/scripts/components/dashboard/ServerRow.tsx @@ -59,7 +59,7 @@ export default ({ server, className }: { server: Server; className?: string }) = getStats().then(() => { // @ts-ignore - interval.current = setInterval(() => getStats(), 20000); + interval.current = setInterval(() => getStats(), 30000); }); return () => {