diff --git a/.env.example b/.env.example index 72a0a764e..e2e9d2dce 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,7 @@ APP_DELETE_MINUTES=10 APP_ENVIRONMENT_ONLY=true LOG_CHANNEL=daily APP_LOCALE=en +APP_URL=http://panel.example.com DB_HOST=127.0.0.1 DB_PORT=3306 @@ -30,7 +31,7 @@ MAILGUN_ENDPOINT=api.mailgun.net # mail servers such as Gmail to reject your mail. # # @see: https://github.com/pterodactyl/panel/pull/3110 -# SERVER_NAME=panel.yourdomain.com +# SERVER_NAME=panel.example.com QUEUE_HIGH=high QUEUE_STANDARD=standard diff --git a/.github/docker/README.md b/.github/docker/README.md index 434e509d6..d8e9f9c14 100644 --- a/.github/docker/README.md +++ b/.github/docker/README.md @@ -33,7 +33,7 @@ Note: If your `APP_URL` starts with `https://` you need to provide an `LETSENCRY | ------------------- | ------------------------------------------------------------------------------ | -------- | | `APP_URL` | The URL the panel will be reachable with (including protocol) | yes | | `APP_TIMEZONE` | The timezone to use for the panel | yes | -| `LETSENCRYPT_EMAIL` | The email used for letsencrypt certificate generation | yes | +| `LE_EMAIL` | The email used for letsencrypt certificate generation | yes | | `DB_HOST` | The host of the mysql instance | yes | | `DB_PORT` | The port of the mysql instance | yes | | `DB_DATABASE` | The name of the mysql database | yes | diff --git a/.github/docker/entrypoint.sh b/.github/docker/entrypoint.sh index e2df6439c..b2460cde9 100644 --- a/.github/docker/entrypoint.sh +++ b/.github/docker/entrypoint.sh @@ -57,7 +57,7 @@ fi ## check for DB up before starting the panel echo "Checking database status." -until nc -z -v -w30 $DB_HOST 3306 +until nc -z -v -w30 $DB_HOST $DB_PORT do echo "Waiting for database connection..." # wait for 1 seconds before check again diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1aa6125f4..c9f5c68ae 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -36,6 +36,7 @@ jobs: if: "!contains(github.ref, 'develop')" with: push: true + platforms: linux/amd64,linux/arm64 tags: ${{ steps.docker_meta.outputs.tags }} labels: ${{ steps.docker_meta.outputs.labels }} - name: Release Development Build @@ -43,5 +44,6 @@ jobs: if: "contains(github.ref, 'develop')" with: push: ${{ github.event_name != 'pull_request' }} + platforms: linux/amd64,linux/arm64 tags: ${{ steps.docker_meta.outputs.tags }} labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/CHANGELOG.md b/CHANGELOG.md index bc1e30d3e..35d87529b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ 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.6.6 +### Fixed +* **[security]** Fixes a CSRF vulnerability for both the administrative test email endpoint and node auto-deployment token generation endpoint. [GHSA-wwgq-9jhf-qgw6](https://github.com/pterodactyl/panel/security/advisories/GHSA-wwgq-9jhf-qgw6) + +### Changed +* Updates Minecraft eggs to include latest Java 17 yolk by default. + ## v1.6.5 ### Fixed * Fixes broken application API endpoints due to changes introduced with session management in 1.6.4. diff --git a/Dockerfile b/Dockerfile index 8d50d3845..73f79bf86 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # Build the assets that are needed for the frontend. This build stage is then discarded # since we won't need NodeJS anymore in the future. This Docker image ships a final production # level distribution of Pterodactyl. -FROM mhart/alpine-node:14 +FROM --platform=$TARGETOS/$TARGETARCH mhart/alpine-node:14 WORKDIR /app COPY . ./ RUN yarn install --frozen-lockfile \ @@ -10,7 +10,7 @@ RUN yarn install --frozen-lockfile \ # Stage 1: # Build the actual container with all of the needed PHP dependencies that will run the application. -FROM php:7.4-fpm-alpine +FROM --platform=$TARGETOS/$TARGETARCH php:7.4-fpm-alpine WORKDIR /app COPY . ./ COPY --from=0 /app/public/assets ./public/assets diff --git a/LICENSE.md b/LICENSE.md index 1fb886e97..cb0e2a9d9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,8 @@ # The MIT License (MIT) ``` -Copyright (c) 2015 - 2021 Dane Everitt and Contributors +Pterodactyl® +Copyright © Dane Everitt and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6d0eec5ad..5357c02c8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ![GitHub contributors](https://img.shields.io/github/contributors/pterodactyl/panel?style=for-the-badge) # Pterodactyl Panel -Pterodactyl is an open-source game server management panel built with PHP 7, React, and Go. Designed with security +Pterodactyl® is a free, open-source game server management panel built with PHP, React, and Go. Designed with security in mind, Pterodactyl runs all game servers in isolated Docker containers while exposing a beautiful and intuitive UI to end users. @@ -14,6 +14,12 @@ 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/DaneEveritt) @@ -37,16 +43,11 @@ I would like to extend my sincere thanks to the following sponsors for helping f | [**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.| | [**RocketNode**](https://rocketnode.net) | RocketNode is a VPS and Game Server provider that offers the best performing VPS and Game hosting Solutions at affordable prices! | - -## 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) +| [**HostEZ**](https://hostez.io) | Providing North America Valheim, Minecraft and other popular games with low latency, high uptime and maximum availability. EZ! | ### Supported Games -We support a huge variety of games by utilizing Docker containers to isolate each instance, giving you the power to -host your games across the world without having to bloat each physical machine with additional dependencies. +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. Some of our core supported games include: @@ -73,27 +74,6 @@ and there are plenty more games available provided by the community. Some of the * [and many more...](https://github.com/parkervcp/eggs) ## License -``` -Copyright (c) 2015 - 2021 Dane Everitt and Contributors +Pterodactyl® Copyright © 2015 - 2022 Dane Everitt and contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -``` - -Some Javascript and CSS used within the panel are licensed under a `MIT` or `Apache 2.0` license. Please check their -respective header files for more information. +Code released under the [MIT License](./LICENSE.md). diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php index cfe1d7981..2746d1a4f 100644 --- a/app/Console/Commands/Environment/AppSettingsCommand.php +++ b/app/Console/Commands/Environment/AppSettingsCommand.php @@ -12,6 +12,7 @@ namespace Pterodactyl\Console\Commands\Environment; use DateTimeZone; use Illuminate\Console\Command; use Illuminate\Contracts\Console\Kernel; +use Illuminate\Validation\Factory as ValidatorFactory; use Pterodactyl\Traits\Commands\EnvironmentWriterTrait; use Illuminate\Contracts\Config\Repository as ConfigRepository; @@ -78,12 +79,13 @@ class AppSettingsCommand extends Command /** * AppSettingsCommand constructor. */ - public function __construct(ConfigRepository $config, Kernel $command) + public function __construct(ConfigRepository $config, Kernel $command, ValidatorFactory $validator) { parent::__construct(); - $this->command = $command; $this->config = $config; + $this->command = $command; + $this->validator = $validator; } /** @@ -103,6 +105,18 @@ class AppSettingsCommand extends Command $this->config->get('pterodactyl.service.author', 'unknown@unknown.com') ); + $validator = $this->validator->make( + ['email' => $this->variables['APP_SERVICE_AUTHOR']], + ['email' => 'email'] + ); + + if ($validator->fails()) { + foreach ($validator->errors()->all() as $error) { + $this->output->error($error); + } + return 1; + } + $this->output->comment(trans('command/messages.environment.app.app_url_help')); $this->variables['APP_URL'] = $this->option('url') ?? $this->ask( trans('command/messages.environment.app.app_url'), diff --git a/database/Seeders/eggs/minecraft/egg-bungeecord.json b/database/Seeders/eggs/minecraft/egg-bungeecord.json index 0df945cf0..9a538a8e1 100644 --- a/database/Seeders/eggs/minecraft/egg-bungeecord.json +++ b/database/Seeders/eggs/minecraft/egg-bungeecord.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-07-04T19:18:34-04:00", + "exported_at": "2021-11-14T19:23:12+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.", @@ -15,14 +15,15 @@ "images": [ "ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_16", + "ghcr.io\/pterodactyl\/yolks:java_17" ], "file_denylist": [], "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_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}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"proxy.log.0\"\r\n}", + "logs": "{}", "stop": "end" }, "scripts": { diff --git a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json index 804f65a8d..7f889f1a9 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-10-22T19:29:26+02:00", + "exported_at": "2021-12-11T22:51:29+00: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.", @@ -13,12 +13,13 @@ "java_version" ], "images": [ - "ghcr.io\/pterodactyl\/yolks:java_8", + "ghcr.io\/pterodactyl\/yolks:java_17", + "ghcr.io\/pterodactyl\/yolks:java_16", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_8" ], "file_denylist": [], - "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true $( [ ! -f unix_args.txt ] && printf %s \"-jar {{SERVER_JARFILE}}\" || printf %s \"@unix_args.txt\" )", + "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true $( [[ ! -f unix_args.txt ]] && printf %s \"-jar {{SERVER_JARFILE}}\" || printf %s \"@unix_args.txt\" )", "config": { "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", @@ -27,7 +28,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\nif [ ! -d \/mnt\/server ]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n# Remove spaces from the version number to avoid issues with curl\r\nFORGE_VERSION=\"$(echo \"$FORGE_VERSION\" | tr -d ' ')\"\r\nMC_VERSION=\"$(echo \"$MC_VERSION\" | tr -d ' ')\"\r\n\r\nif [ ! -z ${FORGE_VERSION} ]; then\r\n DOWNLOAD_LINK=https:\/\/maven.minecraftforge.net\/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 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=latest\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\tFILE_SITE=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/\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 of 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\n\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. Exiting now\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link provided. Exiting now\"\r\n exit 3\r\nfi\r\n\r\ncurl -s -o installer.jar -sS ${DOWNLOAD_LINK}-installer.jar\r\n\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# Delete args file to support downgrading to versions below 1.17\r\nif [ -f unix_args.txt ]; then\r\n rm unix_args.txt\r\n exit\r\nfi\r\n\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\nif [[ $MC_VERSION == *\"1.17.\"* || $MC_VERSION == *\"1.18.\"* || $FORGE_VERSION == *\"1.17.\"* || $FORGE_VERSION == *\"1.18.\"* ]]; then\r\n # Create a symlink for 1.17+ Forge args\r\n ln -sf libraries\/net\/minecraftforge\/forge\/*\/unix_args.txt unix_args.txt\r\n else\r\n # For versions below 1.17 that ship with jar delete installer.jar\r\n mv $FORGE_JAR $SERVER_JARFILE\r\n echo -e \"Deleting installer.jar file.\\n\"\r\n rm -rf installer.jar\r\nfi", + "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\nif [[ ! -d \/mnt\/server ]]; then\r\n mkdir \/mnt\/server\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n# Remove spaces from the version number to avoid issues with curl\r\nFORGE_VERSION=\"$(echo \"$FORGE_VERSION\" | tr -d ' ')\"\r\nMC_VERSION=\"$(echo \"$MC_VERSION\" | tr -d ' ')\"\r\n\r\nif [[ ! -z ${FORGE_VERSION} ]]; then\r\n DOWNLOAD_LINK=https:\/\/maven.minecraftforge.net\/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 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(\"latest\")) | split(\"-\")[0]' | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -1)\r\n BUILD_TYPE=latest\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=https:\/\/maven.minecraftforge.net\/net\/minecraftforge\/forge\/\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(\"latest\"))')\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 echo -e \"The install failed because there is no valid version of forge for the version of minecraft selected.\"\r\n exit 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#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\n\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. Exiting now\"\r\n exit 2\r\n fi\r\nelse\r\n echo -e \"no download link provided. Exiting now\"\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 downloading forge version ${FORGE_VERSION} !!!\"\r\n exit\r\nfi\r\n\r\nfunction unix_args {\r\n echo -e \"Detected Forge 1.17 or newer version. Setting up forge unix args.\"\r\n ln -sf libraries\/net\/minecraftforge\/forge\/*\/unix_args.txt unix_args.txt\r\n}\r\n\r\n# Delete args to support downgrading\/upgrading\r\nrm -rf libraries\/net\/minecraftforge\/forge\r\nrm unix_args.txt\r\n\r\n#Installing server\r\necho -e \"Installing forge server.\\n\"\r\njava -jar installer.jar --installServer || { echo -e \"install failed using Forge version ${FORGE_VERSION} and Minecraft version ${MINECRAFT_VERSION}\"; exit 4; }\r\n\r\n# Check if we need a symlink for 1.17+ Forge JPMS args\r\nif [[ $MC_VERSION =~ ^1\\.(17|18|19|20|21|22|23) || $FORGE_VERSION =~ ^1\\.(17|18|19|20|21|22|23) ]]; then\r\n unix_args\r\n\r\n# Check if someone has set MC to latest but overwrote it with older Forge version, otherwise we would have false positives\r\nelif [[ $MC_VERSION == \"latest\" && $FORGE_VERSION =~ ^1\\.(17|18|19|20|21|22|23) ]]; then\r\n unix_args\r\nelse\r\n # For versions below 1.17 that ship with jar\r\n mv $FORGE_JAR $SERVER_JARFILE\r\nfi\r\n\r\necho -e \"Deleting installer.jar file.\\n\"\r\nrm -rf installer.jar", "container": "openjdk:8-jdk-slim", "entrypoint": "bash" } @@ -67,7 +68,7 @@ "default_value": "", "user_viewable": true, "user_editable": true, - "rules": "nullable|string|max:20" + "rules": "nullable|string|max:25" } ] -} +} \ No newline at end of file diff --git a/database/Seeders/eggs/minecraft/egg-paper.json b/database/Seeders/eggs/minecraft/egg-paper.json index 2c3f130c2..284ee97fb 100644 --- a/database/Seeders/eggs/minecraft/egg-paper.json +++ b/database/Seeders/eggs/minecraft/egg-paper.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-10-22T19:19:11+02:00", + "exported_at": "2021-11-14T19:21:07+00:00", "name": "Paper", "author": "parker@pterodactyl.io", "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", @@ -15,7 +15,8 @@ "images": [ "ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_16", + "ghcr.io\/pterodactyl\/yolks:java_17" ], "file_denylist": [], "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}", diff --git a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json index 64310fcf1..9106d1a72 100644 --- a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json +++ b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-10-22T19:19:23+02:00", + "exported_at": "2021-11-14T19:18:30+00:00", "name": "Vanilla Minecraft", "author": "support@pterodactyl.io", "description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.", @@ -15,14 +15,15 @@ "images": [ "ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_11", - "ghcr.io\/pterodactyl\/yolks:java_16" + "ghcr.io\/pterodactyl\/yolks:java_16", + "ghcr.io\/pterodactyl\/yolks:java_17" ], "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 \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", - "logs": "{\r\n \"custom\": false,\r\n \"location\": \"logs\/latest.log\"\r\n}", + "logs": "{}", "stop": "stop" }, "scripts": { diff --git a/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json b/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json index 012bf62f7..0ea63b6aa 100644 --- a/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json +++ b/database/Seeders/eggs/source-engine/egg-ark--survival-evolved.json @@ -48,7 +48,7 @@ }, { "name": "Server Map", - "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction, Valguero_P, Genesis, CrystalIsles, Gen2", + "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction, Valguero_P, Genesis, CrystalIsles, Gen2, LostIsland", "env_variable": "SERVER_MAP", "default_value": "TheIsland", "user_viewable": true, @@ -119,4 +119,4 @@ "rules": "nullable|string" } ] -} \ No newline at end of file +} diff --git a/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json index a9371a3ea..255c9934e 100644 --- a/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json +++ b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json @@ -4,11 +4,13 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-06-05T16:19:30-04:00", + "exported_at": "2021-09-10T14:36:37-04:00", "name": "Counter-Strike: Global Offensive", "author": "support@pterodactyl.io", "description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.", - "features": null, + "features": [ + "gsl_token" + ], "images": [ "ghcr.io\/pterodactyl\/games:source" ], @@ -16,19 +18,18 @@ "startup": ".\/srcds_run -game csgo -console -port {{SERVER_PORT}} +ip 0.0.0.0 +map {{SRCDS_MAP}} -strictportbind -norestart +sv_setsteamaccount {{STEAM_ACC}}", "config": { "files": "{}", - "startup": "{\r\n \"done\": \"Connection to Steam servers successful\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", + "startup": "{\r\n \"done\": \"Connection to Steam servers successful\"\r\n}", + "logs": "{}", "stop": "quit" }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'ubuntu:18.04'\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", "container": "ghcr.io\/pterodactyl\/installers:debian", "entrypoint": "bash" } }, - "variables": [ - { + "variables": [{ "name": "Map", "description": "The default map for the server.", "env_variable": "SRCDS_MAP", diff --git a/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json b/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json index 881d31ec6..53133a67b 100644 --- a/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json +++ b/database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json @@ -4,7 +4,7 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-06-05T16:24:05-04:00", + "exported_at": "2021-09-10T14:36:22-04:00", "name": "Custom Source Engine Game", "author": "support@pterodactyl.io", "description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.", @@ -16,19 +16,18 @@ "startup": ".\/srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", "config": { "files": "{}", - "startup": "{\r\n \"done\": \"gameserver Steam ID\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", + "startup": "{\r\n \"done\": \"gameserver Steam ID\"\r\n}", + "logs": "{}", "stop": "quit" }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n##\r\n#\r\n# Variables\r\n# STEAM_USER, STEAM_PASS, STEAM_AUTH - Steam user setup. If a user has 2fa enabled it will most likely fail due to timeout. Leave blank for anon install.\r\n# WINDOWS_INSTALL - if it's a windows server you want to install set to 1\r\n# SRCDS_APPID - steam app id ffound here - https:\/\/developer.valvesoftware.com\/wiki\/Dedicated_Servers_List\r\n# EXTRA_FLAGS - when a server has extra glas for things like beta installs or updates.\r\n#\r\n##\r\n\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so", "container": "ghcr.io\/pterodactyl\/installers:debian", "entrypoint": "bash" } }, - "variables": [ - { + "variables": [{ "name": "Game ID", "description": "The ID corresponding to the game to download and run using SRCDS.", "env_variable": "SRCDS_APPID", @@ -83,4 +82,4 @@ "rules": "nullable|string" } ] -} \ No newline at end of file +} diff --git a/database/Seeders/eggs/source-engine/egg-garrys-mod.json b/database/Seeders/eggs/source-engine/egg-garrys-mod.json index 93de416a6..f322507c6 100644 --- a/database/Seeders/eggs/source-engine/egg-garrys-mod.json +++ b/database/Seeders/eggs/source-engine/egg-garrys-mod.json @@ -4,11 +4,13 @@ "version": "PTDL_v1", "update_url": null }, - "exported_at": "2021-08-27T00:12:31-04:00", + "exported_at": "2021-12-04T18:47:10+00:00", "name": "Garrys Mod", "author": "support@pterodactyl.io", "description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.", - "features": null, + "features": [ + "gsl_token" + ], "images": [ "ghcr.io\/pterodactyl\/games:source" ], @@ -22,7 +24,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n# Image to install with is 'debian:buster-slim'\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so\r\n\r\n# Creating needed default files for the game\r\ncd \/mnt\/server\/garrysmod\/lua\/autorun\/server\r\necho '\r\n-- Docs: https:\/\/wiki.garrysmod.com\/page\/resource\/AddWorkshop\r\n-- Place the ID of the workshop addon you want to be downloaded to people who join your server, not the collection ID\r\n-- Use https:\/\/beta.configcreator.com\/create\/gmod\/resources.lua to easily create a list based on your collection ID\r\n\r\nresource.AddWorkshop( \"\" )\r\n' > workshop.lua\r\n\r\ncd \/mnt\/server\/garrysmod\/cfg\r\necho '\r\n\/\/ Please do not set RCon in here, use the startup parameters.\r\n\r\nhostname\t\t\"New Gmod Server\"\r\nsv_password\t\t\"\"\r\nsv_loadingurl \"\"\r\nsv_downloadurl \"\"\r\n\r\n\/\/ Steam Server List Settings\r\n\/\/ sv_location \"eu\"\r\nsv_region \"255\"\r\nsv_lan \"0\"\r\nsv_max_queries_sec_global \"30000\"\r\nsv_max_queries_window \"45\"\r\nsv_max_queries_sec \"5\"\r\n\r\n\/\/ Server Limits\r\nsbox_maxprops\t\t100\r\nsbox_maxragdolls\t5\r\nsbox_maxnpcs\t\t10\r\nsbox_maxballoons\t10\r\nsbox_maxeffects\t\t10\r\nsbox_maxdynamite\t10\r\nsbox_maxlamps\t\t10\r\nsbox_maxthrusters\t10\r\nsbox_maxwheels\t\t10\r\nsbox_maxhoverballs\t10\r\nsbox_maxvehicles\t20\r\nsbox_maxbuttons\t\t10\r\nsbox_maxsents\t\t20\r\nsbox_maxemitters\t5\r\nsbox_godmode\t\t0\r\nsbox_noclip\t\t 0\r\n\r\n\/\/ Network Settings - Please keep these set to default.\r\n\r\nsv_minrate\t\t75000\r\nsv_maxrate\t\t0\r\ngmod_physiterations\t2\r\nnet_splitpacket_maxrate\t45000\r\ndecalfrequency\t\t12 \r\n\r\n\/\/ Execute Ban Files - Please do not edit\r\nexec banned_ip.cfg \r\nexec banned_user.cfg \r\n\r\n\/\/ Add custom lines under here\r\n' > server.cfg", + "script": "#!\/bin\/bash\r\n# steamcmd Base Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\n## just in case someone removed the defaults.\r\nif [ \"${STEAM_USER}\" == \"\" ]; then\r\n echo -e \"steam user is not set.\\n\"\r\n echo -e \"Using anonymous user.\\n\"\r\n STEAM_USER=anonymous\r\n STEAM_PASS=\"\"\r\n STEAM_AUTH=\"\"\r\nelse\r\n echo -e \"user set to ${STEAM_USER}\"\r\nfi\r\n\r\n## download and install steamcmd\r\ncd \/tmp\r\nmkdir -p \/mnt\/server\/steamcmd\r\ncurl -sSL -o steamcmd.tar.gz https:\/\/steamcdn-a.akamaihd.net\/client\/installer\/steamcmd_linux.tar.gz\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/steamapps # Fix steamcmd disk write error when this folder is missing\r\ncd \/mnt\/server\/steamcmd\r\n\r\n# SteamCMD fails otherwise for some reason, even running as root.\r\n# This is changed at the end of the install process anyways.\r\nchown -R root:root \/mnt\r\nexport HOME=\/mnt\/server\r\n\r\n## install game using steamcmd\r\n.\/steamcmd.sh +login ${STEAM_USER} ${STEAM_PASS} ${STEAM_AUTH} $( [[ \"${WINDOWS_INSTALL}\" == \"1\" ]] && printf %s '+@sSteamCmdForcePlatformType windows' ) +force_install_dir \/mnt\/server +app_update ${SRCDS_APPID} ${EXTRA_FLAGS} validate +quit ## other flags may be needed depending on install. looking at you cs 1.6\r\n\r\n## set up 32 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\n## set up 64 bit libraries\r\nmkdir -p \/mnt\/server\/.steam\/sdk64\r\ncp -v linux64\/steamclient.so ..\/.steam\/sdk64\/steamclient.so\r\n\r\n# Creating needed default files for the game\r\ncd \/mnt\/server\/garrysmod\/lua\/autorun\/server\r\necho '\r\n-- Docs: https:\/\/wiki.garrysmod.com\/page\/resource\/AddWorkshop\r\n-- Place the ID of the workshop addon you want to be downloaded to people who join your server, not the collection ID\r\n-- Use https:\/\/beta.configcreator.com\/create\/gmod\/resources.lua to easily create a list based on your collection ID\r\n\r\nresource.AddWorkshop( \"\" )\r\n' > workshop.lua\r\n\r\ncd \/mnt\/server\/garrysmod\/cfg\r\necho '\r\n\/\/ Please do not set RCon in here, use the startup parameters.\r\n\r\nhostname\t\t\"New Gmod Server\"\r\nsv_password\t\t\"\"\r\nsv_loadingurl \"\"\r\nsv_downloadurl \"\"\r\n\r\n\/\/ Steam Server List Settings\r\n\/\/ sv_location \"eu\"\r\nsv_region \"255\"\r\nsv_lan \"0\"\r\nsv_max_queries_sec_global \"30000\"\r\nsv_max_queries_window \"45\"\r\nsv_max_queries_sec \"5\"\r\n\r\n\/\/ Server Limits\r\nsbox_maxprops\t\t100\r\nsbox_maxragdolls\t5\r\nsbox_maxnpcs\t\t10\r\nsbox_maxballoons\t10\r\nsbox_maxeffects\t\t10\r\nsbox_maxdynamite\t10\r\nsbox_maxlamps\t\t10\r\nsbox_maxthrusters\t10\r\nsbox_maxwheels\t\t10\r\nsbox_maxhoverballs\t10\r\nsbox_maxvehicles\t20\r\nsbox_maxbuttons\t\t10\r\nsbox_maxsents\t\t20\r\nsbox_maxemitters\t5\r\nsbox_godmode\t\t0\r\nsbox_noclip\t\t 0\r\n\r\n\/\/ Network Settings - Please keep these set to default.\r\n\r\nsv_minrate\t\t75000\r\nsv_maxrate\t\t0\r\ngmod_physiterations\t2\r\nnet_splitpacket_maxrate\t45000\r\ndecalfrequency\t\t12 \r\n\r\n\/\/ Execute Ban Files - Please do not edit\r\nexec banned_ip.cfg \r\nexec banned_user.cfg \r\n\r\n\/\/ Add custom lines under here\r\n' > server.cfg", "container": "ghcr.io\/pterodactyl\/installers:debian", "entrypoint": "bash" } @@ -101,4 +103,4 @@ "rules": "required|boolean" } ] -} +} \ No newline at end of file diff --git a/docker-compose.example.yml b/docker-compose.example.yml index 8ebc8d114..e25b27fcb 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -34,7 +34,7 @@ x-common: # services: database: - image: library/mysql:8.0 + image: mariadb:10.5 restart: always command: --default-authentication-plugin=mysql_native_password volumes: diff --git a/public/robots.txt b/public/robots.txt index eb0536286..1f53798bb 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,2 @@ User-agent: * -Disallow: +Disallow: / diff --git a/resources/scripts/api/http.ts b/resources/scripts/api/http.ts index a97f63de6..ef2ed8956 100644 --- a/resources/scripts/api/http.ts +++ b/resources/scripts/api/http.ts @@ -7,10 +7,21 @@ const http: AxiosInstance = axios.create({ 'X-Requested-With': 'XMLHttpRequest', Accept: 'application/json', 'Content-Type': 'application/json', - 'X-CSRF-Token': (window as any).X_CSRF_TOKEN as string || '', }, }); +http.interceptors.request.use(req => { + const cookies = document.cookie.split(';').reduce((obj, val) => { + const [ key, value ] = val.trim().split('=').map(decodeURIComponent); + + return { ...obj, [key]: value }; + }, {} as Record); + + req.headers['X-XSRF-TOKEN'] = cookies['XSRF-TOKEN'] || 'nil'; + + return req; +}); + http.interceptors.request.use(req => { if (!req.url?.endsWith('/resources') && (req.url?.indexOf('_debugbar') || -1) < 0) { store.getActions().progress.startContinuous(); diff --git a/resources/scripts/components/dashboard/AccountOverviewContainer.tsx b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx index 5f6653f71..a23ba5c55 100644 --- a/resources/scripts/components/dashboard/AccountOverviewContainer.tsx +++ b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx @@ -46,7 +46,7 @@ export default () => { > - + diff --git a/resources/scripts/components/dashboard/ServerRow.tsx b/resources/scripts/components/dashboard/ServerRow.tsx index ab8c4562e..60922df06 100644 --- a/resources/scripts/components/dashboard/ServerRow.tsx +++ b/resources/scripts/components/dashboard/ServerRow.tsx @@ -4,7 +4,7 @@ import { faEthernet, faHdd, faMemory, faMicrochip, faServer } from '@fortawesome import { Link } from 'react-router-dom'; import { Server } from '@/api/server/getServer'; import getServerResourceUsage, { ServerPowerState, ServerStats } from '@/api/server/getServerResourceUsage'; -import { bytesToHuman, megabytesToHuman } from '@/helpers'; +import { bytesToHuman, megabytesToHuman, formatIp } from '@/helpers'; import tw, { styled } from 'twin.macro'; import GreyRowBox from '@/components/elements/GreyRowBox'; import Spinner from '@/components/elements/Spinner'; @@ -96,7 +96,7 @@ export default ({ server, className }: { server: Server; className?: string }) = { server.allocations.filter(alloc => alloc.isDefault).map(allocation => ( - {allocation.alias || allocation.ip}:{allocation.port} + {allocation.alias || formatIp(allocation.ip)}:{allocation.port} )) } diff --git a/resources/scripts/components/dashboard/search/SearchModal.tsx b/resources/scripts/components/dashboard/search/SearchModal.tsx index 598001af3..c7ce58008 100644 --- a/resources/scripts/components/dashboard/search/SearchModal.tsx +++ b/resources/scripts/components/dashboard/search/SearchModal.tsx @@ -12,7 +12,7 @@ import { ApplicationStore } from '@/state'; import { Link } from 'react-router-dom'; import tw, { styled } from 'twin.macro'; import Input from '@/components/elements/Input'; - +import { formatIp } from '@/helpers'; type Props = RequiredModalProps; interface Values { @@ -108,7 +108,7 @@ export default ({ ...props }: Props) => {

{ server.allocations.filter(alloc => alloc.isDefault).map(allocation => ( - {allocation.alias || allocation.ip}:{allocation.port} + {allocation.alias || formatIp(allocation.ip)}:{allocation.port} )) }

diff --git a/resources/scripts/components/elements/PageContentBlock.tsx b/resources/scripts/components/elements/PageContentBlock.tsx index f4454377f..0bb06a464 100644 --- a/resources/scripts/components/elements/PageContentBlock.tsx +++ b/resources/scripts/components/elements/PageContentBlock.tsx @@ -28,15 +28,15 @@ const PageContentBlock: React.FC = ({ title, showFlashKey

- © 2015 - {(new Date()).getFullYear()}  - Pterodactyl Software + Pterodactyl® +  © 2015 - {(new Date()).getFullYear()}

diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx index 8c6eba1f8..ba9cfc12d 100644 --- a/resources/scripts/components/server/ServerConsole.tsx +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -7,7 +7,7 @@ import ServerContentBlock from '@/components/elements/ServerContentBlock'; import ServerDetailsBlock from '@/components/server/ServerDetailsBlock'; import isEqual from 'react-fast-compare'; import PowerControls from '@/components/server/PowerControls'; -import { EulaModalFeature, JavaVersionModalFeature } from 'feature/index'; +import { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature } from 'feature/index'; import ErrorBoundary from '@/components/elements/ErrorBoundary'; import Spinner from '@/components/elements/Spinner'; @@ -60,6 +60,7 @@ const ServerConsole = () => { {eggFeatures.includes('eula') && } {eggFeatures.includes('java_version') && } + {eggFeatures.includes('gsl_token') && } diff --git a/resources/scripts/components/server/ServerDetailsBlock.tsx b/resources/scripts/components/server/ServerDetailsBlock.tsx index e0706832f..34d975b81 100644 --- a/resources/scripts/components/server/ServerDetailsBlock.tsx +++ b/resources/scripts/components/server/ServerDetailsBlock.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import tw, { TwStyle } from 'twin.macro'; import { faCircle, faEthernet, faHdd, faMemory, faMicrochip, faServer } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { bytesToHuman, megabytesToHuman } from '@/helpers'; +import { bytesToHuman, megabytesToHuman, formatIp } from '@/helpers'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; import { ServerContext } from '@/state/server'; import CopyOnClick from '@/components/elements/CopyOnClick'; @@ -72,7 +72,7 @@ const ServerDetailsBlock = () => { const isTransferring = ServerContext.useStoreState(state => state.server.data!.isTransferring); const limits = ServerContext.useStoreState(state => state.server.data!.limits); const primaryAllocation = ServerContext.useStoreState(state => state.server.data!.allocations.filter(alloc => alloc.isDefault).map( - allocation => (allocation.alias || allocation.ip) + ':' + allocation.port, + allocation => (allocation.alias || formatIp(allocation.ip)) + ':' + allocation.port, )).toString(); const diskLimit = limits.disk ? megabytesToHuman(limits.disk) : 'Unlimited'; diff --git a/resources/scripts/components/server/StatGraphs.tsx b/resources/scripts/components/server/StatGraphs.tsx index cccd199f7..8e66f393a 100644 --- a/resources/scripts/components/server/StatGraphs.tsx +++ b/resources/scripts/components/server/StatGraphs.tsx @@ -97,6 +97,7 @@ export default () => { setCpu( new Chart(node.getContext('2d')!, chartDefaults({ callback: (value) => `${value}% `, + suggestedMax: limits.cpu, })), ); }, []); diff --git a/resources/scripts/components/server/databases/DatabaseRow.tsx b/resources/scripts/components/server/databases/DatabaseRow.tsx index bb8ed59e2..10ecace58 100644 --- a/resources/scripts/components/server/databases/DatabaseRow.tsx +++ b/resources/scripts/components/server/databases/DatabaseRow.tsx @@ -76,7 +76,7 @@ export default ({ database, className }: Props) => {

Confirm database deletion

- Deleting a database is a permanent action, it cannot be undone. This will permanetly + Deleting a database is a permanent action, it cannot be undone. This will permanently delete the {database.name} database and remove all associated data.

diff --git a/resources/scripts/components/server/features/GSLTokenModalFeature.tsx b/resources/scripts/components/server/features/GSLTokenModalFeature.tsx new file mode 100644 index 000000000..6cd96ec0d --- /dev/null +++ b/resources/scripts/components/server/features/GSLTokenModalFeature.tsx @@ -0,0 +1,101 @@ +import React, { useEffect, useState } from 'react'; +import { ServerContext } from '@/state/server'; +import Modal from '@/components/elements/Modal'; +import tw from 'twin.macro'; +import Button from '@/components/elements/Button'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import useFlash from '@/plugins/useFlash'; +import { SocketEvent, SocketRequest } from '@/components/server/events'; +import Field from '@/components/elements/Field'; +import updateStartupVariable from '@/api/server/updateStartupVariable'; +import { Form, Formik } from 'formik'; + +interface Values { + gslToken: string; +} + +const GSLTokenModalFeature = () => { + const [ visible, setVisible ] = useState(false); + const [ loading, setLoading ] = useState(false); + + const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const status = ServerContext.useStoreState(state => state.status.value); + const { clearFlashes, clearAndAddHttpError } = useFlash(); + const { connected, instance } = ServerContext.useStoreState(state => state.socket); + + useEffect(() => { + if (!connected || !instance || status === 'running') return; + + const errors = [ + '(gsl token expired)', + '(account not found)', + ]; + + const listener = (line: string) => { + if (errors.some(p => line.toLowerCase().includes(p))) { + setVisible(true); + } + }; + + instance.addListener(SocketEvent.CONSOLE_OUTPUT, listener); + + return () => { + instance.removeListener(SocketEvent.CONSOLE_OUTPUT, listener); + }; + }, [ connected, instance, status ]); + + const updateGSLToken = (values: Values) => { + setLoading(true); + clearFlashes('feature:gslToken'); + + updateStartupVariable(uuid, 'STEAM_ACC', values.gslToken) + .then(() => { + if (instance) { + instance.send(SocketRequest.SET_STATE, 'restart'); + } + + setLoading(false); + setVisible(false); + }) + .catch(error => { + console.error(error); + clearAndAddHttpError({ key: 'feature:gslToken', error }); + }) + .then(() => setLoading(false)); + }; + + useEffect(() => { + clearFlashes('feature:gslToken'); + }, []); + + return ( + + setVisible(false)} closeOnBackground={false} showSpinnerOverlay={loading}> + + +

Invalid GSL token!

+

It seems like your Gameserver Login Token (GSL token) is invalid or has expired.

+

You can either generate a new one and enter it below or leave the field blank to remove it completely.

+
+ +
+
+ +
+ +
+
+ ); +}; + +export default GSLTokenModalFeature; diff --git a/resources/scripts/components/server/features/index.ts b/resources/scripts/components/server/features/index.ts index b9767940f..b85170bd0 100644 --- a/resources/scripts/components/server/features/index.ts +++ b/resources/scripts/components/server/features/index.ts @@ -7,6 +7,7 @@ import { lazy } from 'react'; * on the feature and the egg). */ const EulaModalFeature = lazy(() => import(/* webpackChunkName: "feature.eula" */'feature/eula/EulaModalFeature')); -const JavaVersionModalFeature = lazy(() => import(/* webpackChunkName: "feature.java_version" */'feature/java_version/JavaVersionModalFeature')); +const JavaVersionModalFeature = lazy(() => import(/* webpackChunkName: "feature.java_version" */'feature/JavaVersionModalFeature')); +const GSLTokenModalFeature = lazy(() => import(/* webpackChunkName: "feature.gsl_token" */'feature/GSLTokenModalFeature')); -export { EulaModalFeature, JavaVersionModalFeature }; +export { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature }; diff --git a/resources/scripts/components/server/features/java_version/JavaVersionModalFeature.tsx b/resources/scripts/components/server/features/java_version/JavaVersionModalFeature.tsx index 3c9d5a767..f15b9abbb 100644 --- a/resources/scripts/components/server/features/java_version/JavaVersionModalFeature.tsx +++ b/resources/scripts/components/server/features/java_version/JavaVersionModalFeature.tsx @@ -10,15 +10,16 @@ import { SocketEvent, SocketRequest } from '@/components/server/events'; import Select from '@/components/elements/Select'; const dockerImageList = [ - { name: 'Java 8', image: 'ghcr.io/pterodactyl/yolks:java_8' }, - { name: 'Java 11', image: 'ghcr.io/pterodactyl/yolks:java_11' }, + { name: 'Java 17', image: 'ghcr.io/pterodactyl/yolks:java_17' }, { name: 'Java 16', image: 'ghcr.io/pterodactyl/yolks:java_16' }, + { name: 'Java 11', image: 'ghcr.io/pterodactyl/yolks:java_11' }, + { name: 'Java 8', image: 'ghcr.io/pterodactyl/yolks:java_8' }, ]; const JavaVersionModalFeature = () => { const [ visible, setVisible ] = useState(false); const [ loading, setLoading ] = useState(false); - const [ selectedVersion, setSelectedVersion ] = useState('ghcr.io/pterodactyl/yolks:java_16'); + const [ selectedVersion, setSelectedVersion ] = useState('ghcr.io/pterodactyl/yolks:java_17'); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const status = ServerContext.useStoreState(state => state.status.value); @@ -30,6 +31,7 @@ const JavaVersionModalFeature = () => { const errors = [ 'minecraft 1.17 requires running the server with java 16 or above', + 'minecraft 1.18 requires running the server with java 17 or above', 'java.lang.unsupportedclassversionerror', 'unsupported major.minor version', 'has been compiled by a more recent version of the java runtime', diff --git a/resources/scripts/components/server/network/AllocationRow.tsx b/resources/scripts/components/server/network/AllocationRow.tsx index 31fe249df..cc11e1db3 100644 --- a/resources/scripts/components/server/network/AllocationRow.tsx +++ b/resources/scripts/components/server/network/AllocationRow.tsx @@ -17,6 +17,7 @@ import CopyOnClick from '@/components/elements/CopyOnClick'; import DeleteAllocationButton from '@/components/server/network/DeleteAllocationButton'; import setPrimaryServerAllocation from '@/api/server/network/setPrimaryServerAllocation'; import getServerAllocations from '@/api/swr/getServerAllocations'; +import { formatIp } from '@/helpers'; const Code = styled.code`${tw`font-mono py-1 px-2 bg-neutral-900 rounded text-sm inline-block`}`; const Label = styled.label`${tw`uppercase text-xs mt-1 text-neutral-400 block px-1 select-none transition-colors duration-150`}`; @@ -65,7 +66,7 @@ const AllocationRow = ({ allocation }: Props) => {
{allocation.alias ? {allocation.alias} : - {allocation.ip}} + {formatIp(allocation.ip)}}
diff --git a/resources/scripts/components/server/settings/SettingsContainer.tsx b/resources/scripts/components/server/settings/SettingsContainer.tsx index c479ecc6f..53fc1bba7 100644 --- a/resources/scripts/components/server/settings/SettingsContainer.tsx +++ b/resources/scripts/components/server/settings/SettingsContainer.tsx @@ -13,6 +13,7 @@ import { LinkButton } from '@/components/elements/Button'; import ServerContentBlock from '@/components/elements/ServerContentBlock'; import isEqual from 'react-fast-compare'; import CopyOnClick from '@/components/elements/CopyOnClick'; +import { formatIp } from '@/helpers'; export default () => { const username = useStoreState(state => state.user.data!.username); @@ -31,10 +32,10 @@ export default () => {
- + @@ -60,7 +61,7 @@ export default () => {
Launch SFTP diff --git a/resources/scripts/helpers.ts b/resources/scripts/helpers.ts index 8ad4e2958..cd57c55b6 100644 --- a/resources/scripts/helpers.ts +++ b/resources/scripts/helpers.ts @@ -63,3 +63,7 @@ export function encodePathSegments (path: string): string { export function hashToPath (hash: string): string { return hash.length > 0 ? decodeURIComponent(hash.substr(1)) : '/'; } + +export function formatIp (ip: string): string { + return /([a-f0-9:]+:+)+[a-f0-9]+/.test(ip) ? `[${ip}]` : ip; +} diff --git a/tests/Integration/Api/Application/Location/LocationControllerTest.php b/tests/Integration/Api/Application/Location/LocationControllerTest.php index 7032ceffa..0ce70be97 100644 --- a/tests/Integration/Api/Application/Location/LocationControllerTest.php +++ b/tests/Integration/Api/Application/Location/LocationControllerTest.php @@ -4,6 +4,7 @@ namespace Pterodactyl\Tests\Integration\Api\Application\Location; use Illuminate\Http\Response; use Pterodactyl\Models\Location; +use Pterodactyl\Transformers\Api\Application\LocationTransformer; use Pterodactyl\Transformers\Api\Application\NodeTransformer; use Pterodactyl\Transformers\Api\Application\ServerTransformer; use Pterodactyl\Tests\Integration\Api\Application\ApplicationApiIntegrationTestCase; @@ -88,6 +89,77 @@ class LocationControllerTest extends ApplicationApiIntegrationTestCase ], true); } + /** + * Test that a location can be created. + */ + public function testCreateLocation() + { + $response = $this->postJson('/api/application/locations', [ + 'short' => 'inhouse', + 'long' => 'This is my inhouse location', + ]); + + $response->assertStatus(Response::HTTP_CREATED); + $response->assertJsonCount(3); + $response->assertJsonStructure([ + 'object', + 'attributes' => ['id', 'short', 'long', 'created_at', 'updated_at'], + 'meta' => ['resource'], + ]); + + $this->assertDatabaseHas('locations', ['short' => 'inhouse', 'long' => 'This is my inhouse location']); + + $location = Location::where('short', 'inhouse')->first(); + $response->assertJson([ + 'object' => 'location', + 'attributes' => $this->getTransformer(LocationTransformer::class)->transform($location), + 'meta' => [ + 'resource' => route('api.application.locations.view', $location->id), + ], + ], true); + } + + /** + * Test that a location can be updated. + */ + public function testUpdateLocation() + { + $location = Location::factory()->create(); + + $response = $this->patchJson('/api/application/locations/' . $location->id, [ + 'short' => 'new inhouse', + 'long' => 'This is my new inhouse location' + ]); + $response->assertStatus(Response::HTTP_OK); + $response->assertJsonCount(2); + $response->assertJsonStructure([ + 'object', + 'attributes' => ['id', 'short', 'long', 'created_at', 'updated_at'] + ]); + + $this->assertDatabaseHas('locations', ['short' => 'new inhouse', 'long' => 'This is my new inhouse location']); + $location = $location->fresh(); + + $response->assertJson([ + 'object' => 'location', + 'attributes' => $this->getTransformer(LocationTransformer::class)->transform($location), + ]); + } + + /** + * Test that a location can be deleted from the database. + */ + public function testDeleteLocation() + { + $location = Location::factory()->create(); + $this->assertDatabaseHas('locations', ['id' => $location->id]); + + $response = $this->delete('/api/application/locations/' . $location->id); + $response->assertStatus(Response::HTTP_NO_CONTENT); + + $this->assertDatabaseMissing('locations', ['id' => $location->id]); + } + /** * Test that all of the defined relationships for a location can be loaded successfully. */