From 110b2568d584e6a5a889249547a04ea46999f66b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 12 Oct 2020 21:12:31 -0700 Subject: [PATCH 01/77] Update changelog --- CHANGELOG.md | 13 +++++++++++++ README.md | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faf955b18..c594f9437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ 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.0.1 +### Fixed +* Fixes 500 error when mounting a mount to a server, and other related errors when handling mounts. +* Ensures that `server_transfers` database is deleted if it already exists to avoid unnecessary error. +* Fixes servers getting marked as "not installed" when modifying their startup arguments. +* Fixes filemanager breadcrumbs being set incorrectly when navigating between files and folders. + +### Changed +* Change the requests per minute from 240 to 720 for the client API to avoid unecessarily displaying +"Too Many Requests" errors. +* Added error output to certain commands that will output and terminate the command execution if the database +migrations have not been run correctly for the instance. + ## v1.0.0 Pterodactyl 1.0 represents the culmination of over two years of work, almost 2,000 commits, endless bug and feature requests, and a dream that has been in the making since 2013. 🎉 diff --git a/README.md b/README.md index df1b21d4a..ef97f3f8c 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ I would like to extend my sincere thanks to the following sponsors for helping f | Company | About | | ------- | ----- | +| [**WISP**](https://wisp.gg) | Extra features. | | [**Bloom.host**](https://bloom.host) | Bloom.host offers dedicated core VPS and Minecraft hosting with Ryzen 9 processors. With owned-hardware, we offer truly unbeatable prices on high-performance hosting. | -| [**VersatileNode**](https://versatilenode.com/) | Looking to host a minecraft server, vps, or a website? VersatileNode is one of the most affordable hosting providers to provide quality yet cheap services with incredible support. | | [**MineStrator**](https://minestrator.com/) | Looking for a French highend hosting company for you minecraft server? More than 14,000 members on our discord, trust us. | | [**DedicatedMC**](https://dedicatedmc.io/) | DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance and giving you the best performance money can buy. | | [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | From 9dc0c3e2c3b2b6a3d41146a1ae38476634968608 Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Wed, 14 Oct 2020 02:34:53 -0400 Subject: [PATCH 02/77] Upgrade Xterm to v4.9, Add Search Changes: Added ` xterm-addon-search ` v0.7.0 Added ` xterm-addon-search-bar ` v0.2.0 Updated ` webpack ` v4.43.0 -> v4.44.2 Updated ` xterm ` v3.14.4 -> v4.9.0 Updated ` xterm-addon-fit ` v0.1.0 -> v0.7.0 Updated ` xterm-addon-attach ` v0.1.0 -> v0.4.0 With the added packages above, when a user does Ctrl + F a search box will apear within the console for them to search whats in the console. This was requested in discord to allow the lines in the console to be searchable. --- package.json | 8 ++- .../scripts/components/server/Console.tsx | 29 +++++++--- yarn.lock | 57 ++++++++++++++++--- 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 15e96c4bd..e0603db4c 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,11 @@ "styled-components-breakpoint": "^3.0.0-preview.20", "swr": "^0.2.3", "uuid": "^3.3.2", - "xterm": "^3.14.4", - "xterm-addon-attach": "^0.1.0", - "xterm-addon-fit": "^0.1.0", + "xterm": "^4.9.0", + "xterm-addon-attach": "^0.6.0", + "xterm-addon-fit": "^0.4.0", + "xterm-addon-search": "^0.7.0", + "xterm-addon-search-bar": "^0.2.0", "yup": "^0.29.1" }, "devDependencies": { diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 0c04d95ec..a4df1ccec 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -1,12 +1,14 @@ import React, { useEffect, useMemo, useRef } from 'react'; import { ITerminalOptions, Terminal } from 'xterm'; -import * as TerminalFit from 'xterm/lib/addons/fit/fit'; +import { FitAddon } from 'xterm-addon-fit'; +import { SearchAddon } from 'xterm-addon-search'; +import { SearchBarAddon } from 'xterm-addon-search-bar'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import { ServerContext } from '@/state/server'; import styled from 'styled-components/macro'; import { usePermissions } from '@/plugins/usePermissions'; import tw from 'twin.macro'; -import 'xterm/dist/xterm.css'; +import 'xterm/css/xterm.css'; import useEventListener from '@/plugins/useEventListener'; import { debounce } from 'debounce'; @@ -29,6 +31,7 @@ const theme = { brightMagenta: '#C792EA', brightCyan: '#89DDFF', brightWhite: '#ffffff', + selection: '#FAF089', }; const terminalProps: ITerminalOptions = { @@ -55,6 +58,9 @@ export default () => { const TERMINAL_PRELUDE = '\u001b[1m\u001b[33mcontainer@pterodactyl~ \u001b[0m'; const ref = useRef(null); const terminal = useMemo(() => new Terminal({ ...terminalProps }), []); + const fitAddon = new FitAddon(); + const searchAddon = new SearchAddon(); + const searchAddonBar = new SearchBarAddon({ searchAddon }); const { connected, instance } = ServerContext.useStoreState(state => state.socket); const [ canSendCommands ] = usePermissions([ 'control.console' ]); @@ -82,26 +88,31 @@ export default () => { useEffect(() => { if (connected && ref.current && !terminal.element) { terminal.open(ref.current); + terminal.loadAddon(fitAddon); + terminal.loadAddon(searchAddon); + terminal.loadAddon(searchAddonBar); + fitAddon.fit(); - // @see https://github.com/xtermjs/xterm.js/issues/2265 - // @see https://github.com/xtermjs/xterm.js/issues/2230 - TerminalFit.fit(terminal); - - // Add support for copying terminal text. + // Add support for capturing keys terminal.attachCustomKeyEventHandler((e: KeyboardEvent) => { - // Ctrl + C + // Ctrl + C ( Copy ) if (e.ctrlKey && (e.key === 'c')) { document.execCommand('copy'); return false; } + if (e.ctrlKey && (e.key === 'f')) { + searchAddonBar.show(); + return false; + } + return true; }); } }, [ terminal, connected ]); const fit = debounce(() => { - TerminalFit.fit(terminal); + fitAddon.fit(); }, 100); useEventListener('resize', () => fit()); diff --git a/yarn.lock b/yarn.lock index 25272d135..113b3370c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1662,6 +1662,14 @@ babel-plugin-syntax-jsx@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -2260,6 +2268,11 @@ core-js-compat@^3.4.7: browserslist "^4.8.0" semver "^6.3.0" +core-js@^2.4.0: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -5797,6 +5810,11 @@ regenerate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + regenerator-runtime@^0.13.2: version "0.13.2" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" @@ -5967,6 +5985,11 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rxjs-compat@^6.5.4: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.6.3.tgz#141405fcee11f48718d428b99c8f01826f594e5c" + integrity sha512-y+wUqq7bS2dG+7rH2fNMoxsDiJ32RQzFxZQE/JdtpnmEZmwLQrb1tCiItyHxdXJHXjmHnnzFscn3b6PEmORGKw== + safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -7313,17 +7336,33 @@ xtend@^4.0.2: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-attach@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/xterm-addon-attach/-/xterm-addon-attach-0.1.0.tgz#e0daa8188e9bb830def9ccad015fc62bc07e3abe" +xterm-addon-attach@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/xterm-addon-attach/-/xterm-addon-attach-0.6.0.tgz#220c23addd62ab88c9914e2d4c06f7407e44680e" + integrity sha512-Mo8r3HTjI/EZfczVCwRU6jh438B4WLXxdFO86OB7bx0jGhwh2GdF4ifx/rP+OB+Cb2vmLhhVIZ00/7x3YSP3dg== -xterm-addon-fit@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.1.0.tgz#dd52d8b2ec6ef05faab8285bafd9310063704468" +xterm-addon-fit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.4.0.tgz#06e0c5d0a6aaacfb009ef565efa1c81e93d90193" + integrity sha512-p4BESuV/g2L6pZzFHpeNLLnep9mp/DkF3qrPglMiucSFtD8iJxtMufEoEJbN8LZwB4i+8PFpFvVuFrGOSpW05w== -xterm@^3.14.4: - version "3.14.4" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.14.4.tgz#68a474fd0628e6027e420f6c8b0df136f6281ff8" +xterm-addon-search-bar@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/xterm-addon-search-bar/-/xterm-addon-search-bar-0.2.0.tgz#e03c020a5ed22f1e8d503946b26a14ade508bc91" + integrity sha512-xvXmBA/ShbnzGe5CCy0kqPNNGqjkpuaRgH3Z1iW0V71vCAPRrtJ/v/hMnysZBH7WGUYhlCQr1cJZagW2fBVvSg== + dependencies: + babel-runtime "^6.26.0" + rxjs-compat "^6.5.4" + +xterm-addon-search@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0.tgz#c929d3e5cbb335e82bff72f158ea82936d9cd4ef" + integrity sha512-6060evmJJ+tZcjnx33FXaeEHLpuXEa7l9UzUsYfMlCKbu88AbE+5LJocTKCHYd71cwCwb9pjmv/G1o9Rf9Zbcg== + +xterm@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0.tgz#7a4c097a433d565339b5533b468bbc60c6c87969" + integrity sha512-wGfqufmioctKr8VkbRuZbVDfjlXWGZZ1PWHy1yqqpGT3Nm6yaJx8lxDbSEBANtgaiVPTcKSp97sxOy5IlpqYfw== y18n@^4.0.0: version "4.0.0" From 471cadda77be64b38284b802f54dd81cc0a2846a Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Wed, 14 Oct 2020 04:54:17 -0400 Subject: [PATCH 03/77] Update UsersContainer.tsx Closes #2514 --- .../scripts/components/server/users/UsersContainer.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/scripts/components/server/users/UsersContainer.tsx b/resources/scripts/components/server/users/UsersContainer.tsx index 06f95c0b4..f29dc9202 100644 --- a/resources/scripts/components/server/users/UsersContainer.tsx +++ b/resources/scripts/components/server/users/UsersContainer.tsx @@ -9,7 +9,7 @@ import FlashMessageRender from '@/components/FlashMessageRender'; import getServerSubusers from '@/api/server/users/getServerSubusers'; import { httpErrorToHuman } from '@/api/http'; import Can from '@/components/elements/Can'; -import PageContentBlock from '@/components/elements/PageContentBlock'; +import ServerContentBlock from '@/components/elements/ServerContentBlock'; import tw from 'twin.macro'; export default () => { @@ -48,7 +48,7 @@ export default () => { } return ( - + {!subusers.length ?

@@ -64,6 +64,6 @@ export default () => { - + ); }; From c53b14acef418a8caef77867ae52d886de1ec035 Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Wed, 14 Oct 2020 07:23:02 -0400 Subject: [PATCH 04/77] Close search bar on esacpe Allows the searchbar to be closed when a user clicks escape, just like in the browser. --- resources/scripts/components/server/Console.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index a4df1ccec..8d16e0e35 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -106,6 +106,9 @@ export default () => { return false; } + if (e.key === 'Escape') { + searchAddonBar.hidden(); + } return true; }); } From cdb881efaf5fa805c59c435fa05951b2d5169f3b Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Wed, 14 Oct 2020 09:54:06 -0400 Subject: [PATCH 05/77] Fix naming --- 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 8d16e0e35..8cd3de853 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -60,7 +60,7 @@ export default () => { const terminal = useMemo(() => new Terminal({ ...terminalProps }), []); const fitAddon = new FitAddon(); const searchAddon = new SearchAddon(); - const searchAddonBar = new SearchBarAddon({ searchAddon }); + const searchBarAddon = new SearchBarAddon({ searchAddon }); const { connected, instance } = ServerContext.useStoreState(state => state.socket); const [ canSendCommands ] = usePermissions([ 'control.console' ]); @@ -90,7 +90,7 @@ export default () => { terminal.open(ref.current); terminal.loadAddon(fitAddon); terminal.loadAddon(searchAddon); - terminal.loadAddon(searchAddonBar); + terminal.loadAddon(searchBarAddon); fitAddon.fit(); // Add support for capturing keys From 807707c07e76ad90a235527a5baf089bbed73182 Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Wed, 14 Oct 2020 11:38:59 -0400 Subject: [PATCH 06/77] Update Console.tsx --- resources/scripts/components/server/Console.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 8cd3de853..5b45e9e24 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -60,7 +60,7 @@ export default () => { const terminal = useMemo(() => new Terminal({ ...terminalProps }), []); const fitAddon = new FitAddon(); const searchAddon = new SearchAddon(); - const searchBarAddon = new SearchBarAddon({ searchAddon }); + const SearchBar = new SearchBarAddon({ searchAddon }); const { connected, instance } = ServerContext.useStoreState(state => state.socket); const [ canSendCommands ] = usePermissions([ 'control.console' ]); @@ -90,24 +90,24 @@ export default () => { terminal.open(ref.current); terminal.loadAddon(fitAddon); terminal.loadAddon(searchAddon); - terminal.loadAddon(searchBarAddon); + terminal.loadAddon(SearchBar); fitAddon.fit(); // Add support for capturing keys terminal.attachCustomKeyEventHandler((e: KeyboardEvent) => { // Ctrl + C ( Copy ) - if (e.ctrlKey && (e.key === 'c')) { + if (e.ctrlKey && e.key === 'c') { document.execCommand('copy'); return false; } - if (e.ctrlKey && (e.key === 'f')) { - searchAddonBar.show(); + if (e.ctrlKey && e.key === 'f') { + SearchBar.show(); return false; } if (e.key === 'Escape') { - searchAddonBar.hidden(); + SearchBar.hidden(); } return true; }); From f33d0b1d724b327ab3e513feb76f44b5b0d529c3 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 14 Oct 2020 20:13:36 -0700 Subject: [PATCH 07/77] Update schedule view UI --- package.json | 8 +- .../scripts/components/elements/Button.tsx | 4 +- .../scripts/components/elements/Icon.tsx | 31 +++++ .../scripts/components/elements/Modal.tsx | 11 +- .../server/schedules/DeleteScheduleButton.tsx | 2 +- .../server/schedules/NewTaskButton.tsx | 3 +- .../server/schedules/ScheduleCronRow.tsx | 35 +++++ .../schedules/ScheduleEditContainer.tsx | 126 ++++++++++++------ .../server/schedules/ScheduleRow.tsx | 32 ++--- .../server/schedules/ScheduleTaskRow.tsx | 67 +++++----- resources/views/templates/base/core.blade.php | 1 + yarn.lock | 44 +++--- 12 files changed, 230 insertions(+), 134 deletions(-) create mode 100644 resources/scripts/components/elements/Icon.tsx create mode 100644 resources/scripts/components/server/schedules/ScheduleCronRow.tsx diff --git a/package.json b/package.json index 15e96c4bd..251623fc1 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "pterodactyl-panel", "dependencies": { - "@fortawesome/fontawesome-svg-core": "1.2.19", - "@fortawesome/free-solid-svg-icons": "^5.9.0", - "@fortawesome/react-fontawesome": "0.1.4", + "@fortawesome/fontawesome-svg-core": "^1.2.32", + "@fortawesome/free-solid-svg-icons": "^5.15.1", + "@fortawesome/react-fontawesome": "^0.1.11", "axios": "^0.19.2", "chart.js": "^2.8.0", "codemirror": "^5.57.0", @@ -23,9 +23,9 @@ "react": "^16.13.1", "react-dom": "npm:@hot-loader/react-dom", "react-fast-compare": "^3.2.0", + "react-ga": "^3.1.2", "react-google-recaptcha": "^2.0.1", "react-helmet": "^6.1.0", - "react-ga": "^3.1.2", "react-hot-loader": "^4.12.21", "react-i18next": "^11.2.1", "react-redux": "^7.1.0", diff --git a/resources/scripts/components/elements/Button.tsx b/resources/scripts/components/elements/Button.tsx index 300f1a9ea..8577fad7f 100644 --- a/resources/scripts/components/elements/Button.tsx +++ b/resources/scripts/components/elements/Button.tsx @@ -57,8 +57,8 @@ const ButtonStyle = styled.button>` `}; `}; - ${props => props.size === 'xsmall' && tw`p-2 text-xs`}; - ${props => (!props.size || props.size === 'small') && tw`p-3`}; + ${props => props.size === 'xsmall' && tw`px-2 py-1 text-xs`}; + ${props => (!props.size || props.size === 'small') && tw`px-4 py-2`}; ${props => props.size === 'large' && tw`p-4 text-sm`}; ${props => props.size === 'xlarge' && tw`p-4 w-full`}; diff --git a/resources/scripts/components/elements/Icon.tsx b/resources/scripts/components/elements/Icon.tsx new file mode 100644 index 000000000..a7d837896 --- /dev/null +++ b/resources/scripts/components/elements/Icon.tsx @@ -0,0 +1,31 @@ +import React, { CSSProperties } from 'react'; +import { IconDefinition } from '@fortawesome/fontawesome-svg-core'; +import tw from 'twin.macro'; + +interface Props { + icon: IconDefinition; + className?: string; + style?: CSSProperties; +} + +const Icon = ({ icon, className, style }: Props) => { + let [ width, height, , , paths ] = icon.icon; + + paths = Array.isArray(paths) ? paths : [ paths ]; + + return ( + + {paths.map((path, index) => ( + + ))} + + ); +}; + +export default Icon; diff --git a/resources/scripts/components/elements/Modal.tsx b/resources/scripts/components/elements/Modal.tsx index 68d6493de..ae618a42d 100644 --- a/resources/scripts/components/elements/Modal.tsx +++ b/resources/scripts/components/elements/Modal.tsx @@ -1,9 +1,10 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import Spinner from '@/components/elements/Spinner'; import tw from 'twin.macro'; import styled, { css } from 'styled-components/macro'; import { breakpoint } from '@/theme'; import Fade from '@/components/elements/Fade'; +import { createPortal } from 'react-dom'; export interface RequiredModalProps { visible: boolean; @@ -124,4 +125,10 @@ const Modal: React.FC = ({ visible, appear, dismissable, showSpinner ); }; -export default Modal; +const PortaledModal: React.FC = ({ children, ...props }) => { + const element = useRef(document.getElementById('modal-portal')); + + return createPortal({children}, element.current!); +}; + +export default PortaledModal; diff --git a/resources/scripts/components/server/schedules/DeleteScheduleButton.tsx b/resources/scripts/components/server/schedules/DeleteScheduleButton.tsx index 198060388..463202dce 100644 --- a/resources/scripts/components/server/schedules/DeleteScheduleButton.tsx +++ b/resources/scripts/components/server/schedules/DeleteScheduleButton.tsx @@ -49,7 +49,7 @@ export default ({ scheduleId, onDeleted }: Props) => { Are you sure you want to delete this schedule? All tasks will be removed and any running processes will be terminated. - diff --git a/resources/scripts/components/server/schedules/NewTaskButton.tsx b/resources/scripts/components/server/schedules/NewTaskButton.tsx index b46124e64..9234f5b42 100644 --- a/resources/scripts/components/server/schedules/NewTaskButton.tsx +++ b/resources/scripts/components/server/schedules/NewTaskButton.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { Schedule } from '@/api/server/schedules/getServerSchedules'; import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal'; import Button from '@/components/elements/Button'; +import tw from 'twin.macro'; interface Props { schedule: Schedule; @@ -18,7 +19,7 @@ export default ({ schedule }: Props) => { onDismissed={() => setVisible(false)} /> } - diff --git a/resources/scripts/components/server/schedules/ScheduleCronRow.tsx b/resources/scripts/components/server/schedules/ScheduleCronRow.tsx new file mode 100644 index 000000000..e7918a132 --- /dev/null +++ b/resources/scripts/components/server/schedules/ScheduleCronRow.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import tw from 'twin.macro'; +import { Schedule } from '@/api/server/schedules/getServerSchedules'; + +interface Props { + cron: Schedule['cron']; + className?: string; +} + +const ScheduleCronRow = ({ cron, className }: Props) => ( +

+
+

{cron.minute}

+

Minute

+
+
+

{cron.hour}

+

Hour

+
+
+

{cron.dayOfMonth}

+

Day (Month)

+
+
+

*

+

Month

+
+
+

{cron.dayOfWeek}

+

Day (Week)

+
+
+); + +export default ScheduleCronRow; diff --git a/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx b/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx index 8f9f07fce..aea4778ae 100644 --- a/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx +++ b/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx @@ -1,12 +1,10 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { Schedule } from '@/api/server/schedules/getServerSchedules'; import getServerSchedule from '@/api/server/schedules/getServerSchedule'; import Spinner from '@/components/elements/Spinner'; import FlashMessageRender from '@/components/FlashMessageRender'; import { httpErrorToHuman } from '@/api/http'; -import ScheduleRow from '@/components/server/schedules/ScheduleRow'; -import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow'; import EditScheduleModal from '@/components/server/schedules/EditScheduleModal'; import NewTaskButton from '@/components/server/schedules/NewTaskButton'; import DeleteScheduleButton from '@/components/server/schedules/DeleteScheduleButton'; @@ -16,7 +14,10 @@ import { ServerContext } from '@/state/server'; import PageContentBlock from '@/components/elements/PageContentBlock'; import tw from 'twin.macro'; import Button from '@/components/elements/Button'; -import GreyRowBox from '@/components/elements/GreyRowBox'; +import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow'; +import isEqual from 'react-fast-compare'; +import { format } from 'date-fns'; +import ScheduleCronRow from '@/components/server/schedules/ScheduleCronRow'; interface Params { id: string; @@ -26,6 +27,24 @@ interface State { schedule?: Schedule; } +const CronBox = ({ title, value }: { title: string; value: string }) => ( +
+

{title}

+

{value}

+
+); + +const ActivePill = ({ active }: { active: boolean }) => ( + + {active ? 'Active' : 'Inactive'} + +); + export default ({ match, history, location: { state } }: RouteComponentProps, State>) => { const id = ServerContext.useStoreState(state => state.server.data!.id); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); @@ -34,7 +53,8 @@ export default ({ match, history, location: { state } }: RouteComponentProps st.schedules.data.find(s => s.id === state.schedule?.id), [ match ]); + // @ts-ignore + const schedule: Schedule | undefined = ServerContext.useStoreState(st => st.schedules.data.find(s => s.id === state.schedule?.id), isEqual); const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule); useEffect(() => { @@ -53,6 +73,10 @@ export default ({ match, history, location: { state } }: RouteComponentProps setIsLoading(false)); }, [ match ]); + const toggleEditModal = useCallback(() => { + setShowEditModal(s => !s); + }, []); + return ( @@ -60,52 +84,68 @@ export default ({ match, history, location: { state } }: RouteComponentProps : <> - - - - setShowEditModal(false)} - /> -
-
-

Configured Tasks

+ +
+ + + + + +
+
+
+
+

+ {schedule.name} + {schedule.isProcessing ? + + + Processing + + : + + } +

+

+ Last run at:  + {schedule.lastRunAt ? format(schedule.lastRunAt, 'MMM do \'at\' h:mma') : 'never'} +

+
+
+ + + + +
+
+
+ {schedule.tasks.length > 0 ? + schedule.tasks.map(task => ( + + )) + : + null + }
- {schedule.tasks.length > 0 ? - <> - { - schedule.tasks - .sort((a, b) => a.sequenceId - b.sequenceId) - .map(task => ( - - )) - } - {schedule.tasks.length > 1 && -

- Task delays are relative to the previous task in the listing. -

- } - - : -

- There are no tasks configured for this schedule. -

- } -
+ +
history.push(`/server/${id}/schedules`)} /> - - - -
} diff --git a/resources/scripts/components/server/schedules/ScheduleRow.tsx b/resources/scripts/components/server/schedules/ScheduleRow.tsx index eb9ff68a5..eccdd0f96 100644 --- a/resources/scripts/components/server/schedules/ScheduleRow.tsx +++ b/resources/scripts/components/server/schedules/ScheduleRow.tsx @@ -4,6 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCalendarAlt } from '@fortawesome/free-solid-svg-icons'; import { format } from 'date-fns'; import tw from 'twin.macro'; +import ScheduleCronRow from '@/components/server/schedules/ScheduleCronRow'; export default ({ schedule }: { schedule: Schedule }) => ( <> @@ -27,36 +28,19 @@ export default ({ schedule }: { schedule: Schedule }) => ( {schedule.isActive ? 'Active' : 'Inactive'}

-
-
-

{schedule.cron.minute}

-

Minute

-
-
-

{schedule.cron.hour}

-

Hour

-
-
-

{schedule.cron.dayOfMonth}

-

Day (Month)

-
-
-

*

-

Month

-
-
-

{schedule.cron.dayOfWeek}

-

Day (Week)

-
-
+
diff --git a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx index c79fafd03..1ee55af2c 100644 --- a/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx +++ b/resources/scripts/components/server/schedules/ScheduleTaskRow.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Schedule, Task } from '@/api/server/schedules/getServerSchedules'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCode, faFileArchive, faPencilAlt, faToggleOn, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { faClock, faCode, faFileArchive, faPencilAlt, faToggleOn, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import deleteScheduleTask from '@/api/server/schedules/deleteScheduleTask'; import { httpErrorToHuman } from '@/api/http'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; @@ -11,6 +11,7 @@ import useFlash from '@/plugins/useFlash'; import { ServerContext } from '@/state/server'; import tw from 'twin.macro'; import ConfirmationModal from '@/components/elements/ConfirmationModal'; +import Icon from '@/components/elements/Icon'; interface Props { schedule: Schedule; @@ -56,7 +57,7 @@ export default ({ schedule, task }: Props) => { const [ title, icon ] = getActionDetails(task.action); return ( -
+
{isEditing && { Are you sure you want to delete this task? This action cannot be undone.
} diff --git a/routes/api-client.php b/routes/api-client.php index 51a2f0dec..fa0455018 100644 --- a/routes/api-client.php +++ b/routes/api-client.php @@ -72,6 +72,7 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ Route::post('/', 'Servers\ScheduleController@store'); Route::get('/{schedule}', 'Servers\ScheduleController@view'); Route::post('/{schedule}', 'Servers\ScheduleController@update'); + Route::post('/{schedule}/execute', 'Servers\ScheduleController@execute'); Route::delete('/{schedule}', 'Servers\ScheduleController@delete'); Route::post('/{schedule}/tasks', 'Servers\ScheduleTaskController@store'); diff --git a/tests/Unit/Services/Schedules/ProcessScheduleServiceTest.php b/tests/Unit/Services/Schedules/ProcessScheduleServiceTest.php deleted file mode 100644 index 01bbac149..000000000 --- a/tests/Unit/Services/Schedules/ProcessScheduleServiceTest.php +++ /dev/null @@ -1,85 +0,0 @@ -dispatcher = m::mock(Dispatcher::class); - $this->scheduleRepository = m::mock(ScheduleRepositoryInterface::class); - $this->taskRepository = m::mock(TaskRepositoryInterface::class); - } - - /** - * Test that a schedule can be updated and first task set to run. - */ - public function testScheduleIsUpdatedAndRun() - { - $model = factory(Schedule::class)->make(['id' => 123]); - $model->setRelation('tasks', collect([$task = factory(Task::class)->make([ - 'sequence_id' => 1, - ])])); - - $this->scheduleRepository->shouldReceive('loadTasks')->with($model)->once()->andReturn($model); - - $formatted = sprintf('%s %s %s * %s', $model->cron_minute, $model->cron_hour, $model->cron_day_of_month, $model->cron_day_of_week); - $this->scheduleRepository->shouldReceive('update')->with($model->id, [ - 'is_processing' => true, - 'next_run_at' => CronExpression::factory($formatted)->getNextRunDate(), - ]); - - $this->taskRepository->shouldReceive('update')->with($task->id, ['is_queued' => true])->once(); - - $this->dispatcher->shouldReceive('dispatch')->with(m::on(function ($class) use ($model, $task) { - $this->assertInstanceOf(RunTaskJob::class, $class); - $this->assertSame($task->time_offset, $class->delay); - $this->assertSame($task->id, $class->task->id); - - return true; - }))->once(); - - $this->getService()->handle($model); - } - - /** - * Return an instance of the service for testing purposes. - * - * @return \Pterodactyl\Services\Schedules\ProcessScheduleService - */ - private function getService(): ProcessScheduleService - { - return new ProcessScheduleService($this->dispatcher, $this->scheduleRepository, $this->taskRepository); - } -} From e7c64bc60e5e0e8e3ef2ecdc84ffb97b674f3226 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 14 Oct 2020 21:06:27 -0700 Subject: [PATCH 09/77] Add test coverage for schedule execution --- .../Schedules/TriggerScheduleRequest.php | 6 +- .../Schedules/ProcessScheduleService.php | 9 +- .../schedules/ScheduleEditContainer.tsx | 2 +- .../Server/Schedule/ExecuteScheduleTest.php | 94 +++++++++++++++++++ 4 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php index 7651b7419..d89f5ed30 100644 --- a/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Schedules/TriggerScheduleRequest.php @@ -3,9 +3,9 @@ namespace Pterodactyl\Http\Requests\Api\Client\Servers\Schedules; use Pterodactyl\Models\Permission; -use Illuminate\Foundation\Http\FormRequest; +use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest; -class TriggerScheduleRequest extends FormRequest +class TriggerScheduleRequest extends ClientApiRequest { /** * @return string @@ -18,7 +18,7 @@ class TriggerScheduleRequest extends FormRequest /** * @return array */ - public function rules() + public function rules(): array { return []; } diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php index 5d4ad60cf..1f810d6f5 100644 --- a/app/Services/Schedules/ProcessScheduleService.php +++ b/app/Services/Schedules/ProcessScheduleService.php @@ -6,6 +6,7 @@ use Pterodactyl\Models\Schedule; use Illuminate\Contracts\Bus\Dispatcher; use Pterodactyl\Jobs\Schedule\RunTaskJob; use Illuminate\Database\ConnectionInterface; +use Pterodactyl\Exceptions\DisplayException; class ProcessScheduleService { @@ -42,7 +43,13 @@ class ProcessScheduleService public function handle(Schedule $schedule, bool $now = false) { /** @var \Pterodactyl\Models\Task $task */ - $task = $schedule->tasks()->where('sequence_id', 1)->firstOrFail(); + $task = $schedule->tasks()->where('sequence_id', 1)->first(); + + if (is_null($task)) { + throw new DisplayException( + 'Cannot process schedule for task execution: no tasks are registered.' + ); + } $this->connection->transaction(function () use ($schedule, $task) { $schedule->forceFill([ diff --git a/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx b/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx index 5b2265845..d7c5f2abf 100644 --- a/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx +++ b/resources/scripts/components/server/schedules/ScheduleEditContainer.tsx @@ -146,7 +146,7 @@ export default ({ match, history, location: { state } }: RouteComponentProps history.push(`/server/${id}/schedules`)} /> - {schedule.isActive && + {schedule.isActive && schedule.tasks.length > 0 && diff --git a/tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php b/tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php new file mode 100644 index 000000000..00c57d4a4 --- /dev/null +++ b/tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php @@ -0,0 +1,94 @@ +generateTestAccount($permissions); + + Bus::fake(); + + /** @var \Pterodactyl\Models\Schedule $schedule */ + $schedule = factory(Schedule::class)->create([ + 'server_id' => $server->id, + ]); + + $response = $this->actingAs($user)->postJson($this->link($schedule, '/execute')); + $response->assertStatus(Response::HTTP_BAD_REQUEST); + $response->assertJsonPath('errors.0.code', 'DisplayException'); + $response->assertJsonPath('errors.0.detail', 'Cannot process schedule for task execution: no tasks are registered.'); + + /** @var \Pterodactyl\Models\Task $task */ + $task = factory(Task::class)->create([ + 'schedule_id' => $schedule->id, + 'sequence_id' => 1, + 'time_offset' => 2, + ]); + + $this->actingAs($user)->postJson($this->link($schedule, '/execute'))->assertStatus(Response::HTTP_ACCEPTED); + + Bus::assertDispatched(function (RunTaskJob $job) use ($task) { + $this->assertSame($task->time_offset, $job->delay); + $this->assertSame($task->id, $job->task->id); + + return true; + }); + } + + /** + * Test that the schedule is not executed if it is not currently active. + */ + public function testScheduleIsNotExecutedIfNotActive() + { + [$user, $server] = $this->generateTestAccount(); + + /** @var \Pterodactyl\Models\Schedule $schedule */ + $schedule = factory(Schedule::class)->create([ + 'server_id' => $server->id, + 'is_active' => false, + ]); + + $response = $this->actingAs($user)->postJson($this->link($schedule, "/execute")); + + $response->assertStatus(Response::HTTP_BAD_REQUEST); + $response->assertJsonPath('errors.0.code', 'BadRequestHttpException'); + $response->assertJsonPath('errors.0.detail', 'Cannot trigger schedule exection for a schedule that is not currently active.'); + } + + /** + * Test that a user without the schedule update permission cannot execute it. + */ + public function testUserWithoutScheduleUpdatePermissionCannotExecute() + { + [$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]); + + /** @var \Pterodactyl\Models\Schedule $schedule */ + $schedule = factory(Schedule::class)->create(['server_id' => $server->id]); + + $this->actingAs($user)->postJson($this->link($schedule, '/execute'))->assertForbidden(); + } + + /** + * @return array + */ + public function permissionsDataProvider(): array + { + return [[[]], [[Permission::ACTION_SCHEDULE_UPDATE]]]; + } +} From 14099c164baff1a50ecd9726db932956d8a54e63 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 14 Oct 2020 21:17:57 -0700 Subject: [PATCH 10/77] Add test coverage for schedule service --- .../Schedules/ProcessScheduleServiceTest.php | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php diff --git a/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php new file mode 100644 index 000000000..4941a9bd9 --- /dev/null +++ b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php @@ -0,0 +1,99 @@ +createServerModel(); + $schedule = factory(Schedule::class)->create(['server_id' => $server->id]); + + $this->expectException(DisplayException::class); + $this->expectExceptionMessage('Cannot process schedule for task execution: no tasks are registered.'); + + $this->getService()->handle($schedule); + } + + /** + * Test that an error during the schedule update is not persisted to the database. + */ + public function testErrorDuringScheduleDataUpdateDoesNotPersistChanges() + { + $server = $this->createServerModel(); + + /** @var \Pterodactyl\Models\Schedule $schedule */ + $schedule = factory(Schedule::class)->create([ + 'server_id' => $server->id, + 'cron_minute' => 'hodor', // this will break the getNextRunDate() function. + ]); + + /** @var \Pterodactyl\Models\Task $task */ + $task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]); + + $this->expectException(InvalidArgumentException::class); + + $this->getService()->handle($schedule); + + $this->assertDatabaseMissing('schedules', ['id' => $schedule->id, 'is_processing' => true]); + $this->assertDatabaseMissing('tasks', ['id' => $task->id, 'is_queued' => true]); + } + + /** + * Test that a job is dispatched as expected using the initial delay. + * + * @param bool $now + * @dataProvider dispatchNowDataProvider + */ + public function testJobCanBeDispatchedWithExpectedInitialDelay($now) + { + $this->swap(Dispatcher::class, $dispatcher = Mockery::mock(Dispatcher::class)); + + $server = $this->createServerModel(); + + /** @var \Pterodactyl\Models\Schedule $schedule */ + $schedule = factory(Schedule::class)->create(['server_id' => $server->id]); + + /** @var \Pterodactyl\Models\Task $task */ + $task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'time_offset' => 10, 'sequence_id' => 1]); + + $dispatcher->expects($now ? 'dispatchNow' : 'dispatch')->with(Mockery::on(function (RunTaskJob $job) use ($task) { + return $task->id === $job->task->id && $job->delay === 10; + })); + + $this->getService()->handle($schedule, $now); + + $this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]); + $this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]); + } + + /** + * @return array + */ + public function dispatchNowDataProvider(): array + { + return [[true], [false]]; + } + + /** + * @return \Pterodactyl\Services\Schedules\ProcessScheduleService + */ + private function getService() + { + return $this->app->make(ProcessScheduleService::class); + } +} From 8f2e90d1e851b3bb9efb4f4d954b2379336deae0 Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Thu, 15 Oct 2020 16:41:11 -0400 Subject: [PATCH 11/77] Update Console.tsx --- resources/scripts/components/server/Console.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 5b45e9e24..e3720ab69 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -60,7 +60,7 @@ export default () => { const terminal = useMemo(() => new Terminal({ ...terminalProps }), []); const fitAddon = new FitAddon(); const searchAddon = new SearchAddon(); - const SearchBar = new SearchBarAddon({ searchAddon }); + const searchBar = new SearchBarAddon({ searchAddon }); const { connected, instance } = ServerContext.useStoreState(state => state.socket); const [ canSendCommands ] = usePermissions([ 'control.console' ]); @@ -90,7 +90,7 @@ export default () => { terminal.open(ref.current); terminal.loadAddon(fitAddon); terminal.loadAddon(searchAddon); - terminal.loadAddon(SearchBar); + terminal.loadAddon(searchBar); fitAddon.fit(); // Add support for capturing keys @@ -102,12 +102,12 @@ export default () => { } if (e.ctrlKey && e.key === 'f') { - SearchBar.show(); + searchBar.show(); return false; } if (e.key === 'Escape') { - SearchBar.hidden(); + searchBar.hidden(); } return true; }); From 9726a0de468809550cb52ae248d0d757740d8166 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 15 Oct 2020 20:09:13 -0700 Subject: [PATCH 12/77] Autofocus search when opening; closes #2522 --- .../components/dashboard/search/SearchModal.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/scripts/components/dashboard/search/SearchModal.tsx b/resources/scripts/components/dashboard/search/SearchModal.tsx index 75eff1bfd..9bd871ce5 100644 --- a/resources/scripts/components/dashboard/search/SearchModal.tsx +++ b/resources/scripts/components/dashboard/search/SearchModal.tsx @@ -63,22 +63,24 @@ export default ({ ...props }: Props) => { console.error(error); addError({ key: 'search', message: httpErrorToHuman(error) }); }) - .then(() => setLoading(false)); + .then(() => setLoading(false)) + .then(() => ref.current?.focus()); }, 500); useEffect(() => { if (props.visible) { - setTimeout(() => ref.current?.focus(), 250); + if (ref.current) ref.current.focus(); } }, [ props.visible ]); + // Formik does not support an innerRef on custom components. + const InputWithRef = (props: any) => ; + return ( @@ -95,7 +97,7 @@ export default ({ ...props }: Props) => { > - + From f30dab053b68e01efe224f677dc24efaadec8112 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 15 Oct 2020 21:21:38 -0700 Subject: [PATCH 13/77] Support much better server querying from frontend Search all servers if making a query as an admin, allow searching by a more complex set of data, fix unfocus on search field when loading indicator was rendered --- .../Api/Client/ClientController.php | 35 ++++-- app/Models/Filters/MultiFieldServerFilter.php | 74 +++++++++++++ resources/scripts/api/getServers.ts | 9 +- .../dashboard/DashboardContainer.tsx | 2 +- .../dashboard/search/SearchModal.tsx | 100 +++++++++--------- .../components/elements/InputSpinner.tsx | 7 +- 6 files changed, 152 insertions(+), 75 deletions(-) create mode 100644 app/Models/Filters/MultiFieldServerFilter.php diff --git a/app/Http/Controllers/Api/Client/ClientController.php b/app/Http/Controllers/Api/Client/ClientController.php index 5eec40b51..243868b5e 100644 --- a/app/Http/Controllers/Api/Client/ClientController.php +++ b/app/Http/Controllers/Api/Client/ClientController.php @@ -5,6 +5,8 @@ namespace Pterodactyl\Http\Controllers\Api\Client; use Pterodactyl\Models\Server; use Pterodactyl\Models\Permission; use Spatie\QueryBuilder\QueryBuilder; +use Spatie\QueryBuilder\AllowedFilter; +use Pterodactyl\Models\Filters\MultiFieldServerFilter; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Transformers\Api\Client\ServerTransformer; use Pterodactyl\Http\Requests\Api\Client\GetServersRequest; @@ -43,21 +45,32 @@ class ClientController extends ClientApiController // Start the query builder and ensure we eager load any requested relationships from the request. $builder = QueryBuilder::for( Server::query()->with($this->getIncludesForTransformer($transformer, ['node'])) - )->allowedFilters('uuid', 'name', 'external_id'); + )->allowedFilters([ + 'uuid', + 'name', + 'external_id', + AllowedFilter::custom('*', new MultiFieldServerFilter), + ]); + $type = $request->input('type'); // Either return all of the servers the user has access to because they are an admin `?type=admin` or // just return all of the servers the user has access to because they are the owner or a subuser of the - // server. - if ($request->input('type') === 'admin') { - $builder = $user->root_admin - ? $builder->whereNotIn('id', $user->accessibleServers()->pluck('id')->all()) - // If they aren't an admin but want all the admin servers don't fail the request, just - // make it a query that will never return any results back. - : $builder->whereRaw('1 = 2'); - } elseif ($request->input('type') === 'owner') { - $builder = $builder->where('owner_id', $user->id); + // server. If ?type=admin-all is passed all servers on the system will be returned to the user, rather + // than only servers they can see because they are an admin. + if (in_array($type, ['admin', 'admin-all'])) { + // If they aren't an admin but want all the admin servers don't fail the request, just + // make it a query that will never return any results back. + if (! $user->root_admin) { + $builder->whereRaw('1 = 2'); + } else { + $builder = $type === 'admin-all' + ? $builder + : $builder->whereNotIn('servers.id', $user->accessibleServers()->pluck('id')->all()); + } + } else if ($type === 'owner') { + $builder = $builder->where('servers.owner_id', $user->id); } else { - $builder = $builder->whereIn('id', $user->accessibleServers()->pluck('id')->all()); + $builder = $builder->whereIn('servers.id', $user->accessibleServers()->pluck('id')->all()); } $servers = $builder->paginate(min($request->query('per_page', 50), 100))->appends($request->query()); diff --git a/app/Models/Filters/MultiFieldServerFilter.php b/app/Models/Filters/MultiFieldServerFilter.php new file mode 100644 index 000000000..ed47d132f --- /dev/null +++ b/app/Models/Filters/MultiFieldServerFilter.php @@ -0,0 +1,74 @@ +getQuery()->from !== 'servers') { + throw new BadMethodCallException( + 'Cannot use the MultiFieldServerFilter against a non-server model.' + ); + } + + if (preg_match(self::IPV4_REGEX, $value) || preg_match('/^:\d{1,5}$/', $value)) { + $query + // Only select the server values, otherwise you'll end up merging the allocation and + // server objects together, resulting in incorrect behavior and returned values. + ->select('servers.*') + ->join('allocations', 'allocations.server_id', '=', 'servers.id') + ->where(function (Builder $builder) use ($value) { + $parts = explode(':', $value); + + $builder->when( + !Str::startsWith($value, ':'), + // When the string does not start with a ":" it means we're looking for an IP or IP:Port + // combo, so use a query to handle that. + function (Builder $builder) use ($parts) { + $builder->orWhere('allocations.ip', $parts[0]); + if (!is_null($parts[1] ?? null)) { + $builder->where('allocations.port', 'LIKE', "%{$parts[1]}"); + } + }, + // Otherwise, just try to search for that specific port in the allocations. + function (Builder $builder) use ($value) { + $builder->orWhere('allocations.port', substr($value, 1)); + } + ); + }) + ->groupBy('servers.id'); + + return; + } + + $query + ->where(function (Builder $builder) use ($value) { + $builder->where('servers.uuid', $value) + ->orWhere('servers.uuid', 'LIKE', "$value%") + ->orWhere('servers.uuidShort', $value) + ->orWhere('servers.external_id', $value) + ->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]); + }); + } +} diff --git a/resources/scripts/api/getServers.ts b/resources/scripts/api/getServers.ts index 63329bfa7..6094b1706 100644 --- a/resources/scripts/api/getServers.ts +++ b/resources/scripts/api/getServers.ts @@ -4,16 +4,15 @@ import http, { getPaginationSet, PaginatedResult } from '@/api/http'; interface QueryParams { query?: string; page?: number; - onlyAdmin?: boolean; + type?: string; } -export default ({ query, page = 1, onlyAdmin = false }: QueryParams): Promise> => { +export default ({ query, ...params }: QueryParams): Promise> => { return new Promise((resolve, reject) => { http.get('/api/client', { params: { - type: onlyAdmin ? 'admin' : undefined, - 'filter[name]': query, - page, + 'filter[*]': query, + ...params, }, }) .then(({ data }) => resolve({ diff --git a/resources/scripts/components/dashboard/DashboardContainer.tsx b/resources/scripts/components/dashboard/DashboardContainer.tsx index f8b13eda2..b4cb4c290 100644 --- a/resources/scripts/components/dashboard/DashboardContainer.tsx +++ b/resources/scripts/components/dashboard/DashboardContainer.tsx @@ -21,7 +21,7 @@ export default () => { const { data: servers, error } = useSWR>( [ '/api/client/servers', showOnlyAdmin, page ], - () => getServers({ onlyAdmin: showOnlyAdmin, page }), + () => getServers({ page, type: showOnlyAdmin ? 'admin' : undefined }), ); useEffect(() => { diff --git a/resources/scripts/components/dashboard/search/SearchModal.tsx b/resources/scripts/components/dashboard/search/SearchModal.tsx index 9bd871ce5..e8a2a7511 100644 --- a/resources/scripts/components/dashboard/search/SearchModal.tsx +++ b/resources/scripts/components/dashboard/search/SearchModal.tsx @@ -47,23 +47,21 @@ const SearchWatcher = () => { export default ({ ...props }: Props) => { const ref = useRef(null); - const [ loading, setLoading ] = useState(false); - const [ servers, setServers ] = useState([]); const isAdmin = useStoreState(state => state.user.data!.rootAdmin); - const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + const [ servers, setServers ] = useState([]); + const { clearAndAddHttpError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); const search = debounce(({ term }: Values, { setSubmitting }: FormikHelpers) => { - setLoading(true); - setSubmitting(false); clearFlashes('search'); - getServers({ query: term }) + // if (ref.current) ref.current.focus(); + getServers({ query: term, type: isAdmin ? 'admin-all' : undefined }) .then(servers => setServers(servers.items.filter((_, index) => index < 5))) .catch(error => { console.error(error); - addError({ key: 'search', message: httpErrorToHuman(error) }); + clearAndAddHttpError({ key: 'search', error }); }) - .then(() => setLoading(false)) + .then(() => setSubmitting(false)) .then(() => ref.current?.focus()); }, 500); @@ -74,7 +72,7 @@ export default ({ ...props }: Props) => { }, [ props.visible ]); // Formik does not support an innerRef on custom components. - const InputWithRef = (props: any) => ; + const InputWithRef = (props: any) => ; return ( { })} initialValues={{ term: '' } as Values} > - -
- - - - - - -
- {servers.length > 0 && -
- { - servers.map(server => ( - props.onDismissed()} - > -
-

{server.name}

-

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

-
-
+ {({ isSubmitting }) => ( + +
+ + + + + + +
+ {servers.length > 0 && +
+ { + servers.map(server => ( + props.onDismissed()} + > +
+

{server.name}

+

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

+
+
{server.node} -
-
- )) +
+ + )) + } +
} -
- } -
+ + )}
); }; diff --git a/resources/scripts/components/elements/InputSpinner.tsx b/resources/scripts/components/elements/InputSpinner.tsx index ce4843dff..cac920f8a 100644 --- a/resources/scripts/components/elements/InputSpinner.tsx +++ b/resources/scripts/components/elements/InputSpinner.tsx @@ -5,12 +5,7 @@ import tw from 'twin.macro'; const InputSpinner = ({ visible, children }: { visible: boolean, children: React.ReactNode }) => (
- +
From f52f13600f9d74e7b8b3c4250274eab7617e463a Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 15 Oct 2020 21:23:31 -0700 Subject: [PATCH 14/77] Fix text wrap on long server names in search bar --- .../components/dashboard/search/SearchModal.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/scripts/components/dashboard/search/SearchModal.tsx b/resources/scripts/components/dashboard/search/SearchModal.tsx index e8a2a7511..5bc1fe290 100644 --- a/resources/scripts/components/dashboard/search/SearchModal.tsx +++ b/resources/scripts/components/dashboard/search/SearchModal.tsx @@ -105,7 +105,7 @@ export default ({ ...props }: Props) => { to={`/server/${server.id}`} onClick={() => props.onDismissed()} > -
+

{server.name}

{ @@ -115,10 +115,10 @@ export default ({ ...props }: Props) => { }

-
- - {server.node} - +
+ + {server.node} +
)) From a4abb2543bba6dc16cba6d41cd68ca5da866ecd5 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 15 Oct 2020 21:28:43 -0700 Subject: [PATCH 15/77] lint --- resources/scripts/components/dashboard/search/SearchModal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/scripts/components/dashboard/search/SearchModal.tsx b/resources/scripts/components/dashboard/search/SearchModal.tsx index 5bc1fe290..67aa518f8 100644 --- a/resources/scripts/components/dashboard/search/SearchModal.tsx +++ b/resources/scripts/components/dashboard/search/SearchModal.tsx @@ -9,7 +9,6 @@ import InputSpinner from '@/components/elements/InputSpinner'; import getServers from '@/api/getServers'; import { Server } from '@/api/server/getServer'; import { ApplicationStore } from '@/state'; -import { httpErrorToHuman } from '@/api/http'; import { Link } from 'react-router-dom'; import styled from 'styled-components/macro'; import tw from 'twin.macro'; From 40d44598daf52f715a2b8b43368f31df142cef56 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 15 Oct 2020 21:32:49 -0700 Subject: [PATCH 16/77] Add test coverage for change to endpoint --- .../Api/Client/ClientControllerTest.php | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/tests/Integration/Api/Client/ClientControllerTest.php b/tests/Integration/Api/Client/ClientControllerTest.php index b894b14b0..14b120eab 100644 --- a/tests/Integration/Api/Client/ClientControllerTest.php +++ b/tests/Integration/Api/Client/ClientControllerTest.php @@ -154,10 +154,42 @@ class ClientControllerTest extends ClientApiIntegrationTestCase } /** - * Test that no servers get returned if the user requests all admin level servers by using - * ?type=admin in the request. + * Test that all servers a user can access as an admin are returned if using ?filter=admin-all. */ - public function testNoServersAreReturnedIfAdminFilterIsPassedByRegularUser() + public function testAllServersAreReturnedToAdmin() + { + /** @var \Pterodactyl\Models\User[] $users */ + $users = factory(User::class)->times(4)->create(); + $users[0]->update(['root_admin' => true]); + + $servers = [ + $this->createServerModel(['user_id' => $users[0]->id]), + $this->createServerModel(['user_id' => $users[1]->id]), + $this->createServerModel(['user_id' => $users[2]->id]), + $this->createServerModel(['user_id' => $users[3]->id]), + ]; + + Subuser::query()->create([ + 'user_id' => $users[0]->id, + 'server_id' => $servers[1]->id, + 'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT], + ]); + + // All servers should be returned. + $response = $this->actingAs($users[0])->getJson('/api/client?type=admin-all'); + + $response->assertOk(); + $response->assertJsonCount(4, 'data'); + } + + /** + * Test that no servers get returned if the user requests all admin level servers by using + * ?type=admin or ?type=admin-all in the request. + * + * @param string $type + * @dataProvider filterTypeDataProvider + */ + public function testNoServersAreReturnedIfAdminFilterIsPassedByRegularUser($type) { /** @var \Pterodactyl\Models\User[] $users */ $users = factory(User::class)->times(3)->create(); @@ -166,9 +198,17 @@ class ClientControllerTest extends ClientApiIntegrationTestCase $this->createServerModel(['user_id' => $users[1]->id]); $this->createServerModel(['user_id' => $users[2]->id]); - $response = $this->actingAs($users[0])->getJson('/api/client?type=admin'); + $response = $this->actingAs($users[0])->getJson('/api/client?type=' . $type); $response->assertOk(); $response->assertJsonCount(0, 'data'); } + + /** + * @return array + */ + public function filterTypeDataProvider() + { + return [['admin'], ['admin-all']]; + } } From cd3572730b8896adec480cb238acbc365fdb5a85 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 15 Oct 2020 21:52:26 -0700 Subject: [PATCH 17/77] Add test coverage to ensure filters don't unexpectedly get broken --- app/Models/Filters/MultiFieldServerFilter.php | 4 +- .../Api/Client/ClientControllerTest.php | 100 ++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/app/Models/Filters/MultiFieldServerFilter.php b/app/Models/Filters/MultiFieldServerFilter.php index ed47d132f..21c45b779 100644 --- a/app/Models/Filters/MultiFieldServerFilter.php +++ b/app/Models/Filters/MultiFieldServerFilter.php @@ -48,12 +48,12 @@ class MultiFieldServerFilter implements Filter function (Builder $builder) use ($parts) { $builder->orWhere('allocations.ip', $parts[0]); if (!is_null($parts[1] ?? null)) { - $builder->where('allocations.port', 'LIKE', "%{$parts[1]}"); + $builder->where('allocations.port', 'LIKE', "{$parts[1]}%"); } }, // Otherwise, just try to search for that specific port in the allocations. function (Builder $builder) use ($value) { - $builder->orWhere('allocations.port', substr($value, 1)); + $builder->orWhere('allocations.port', 'LIKE', substr($value, 1) . '%'); } ); }) diff --git a/tests/Integration/Api/Client/ClientControllerTest.php b/tests/Integration/Api/Client/ClientControllerTest.php index 14b120eab..30d837815 100644 --- a/tests/Integration/Api/Client/ClientControllerTest.php +++ b/tests/Integration/Api/Client/ClientControllerTest.php @@ -6,6 +6,7 @@ use Pterodactyl\Models\User; use Pterodactyl\Models\Server; use Pterodactyl\Models\Subuser; use Pterodactyl\Models\Permission; +use Pterodactyl\Models\Allocation; class ClientControllerTest extends ClientApiIntegrationTestCase { @@ -38,6 +39,105 @@ class ClientControllerTest extends ClientApiIntegrationTestCase $response->assertJsonPath('meta.pagination.per_page', 50); } + /** + * Test that using ?filter[*]=name|uuid returns any server matching that name or UUID + * with the search filters. + */ + public function testServersAreFilteredUsingNameAndUuidInformation() + { + /** @var \Pterodactyl\Models\User[] $users */ + $users = factory(User::class)->times(2)->create(); + $users[0]->update(['root_admin' => true]); + + /** @var \Pterodactyl\Models\Server[] $servers */ + $servers = [ + $this->createServerModel(['user_id' => $users[0]->id, 'name' => 'Julia']), + $this->createServerModel(['user_id' => $users[1]->id, 'uuidShort' => '12121212', 'name' => 'Janice']), + $this->createServerModel(['user_id' => $users[1]->id, 'uuid' => '88788878-12356789', 'external_id' => 'ext123', 'name' => 'Julia']), + $this->createServerModel(['user_id' => $users[1]->id, 'uuid' => '88788878-abcdefgh', 'name' => 'Jennifer']), + ]; + + $this->actingAs($users[1])->getJson('/api/client?filter[*]=Julia') + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.attributes.identifier', $servers[2]->uuidShort); + + $this->actingAs($users[1])->getJson('/api/client?filter[*]=ext123') + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.attributes.identifier', $servers[2]->uuidShort); + + $this->actingAs($users[1])->getJson('/api/client?filter[*]=ext123') + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.attributes.identifier', $servers[2]->uuidShort); + + $this->actingAs($users[1])->getJson('/api/client?filter[*]=12121212') + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.attributes.identifier', $servers[1]->uuidShort); + + $this->actingAs($users[1])->getJson('/api/client?filter[*]=88788878') + ->assertOk() + ->assertJsonCount(2, 'data') + ->assertJsonPath('data.0.attributes.identifier', $servers[2]->uuidShort) + ->assertJsonPath('data.1.attributes.identifier', $servers[3]->uuidShort); + + $this->actingAs($users[1])->getJson('/api/client?filter[*]=88788878-abcd') + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.attributes.identifier', $servers[3]->uuidShort); + + $this->actingAs($users[0])->getJson('/api/client?filter[*]=Julia&type=admin-all') + ->assertOk() + ->assertJsonCount(2, 'data') + ->assertJsonPath('data.0.attributes.identifier', $servers[0]->uuidShort) + ->assertJsonPath('data.1.attributes.identifier', $servers[2]->uuidShort); + } + + /** + * Test that using ?filter[*]=:25565 or ?filter[*]=192.168.1.1:25565 returns only those servers + * with the same allocation for the given user. + */ + public function testServersAreFilteredUsingAllocationInformation() + { + /** @var \Pterodactyl\Models\User $user */ + /** @var \Pterodactyl\Models\Server $server */ + [$user, $server] = $this->generateTestAccount(); + $server2 = $this->createServerModel(['user_id' => $user->id, 'node_id' => $server->node_id]); + + $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id, 'server_id' => $server->id, 'ip' => '192.168.1.1', 'port' => 25565]); + $allocation2 = factory(Allocation::class)->create(['node_id' => $server->node_id, 'server_id' => $server2->id, 'ip' => '192.168.1.1', 'port' => 25570]); + + $server->update(['allocation_id' => $allocation->id]); + $server2->update(['allocation_id' => $allocation2->id]); + + $server->refresh(); + $server2->refresh(); + + $this->actingAs($user)->getJson('/api/client?filter[*]=192.168.1.1') + ->assertOk() + ->assertJsonCount(2, 'data') + ->assertJsonPath('data.0.attributes.identifier', $server->uuidShort) + ->assertJsonPath('data.1.attributes.identifier', $server2->uuidShort); + + $this->actingAs($user)->getJson('/api/client?filter[*]=192.168.1.1:25565') + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.attributes.identifier', $server->uuidShort); + + $this->actingAs($user)->getJson('/api/client?filter[*]=:25570') + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.attributes.identifier', $server2->uuidShort); + + $this->actingAs($user)->getJson('/api/client?filter[*]=:255') + ->assertOk() + ->assertJsonCount(2, 'data') + ->assertJsonPath('data.0.attributes.identifier', $server->uuidShort) + ->assertJsonPath('data.1.attributes.identifier', $server2->uuidShort); + } + /** * Test that servers where the user is a subuser are returned by default in the API call. */ From 7ad26fc45632325c47edae620a27ca7d06a4aa3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20G=2E=20J=C3=B8rgensen?= Date: Fri, 16 Oct 2020 15:49:35 +0200 Subject: [PATCH 18/77] Remove target="blank" from /admin link This should be changed to make it consistent with the admin panel, where the link back to the user panel does not open in a new tab. --- resources/scripts/components/NavigationBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/scripts/components/NavigationBar.tsx b/resources/scripts/components/NavigationBar.tsx index 384a32edc..1bc934ccb 100644 --- a/resources/scripts/components/NavigationBar.tsx +++ b/resources/scripts/components/NavigationBar.tsx @@ -63,7 +63,7 @@ export default () => { {rootAdmin && - + } From eabc17eab35a9d81089c5bc2141574c05c875371 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sat, 17 Oct 2020 19:19:03 +0200 Subject: [PATCH 19/77] add conact links to discord to issue creation page --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..dda021aef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: :shrug: Installation Help + url: https://discord.gg/pterodactyl + about: Please visit our Discord for help with your installation. + - name: :question: General Question + url: https://discord.gg/pterodactyl + about: Please visit our Discord for general questions about Pterodactyl. From 18911d8fe49182bacd0e7ceb4f3d24f148a569a2 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sat, 17 Oct 2020 19:19:42 +0200 Subject: [PATCH 20/77] remove installation help issue template --- .github/ISSUE_TEMPLATE/--installation-help.md | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/--installation-help.md diff --git a/.github/ISSUE_TEMPLATE/--installation-help.md b/.github/ISSUE_TEMPLATE/--installation-help.md deleted file mode 100644 index 4d5d27e71..000000000 --- a/.github/ISSUE_TEMPLATE/--installation-help.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: "⛔ Installation Help" -about: 'Visit our Discord for installation help: https://pterodactyl.io/discord' - ---- - -We use GitHub issues only to discuss about Pterodactyl bugs and new features. For -this kind of questions about using Pterodactyl, please visit our Discord for assistance: https://pterodactyl.io/discord - -DO NOT REPORT ISSUES CONFIGURING: SSL, PHP, APACHE, NGINX, YOUR MACHINE, SSH, SFTP, ETC. ON THIS GITHUB TRACKER. - -For assistance installating this software, as well as debugging issues with dependencies, please use our discord server: https://discord.gg/pterodactyl - -PLEASE stop spamming our tracker with "bugs" that are not related to this project. From 652e93871d38db0817fc24d05b23fab517b8d668 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sat, 17 Oct 2020 19:25:09 +0200 Subject: [PATCH 21/77] use emoji directly instead of :names: --- .github/ISSUE_TEMPLATE/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index dda021aef..b3b99a661 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: true contact_links: - - name: :shrug: Installation Help + - name: 🤷 Installation Help url: https://discord.gg/pterodactyl about: Please visit our Discord for help with your installation. - - name: :question: General Question + - name: ❓ General Question url: https://discord.gg/pterodactyl about: Please visit our Discord for general questions about Pterodactyl. From 10548c9d8fa79f10df915b9c4aaaa7aa98e42ad1 Mon Sep 17 00:00:00 2001 From: Nobody Date: Sat, 17 Oct 2020 18:26:34 +0100 Subject: [PATCH 22/77] Update message and update query --- app/Console/Commands/User/DeleteUserCommand.php | 5 +++-- resources/lang/en/command/messages.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/User/DeleteUserCommand.php b/app/Console/Commands/User/DeleteUserCommand.php index f458b51dc..64885dca1 100644 --- a/app/Console/Commands/User/DeleteUserCommand.php +++ b/app/Console/Commands/User/DeleteUserCommand.php @@ -58,8 +58,9 @@ class DeleteUserCommand extends Command Assert::notEmpty($search, 'Search term should be an email address, got: %s.'); $results = User::query() - ->where('email', 'LIKE', "$search%") - ->where('username', 'LIKE', "$search%") + ->where('id', 'LIKE', "$search%") + ->orWhere('username', 'LIKE', "$search%") + ->orWhere('email', 'LIKE', "$search%") ->get(); if (count($results) < 1) { diff --git a/resources/lang/en/command/messages.php b/resources/lang/en/command/messages.php index 71731a2d0..002c699bb 100644 --- a/resources/lang/en/command/messages.php +++ b/resources/lang/en/command/messages.php @@ -14,7 +14,7 @@ return [ 'deleted' => 'Successfully deleted the requested location.', ], 'user' => [ - 'search_users' => 'Enter a Username, UUID, or Email Address', + 'search_users' => 'Enter a Username, User ID, or Email Address', 'select_search_user' => 'ID of user to delete (Enter \'0\' to re-search)', 'deleted' => 'User successfully deleted from the Panel.', 'confirm_delete' => 'Are you sure you want to delete this user from the Panel?', From 839e277763e4ae0f511ababfa447830e4aec9c65 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 11:52:21 -0700 Subject: [PATCH 23/77] Fix exception when passing location IDs to creation service; closes #2529 --- .../Deployment/FindViableNodesService.php | 7 ++--- .../Deployment/FindViableNodesServiceTest.php | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/app/Services/Deployment/FindViableNodesService.php b/app/Services/Deployment/FindViableNodesService.php index d89c73f5e..8c3d7c757 100644 --- a/app/Services/Deployment/FindViableNodesService.php +++ b/app/Services/Deployment/FindViableNodesService.php @@ -4,7 +4,6 @@ namespace Pterodactyl\Services\Deployment; use Webmozart\Assert\Assert; use Pterodactyl\Models\Node; -use Illuminate\Support\LazyCollection; use Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException; class FindViableNodesService @@ -32,7 +31,7 @@ class FindViableNodesService */ public function setLocations(array $locations): self { - Assert::allInteger($locations, 'An array of location IDs should be provided when calling setLocations.'); + Assert::allIntegerish($locations, 'An array of location IDs should be provided when calling setLocations.'); $this->locations = $locations; @@ -97,8 +96,8 @@ class FindViableNodesService } $results = $query->groupBy('nodes.id') - ->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [ $this->memory ]) - ->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [ $this->disk ]) + ->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [$this->memory]) + ->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [$this->disk]) ->get() ->toBase(); diff --git a/tests/Integration/Services/Deployment/FindViableNodesServiceTest.php b/tests/Integration/Services/Deployment/FindViableNodesServiceTest.php index 47261dbe8..abc0bd9a7 100644 --- a/tests/Integration/Services/Deployment/FindViableNodesServiceTest.php +++ b/tests/Integration/Services/Deployment/FindViableNodesServiceTest.php @@ -2,6 +2,7 @@ namespace Pterodactyl\Tests\Integration\Services\Deployment; +use Exception; use Pterodactyl\Models\Node; use InvalidArgumentException; use Pterodactyl\Models\Server; @@ -39,6 +40,34 @@ class FindViableNodesServiceTest extends IntegrationTestCase $this->getService()->setDisk(10)->handle(); } + /** + * Ensure that errors are not thrown back when passing in expected values. + * + * @see https://github.com/pterodactyl/panel/issues/2529 + */ + public function testNoExceptionIsThrownIfStringifiedIntegersArePassedForLocations() + { + $this->getService()->setLocations([1, 2, 3]); + $this->getService()->setLocations(['1', '2', '3']); + $this->getService()->setLocations(['1', 2, 3]); + + try { + $this->getService()->setLocations(['a']); + $this->assertTrue(false, 'This expectation should not be called.'); + } catch (Exception $exception) { + $this->assertInstanceOf(InvalidArgumentException::class, $exception); + $this->assertSame('An array of location IDs should be provided when calling setLocations.', $exception->getMessage()); + } + + try { + $this->getService()->setLocations(['1.2', '1', 2]); + $this->assertTrue(false, 'This expectation should not be called.'); + } catch (Exception $exception) { + $this->assertInstanceOf(InvalidArgumentException::class, $exception); + $this->assertSame('An array of location IDs should be provided when calling setLocations.', $exception->getMessage()); + } + } + public function testExpectedNodeIsReturnedForLocation() { /** @var \Pterodactyl\Models\Location[] $locations */ From b8377b840f2c7f752fc113dc4466b5487ec78401 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 12:11:14 -0700 Subject: [PATCH 24/77] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef97f3f8c..2bef0557f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/pterodactyl/panel/tests?label=Tests&style=for-the-badge) ![Discord](https://img.shields.io/discord/122900397965705216?label=Discord&logo=Discord&logoColor=white&style=for-the-badge) ![GitHub Releases](https://img.shields.io/github/downloads/pterodactyl/panel/latest/total?style=for-the-badge) -![GitHub Pre-Releases](https://img.shields.io/github/downloads-pre/pterodactyl/panel/v1.0.0-rc.7/total?style=for-the-badge) ![GitHub contributors](https://img.shields.io/github/contributors/pterodactyl/panel?style=for-the-badge) # Pterodactyl Panel @@ -28,6 +27,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f | [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | | [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. | | [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHosting’s reliable servers and network. Easy to use, provisioned in a couple of minutes. | +| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. | ## Documentation * [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html) From b02a49f42e56162970ddfcf728b50f46e05d8128 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 12:23:25 -0700 Subject: [PATCH 25/77] Get everything on the same version --- package.json | 26 +- yarn.lock | 1345 +++++++++++++++++++++++++++++--------------------- 2 files changed, 796 insertions(+), 575 deletions(-) diff --git a/package.json b/package.json index 8d2aec795..f7c7b8b81 100644 --- a/package.json +++ b/package.json @@ -45,23 +45,23 @@ "yup": "^0.29.1" }, "devDependencies": { - "@babel/core": "^7.7.5", - "@babel/plugin-proposal-class-properties": "^7.7.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.7.4", - "@babel/plugin-proposal-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-dynamic-import": "^7.7.4", - "@babel/plugin-transform-react-jsx": "^7.10.4", - "@babel/plugin-transform-runtime": "^7.7.5", - "@babel/preset-env": "^7.7.5", - "@babel/preset-react": "^7.7.4", - "@babel/preset-typescript": "^7.7.4", - "@babel/runtime": "^7.7.5", + "@babel/core": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-react-jsx": "^7.12.1", + "@babel/plugin-transform-runtime": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.1", + "@babel/preset-typescript": "^7.12.1", + "@babel/runtime": "^7.12.1", "@types/chart.js": "^2.8.5", "@types/codemirror": "^0.0.98", "@types/debounce": "^1.2.0", "@types/events": "^3.0.0", - "@types/node": "^12.6.9", + "@types/node": "^14.11.10", "@types/query-string": "^6.3.0", "@types/react": "^16.9.41", "@types/react-dom": "^16.9.8", diff --git a/yarn.lock b/yarn.lock index 9c1724ca9..d25cfe4f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": +"@babel/code-frame@^7.0.0": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" dependencies: @@ -15,21 +15,29 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/core@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.5.tgz#ae1323cd035b5160293307f50647e83f8ba62f7e" +"@babel/compat-data@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.1.tgz#d7386a689aa0ddf06255005b4b991988021101a0" + integrity sha512-725AQupWJZ8ba0jbKceeFblZTY90McUBWMwHhkFQ9q1zKPJ95GUktljFcgcsIVwRnTnRKlcYzfiNImg5G9m6ZQ== + +"@babel/core@^7.12.1": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" + integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.4" - "@babel/helpers" "^7.7.4" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.1" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.1" + "@babel/parser" "^7.12.3" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" convert-source-map "^1.7.0" debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.13" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" @@ -44,16 +52,16 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369" +"@babel/generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" + integrity sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.12.1" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.7.4": +"@babel/helper-annotate-as-pure@^7.0.0": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz#bb3faf1e74b74bd547e867e48f551fa6b098b6ce" dependencies: @@ -66,21 +74,22 @@ dependencies: "@babel/types" "^7.10.4" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz#5f73f2b28580e224b5b9bd03146a4015d6217f5f" - dependencies: - "@babel/helper-explode-assignable-expression" "^7.7.4" - "@babel/types" "^7.7.4" - -"@babel/helper-builder-react-jsx-experimental@^7.10.4": +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.4.tgz#d0ffb875184d749c63ffe1f4f65be15143ec322d" - integrity sha512-LyacH/kgQPgLAuaWrvvq1+E7f5bLyT8jXCh7nM67sRsy2cpIGfgWJ+FCnAKQXfY+F0tXUaN6FqLkp4JiCzdK8Q== + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-builder-react-jsx-experimental@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.1.tgz#1f1ad4c95f1d059856d2fdbc0763489d020cd02d" + integrity sha512-82to8lR7TofZWbTd3IEZT1xNHfeU/Ef4rDm/GLXddzqDh+yQ19QuGSzqww51aNxVH8rwfRIzL0EUQsvODVhtyw== dependencies: "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-module-imports" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/helper-module-imports" "^7.12.1" + "@babel/types" "^7.12.1" "@babel/helper-builder-react-jsx@^7.10.4": version "7.10.4" @@ -90,53 +99,51 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helper-builder-react-jsx@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.7.4.tgz#da188d247508b65375b2c30cf59de187be6b0c66" +"@babel/helper-compilation-targets@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.1.tgz#310e352888fbdbdd8577be8dfdd2afb9e7adcf50" + integrity sha512-jtBEif7jsPwP27GPHs06v4WBV0KrE8a/P7n0N0sSvHn2hwUCYnolP/CLmz51IzAW4NlN+HuoBtb9QcwnRo9F/g== dependencies: - "@babel/types" "^7.7.4" - esutils "^2.0.0" + "@babel/compat-data" "^7.12.1" + "@babel/helper-validator-option" "^7.12.1" + browserslist "^4.12.0" + semver "^5.5.0" -"@babel/helper-call-delegate@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz#621b83e596722b50c0066f9dc37d3232e461b801" +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== dependencies: - "@babel/helper-hoist-variables" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" -"@babel/helper-create-class-features-plugin@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.4.tgz#fce60939fd50618610942320a8d951b3b639da2d" +"@babel/helper-create-regexp-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz#18b1302d4677f9dc4740fe8c9ed96680e29d37e8" + integrity sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA== dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-member-expression-to-functions" "^7.7.4" - "@babel/helper-optimise-call-expression" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.1" -"@babel/helper-create-regexp-features-plugin@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz#6d5762359fd34f4da1500e4cff9955b5299aaf59" +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== dependencies: - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.6.0" + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" -"@babel/helper-define-map@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz#2841bf92eb8bd9c906851546fe6b9d45e162f176" +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz#8006a466695c4ad86a2a5f2fb15b5f2c31ad5633" + integrity sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA== dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/types" "^7.7.4" - lodash "^4.17.13" - -"@babel/helper-explode-assignable-expression@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz#fa700878e008d85dc51ba43e9fb835cddfe05c84" - dependencies: - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.12.1" "@babel/helper-function-name@^7.10.4": version "7.10.4" @@ -147,14 +154,6 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helper-function-name@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" - dependencies: - "@babel/helper-get-function-arity" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" - "@babel/helper-get-function-arity@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" @@ -162,54 +161,55 @@ dependencies: "@babel/types" "^7.10.4" -"@babel/helper-get-function-arity@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.10.4" -"@babel/helper-hoist-variables@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz#612384e3d823fdfaaf9fce31550fe5d4db0f3d12" +"@babel/helper-member-expression-to-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz#fba0f2fcff3fba00e6ecb664bb5e6e26e2d6165c" + integrity sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.12.1" -"@babel/helper-member-expression-to-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz#356438e2569df7321a8326644d4b790d2122cb74" - dependencies: - "@babel/types" "^7.7.4" - -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.7.4": +"@babel/helper-module-imports@^7.0.0": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz#e5a92529f8888bf319a6376abfbd1cebc491ad91" dependencies: "@babel/types" "^7.7.4" -"@babel/helper-module-imports@^7.10.4": +"@babel/helper-module-imports@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz#1644c01591a15a2f084dd6d092d9430eb1d1216c" + integrity sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-module-transforms@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" + integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-simple-access" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/helper-validator-identifier" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" - integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== dependencies: "@babel/types" "^7.10.4" -"@babel/helper-module-transforms@^7.7.4", "@babel/helper-module-transforms@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz#d044da7ffd91ec967db25cd6748f704b6b244835" - dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-simple-access" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" - lodash "^4.17.13" - -"@babel/helper-optimise-call-expression@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz#034af31370d2995242aa4df402c3b7794b2dcdf2" - dependencies: - "@babel/types" "^7.7.4" - "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" @@ -223,37 +223,45 @@ version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" -"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== dependencies: - lodash "^4.17.13" + lodash "^4.17.19" -"@babel/helper-remap-async-to-generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz#c68c2407350d9af0e061ed6726afb4fff16d0234" +"@babel/helper-remap-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd" + integrity sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A== dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-wrap-function" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/types" "^7.12.1" -"@babel/helper-replace-supers@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz#3c881a6a6a7571275a72d82e6107126ec9e2cdd2" +"@babel/helper-replace-supers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz#f15c9cc897439281891e11d5ce12562ac0cf3fa9" + integrity sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw== dependencies: - "@babel/helper-member-expression-to-functions" "^7.7.4" - "@babel/helper-optimise-call-expression" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" -"@babel/helper-simple-access@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz#a169a0adb1b5f418cfc19f22586b2ebf58a9a294" +"@babel/helper-simple-access@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" + integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== dependencies: - "@babel/template" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/types" "^7.12.1" + +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + dependencies: + "@babel/types" "^7.12.1" "@babel/helper-split-export-declaration@^7.10.4": version "7.10.4" @@ -262,33 +270,41 @@ dependencies: "@babel/types" "^7.10.4" -"@babel/helper-split-export-declaration@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8" +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== dependencies: - "@babel/types" "^7.7.4" + "@babel/types" "^7.11.0" "@babel/helper-validator-identifier@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== -"@babel/helper-wrap-function@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz#37ab7fed5150e22d9d7266e830072c0cdd8baace" - dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" +"@babel/helper-validator-option@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" + integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== -"@babel/helpers@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" +"@babel/helper-wrap-function@^7.10.4": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz#3332339fc4d1fbbf1c27d7958c27d34708e990d9" + integrity sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow== dependencies: - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helpers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.1.tgz#8a8261c1d438ec18cb890434df4ec768734c1e79" + integrity sha512-9JoDSBGoWtmbay98efmT2+mySkwjzeFeAL9BuWNoVQpkPFQF8SIIFUfY5os9u8wVzglzoiPRSW7cuJmBDUt43g== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" "@babel/highlight@^7.0.0": version "7.0.0" @@ -312,105 +328,166 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.4.tgz#9eedf27e1998d87739fb5028a5120557c06a1a64" integrity sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA== -"@babel/parser@^7.7.4", "@babel/parser@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71" +"@babel/parser@^7.12.1", "@babel/parser@^7.12.3": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.3.tgz#a305415ebe7a6c7023b40b5122a0662d928334cd" + integrity sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw== -"@babel/plugin-proposal-async-generator-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz#0351c5ac0a9e927845fffd5b82af476947b7ce6d" +"@babel/plugin-proposal-async-generator-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e" + integrity sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.7.4" - "@babel/plugin-syntax-async-generators" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz#2f964f0cb18b948450362742e33e15211e77c2ba" +"@babel/plugin-proposal-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" + integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== dependencies: - "@babel/helper-create-class-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-proposal-dynamic-import@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.4.tgz#dde64a7f127691758cbfed6cf70de0fa5879d52d" +"@babel/plugin-proposal-dynamic-import@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz#43eb5c2a3487ecd98c5c8ea8b5fdb69a2749b2dc" + integrity sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" -"@babel/plugin-proposal-json-strings@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz#7700a6bfda771d8dc81973249eac416c6b4c697d" +"@babel/plugin-proposal-export-namespace-from@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz#8b9b8f376b2d88f5dd774e4d24a5cc2e3679b6d4" + integrity sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-json-strings" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" - integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== +"@babel/plugin-proposal-json-strings@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz#d45423b517714eedd5621a9dfdc03fa9f4eb241c" + integrity sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-logical-assignment-operators@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz#f2c490d36e1b3c9659241034a5d2cd50263a2751" + integrity sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz#3ed4fff31c015e7f3f1467f190dbe545cd7b046c" + integrity sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-object-rest-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz#cc57849894a5c774214178c8ab64f6334ec8af71" +"@babel/plugin-proposal-numeric-separator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.1.tgz#0e2c6774c4ce48be412119b4d693ac777f7685a6" + integrity sha512-MR7Ok+Af3OhNTCxYVjJZHS0t97ydnJZt/DbR4WISO39iDnhiD8XHrY12xuSJ90FFEGjir0Fzyyn7g/zY6hxbxA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-optional-catch-binding@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz#ec21e8aeb09ec6711bc0a39ca49520abee1de379" +"@babel/plugin-proposal-object-rest-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" -"@babel/plugin-proposal-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543" +"@babel/plugin-proposal-optional-catch-binding@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz#ccc2421af64d3aae50b558a71cede929a5ab2942" + integrity sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g== dependencies: - "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz#cce122203fc8a32794296fc377c6dedaf4363797" + integrity sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-unicode-property-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.4.tgz#7c239ccaf09470dbe1d453d50057460e84517ebb" +"@babel/plugin-proposal-private-methods@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz#86814f6e7a21374c980c10d38b4493e703f4a389" + integrity sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-async-generators@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz#331aaf310a10c80c44a66b238b6e49132bd3c889" +"@babel/plugin-proposal-unicode-property-regex@^7.12.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz#2a183958d417765b9eae334f47758e5d6a82e072" + integrity sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-dynamic-import@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz#29ca3b4415abfe4a5ec381e903862ad1a54c3aec" +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-json-strings@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz#86e63f7d2e22f9e27129ac4e83ea989a382e86cc" - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-jsx@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c" - integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g== +"@babel/plugin-syntax-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz#bcb297c5366e79bebadef509549cd93b04f19978" + integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-jsx@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz#dab2b56a36fb6c3c222a1fbc71f7bf97f327a9ec" +"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": version "7.8.3" @@ -419,17 +496,26 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-object-rest-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz#47cf220d19d6d0d7b154304701f468fc1cc6ff46" +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-optional-catch-binding@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz#a3e38f59f4b6233867b4a92dcb0ee05b2c334aa6" +"@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.0": version "7.8.3" @@ -437,365 +523,454 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz#bd7d8fa7b9fee793a36e4027fd6dd1aa32f946da" +"@babel/plugin-syntax-top-level-await@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz#dd6c0b357ac1bb142d98537450a319625d13d2a0" + integrity sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-typescript@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.7.4.tgz#5d037ffa10f3b25a16f32570ebbe7a8c2efa304b" +"@babel/plugin-syntax-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz#460ba9d77077653803c3dd2e673f76d66b4029e5" + integrity sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-arrow-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz#76309bd578addd8aee3b379d809c802305a98a12" +"@babel/plugin-transform-arrow-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz#8083ffc86ac8e777fbe24b5967c4b2521f3cb2b3" + integrity sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-async-to-generator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz#694cbeae6d613a34ef0292713fa42fb45c4470ba" +"@babel/plugin-transform-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz#3849a49cc2a22e9743cbd6b52926d30337229af1" + integrity sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A== dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.7.4" + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" -"@babel/plugin-transform-block-scoped-functions@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz#d0d9d5c269c78eaea76227ace214b8d01e4d837b" +"@babel/plugin-transform-block-scoped-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz#f2a1a365bde2b7112e0a6ded9067fdd7c07905d9" + integrity sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-block-scoping@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz#200aad0dcd6bb80372f94d9e628ea062c58bf224" +"@babel/plugin-transform-block-scoping@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz#f0ee727874b42a208a48a586b84c3d222c2bbef1" + integrity sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-classes@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz#c92c14be0a1399e15df72667067a8f510c9400ec" +"@babel/plugin-transform-classes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz#65e650fcaddd3d88ddce67c0f834a3d436a32db6" + integrity sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog== dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-define-map" "^7.7.4" - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-optimise-call-expression" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz#e856c1628d3238ffe12d668eb42559f79a81910d" +"@babel/plugin-transform-computed-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz#d68cf6c9b7f838a8a4144badbe97541ea0904852" + integrity sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-destructuring@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz#2b713729e5054a1135097b6a67da1b6fe8789267" +"@babel/plugin-transform-destructuring@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz#b9a570fe0d0a8d460116413cb4f97e8e08b2f847" + integrity sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-dotall-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.4.tgz#f7ccda61118c5b7a2599a72d5e3210884a021e96" +"@babel/plugin-transform-dotall-regex@^7.12.1", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz#a1d16c14862817b6409c0a678d6f9373ca9cd975" + integrity sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-duplicate-keys@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz#3d21731a42e3f598a73835299dd0169c3b90ac91" +"@babel/plugin-transform-duplicate-keys@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz#745661baba295ac06e686822797a69fbaa2ca228" + integrity sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-exponentiation-operator@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz#dd30c0191e3a1ba19bcc7e389bdfddc0729d5db9" +"@babel/plugin-transform-exponentiation-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz#b0f2ed356ba1be1428ecaf128ff8a24f02830ae0" + integrity sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-for-of@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz#248800e3a5e507b1f103d8b4ca998e77c63932bc" +"@babel/plugin-transform-for-of@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz#07640f28867ed16f9511c99c888291f560921cfa" + integrity sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-function-name@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz#75a6d3303d50db638ff8b5385d12451c865025b1" +"@babel/plugin-transform-function-name@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz#2ec76258c70fe08c6d7da154003a480620eba667" + integrity sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw== dependencies: - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz#27fe87d2b5017a2a5a34d1c41a6b9f6a6262643e" +"@babel/plugin-transform-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz#d73b803a26b37017ddf9d3bb8f4dc58bfb806f57" + integrity sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz#aee127f2f3339fc34ce5e3055d7ffbf7aa26f19a" +"@babel/plugin-transform-member-expression-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz#496038602daf1514a64d43d8e17cbb2755e0c3ad" + integrity sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-modules-amd@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz#39e0fb717224b59475b306402bb8eedab01e729c" +"@babel/plugin-transform-modules-amd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz#3154300b026185666eebb0c0ed7f8415fefcf6f9" + integrity sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ== dependencies: - "@babel/helper-module-transforms" "^7.7.5" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz#1d27f5eb0bcf7543e774950e5b2fa782e637b345" +"@babel/plugin-transform-modules-commonjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz#fa403124542636c786cf9b460a0ffbb48a86e648" + integrity sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag== dependencies: - "@babel/helper-module-transforms" "^7.7.5" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.7.4" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.12.1" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz#cd98152339d3e763dfe838b7d4273edaf520bb30" +"@babel/plugin-transform-modules-systemjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz#663fea620d593c93f214a464cd399bf6dc683086" + integrity sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q== dependencies: - "@babel/helper-hoist-variables" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-identifier" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz#1027c355a118de0aae9fee00ad7813c584d9061f" +"@babel/plugin-transform-modules-umd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz#eb5a218d6b1c68f3d6217b8fa2cc82fec6547902" + integrity sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q== dependencies: - "@babel/helper-module-transforms" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-named-capturing-groups-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz#fb3bcc4ee4198e7385805007373d6b6f42c98220" +"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz#b407f5c96be0d9f5f88467497fa82b30ac3e8753" + integrity sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" + "@babel/helper-create-regexp-features-plugin" "^7.12.1" -"@babel/plugin-transform-new-target@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz#4a0753d2d60639437be07b592a9e58ee00720167" +"@babel/plugin-transform-new-target@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz#80073f02ee1bb2d365c3416490e085c95759dec0" + integrity sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-object-super@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz#48488937a2d586c0148451bf51af9d7dda567262" +"@babel/plugin-transform-object-super@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz#4ea08696b8d2e65841d0c7706482b048bed1066e" + integrity sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" -"@babel/plugin-transform-parameters@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.4.tgz#da4555c97f39b51ac089d31c7380f03bca4075ce" +"@babel/plugin-transform-parameters@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz#d2e963b038771650c922eff593799c96d853255d" + integrity sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg== dependencies: - "@babel/helper-call-delegate" "^7.7.4" - "@babel/helper-get-function-arity" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-property-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz#2388d6505ef89b266103f450f9167e6bd73f98c2" +"@babel/plugin-transform-property-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz#41bc81200d730abb4456ab8b3fbd5537b59adecd" + integrity sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-display-name@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.7.4.tgz#9f2b80b14ebc97eef4a9b29b612c58ed9c0d10dd" +"@babel/plugin-transform-react-display-name@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz#1cbcd0c3b1d6648c55374a22fc9b6b7e5341c00d" + integrity sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-jsx-self@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.7.4.tgz#81b8fbfd14b2215e8f1c2c3adfba266127b0231c" +"@babel/plugin-transform-react-jsx-development@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.1.tgz#0b8f8cd531dcf7991f1e5f2c10a2a4f1cfc78e36" + integrity sha512-IilcGWdN1yNgEGOrB96jbTplRh+V2Pz1EoEwsKsHfX1a/L40cUYuD71Zepa7C+ujv7kJIxnDftWeZbKNEqZjCQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.7.4" + "@babel/helper-builder-react-jsx-experimental" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.12.1" -"@babel/plugin-transform-react-jsx-source@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.7.4.tgz#8994b1bf6014b133f5a46d3b7d1ee5f5e3e72c10" +"@babel/plugin-transform-react-jsx-self@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.1.tgz#ef43cbca2a14f1bd17807dbe4376ff89d714cf28" + integrity sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-jsx@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" - integrity sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A== +"@babel/plugin-transform-react-jsx-source@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.1.tgz#d07de6863f468da0809edcf79a1aa8ce2a82a26b" + integrity sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-react-jsx@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.1.tgz#c2d96c77c2b0e4362cc4e77a43ce7c2539d478cb" + integrity sha512-RmKejwnT0T0QzQUzcbP5p1VWlpnP8QHtdhEtLG55ZDQnJNalbF3eeDyu3dnGKvGzFIQiBzFhBYTwvv435p9Xpw== dependencies: "@babel/helper-builder-react-jsx" "^7.10.4" - "@babel/helper-builder-react-jsx-experimental" "^7.10.4" + "@babel/helper-builder-react-jsx-experimental" "^7.12.1" "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.12.1" -"@babel/plugin-transform-react-jsx@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.7.4.tgz#d91205717fae4e2f84d020cd3057ec02a10f11da" +"@babel/plugin-transform-react-pure-annotations@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz#05d46f0ab4d1339ac59adf20a1462c91b37a1a42" + integrity sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg== dependencies: - "@babel/helper-builder-react-jsx" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.7.4" + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-regenerator@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz#3a8757ee1a2780f390e89f246065ecf59c26fce9" +"@babel/plugin-transform-regenerator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz#5f0a28d842f6462281f06a964e88ba8d7ab49753" + integrity sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng== dependencies: - regenerator-transform "^0.14.0" + regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.7.4.tgz#6a7cf123ad175bb5c69aec8f6f0770387ed3f1eb" +"@babel/plugin-transform-reserved-words@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz#6fdfc8cc7edcc42b36a7c12188c6787c873adcd8" + integrity sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-runtime@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.5.tgz#b0bd98f54f10b068d66c55bf85c7ab29587627a6" +"@babel/plugin-transform-runtime@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz#04b792057eb460389ff6a4198e377614ea1e7ba5" + integrity sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg== dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-shorthand-properties@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz#74a0a9b2f6d67a684c6fbfd5f0458eb7ba99891e" +"@babel/plugin-transform-shorthand-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz#0bf9cac5550fce0cfdf043420f661d645fdc75e3" + integrity sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-spread@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz#aa673b356fe6b7e70d69b6e33a17fef641008578" +"@babel/plugin-transform-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz#527f9f311be4ec7fdc2b79bb89f7bf884b3e1e1e" + integrity sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" -"@babel/plugin-transform-sticky-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz#ffb68c05090c30732076b1285dc1401b404a123c" +"@babel/plugin-transform-sticky-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz#5c24cf50de396d30e99afc8d1c700e8bce0f5caf" + integrity sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" -"@babel/plugin-transform-template-literals@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz#1eb6411736dd3fe87dbd20cc6668e5121c17d604" +"@babel/plugin-transform-template-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz#b43ece6ed9a79c0c71119f576d299ef09d942843" + integrity sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw== dependencies: - "@babel/helper-annotate-as-pure" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-typeof-symbol@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz#3174626214f2d6de322882e498a38e8371b2140e" +"@babel/plugin-transform-typeof-symbol@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz#9ca6be343d42512fbc2e68236a82ae64bc7af78a" + integrity sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-typescript@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.7.4.tgz#2974fd05f4e85c695acaf497f432342de9fc0636" +"@babel/plugin-transform-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz#d92cc0af504d510e26a754a7dbc2e5c8cd9c7ab4" + integrity sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-typescript" "^7.7.4" + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.12.1" -"@babel/plugin-transform-unicode-regex@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz#a3c0f65b117c4c81c5b6484f2a5e7b95346b83ae" +"@babel/plugin-transform-unicode-escapes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz#5232b9f81ccb07070b7c3c36c67a1b78f1845709" + integrity sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-plugin-utils" "^7.10.4" -"@babel/preset-env@^7.7.5": - version "7.7.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.7.5.tgz#f28573ed493edb4ba763b37fb4fbb85601469370" +"@babel/plugin-transform-unicode-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz#cc9661f61390db5c65e3febaccefd5c6ac3faecb" + integrity sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg== dependencies: - "@babel/helper-module-imports" "^7.7.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.7.4" - "@babel/plugin-proposal-dynamic-import" "^7.7.4" - "@babel/plugin-proposal-json-strings" "^7.7.4" - "@babel/plugin-proposal-object-rest-spread" "^7.7.4" - "@babel/plugin-proposal-optional-catch-binding" "^7.7.4" - "@babel/plugin-proposal-unicode-property-regex" "^7.7.4" - "@babel/plugin-syntax-async-generators" "^7.7.4" - "@babel/plugin-syntax-dynamic-import" "^7.7.4" - "@babel/plugin-syntax-json-strings" "^7.7.4" - "@babel/plugin-syntax-object-rest-spread" "^7.7.4" - "@babel/plugin-syntax-optional-catch-binding" "^7.7.4" - "@babel/plugin-syntax-top-level-await" "^7.7.4" - "@babel/plugin-transform-arrow-functions" "^7.7.4" - "@babel/plugin-transform-async-to-generator" "^7.7.4" - "@babel/plugin-transform-block-scoped-functions" "^7.7.4" - "@babel/plugin-transform-block-scoping" "^7.7.4" - "@babel/plugin-transform-classes" "^7.7.4" - "@babel/plugin-transform-computed-properties" "^7.7.4" - "@babel/plugin-transform-destructuring" "^7.7.4" - "@babel/plugin-transform-dotall-regex" "^7.7.4" - "@babel/plugin-transform-duplicate-keys" "^7.7.4" - "@babel/plugin-transform-exponentiation-operator" "^7.7.4" - "@babel/plugin-transform-for-of" "^7.7.4" - "@babel/plugin-transform-function-name" "^7.7.4" - "@babel/plugin-transform-literals" "^7.7.4" - "@babel/plugin-transform-member-expression-literals" "^7.7.4" - "@babel/plugin-transform-modules-amd" "^7.7.5" - "@babel/plugin-transform-modules-commonjs" "^7.7.5" - "@babel/plugin-transform-modules-systemjs" "^7.7.4" - "@babel/plugin-transform-modules-umd" "^7.7.4" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.7.4" - "@babel/plugin-transform-new-target" "^7.7.4" - "@babel/plugin-transform-object-super" "^7.7.4" - "@babel/plugin-transform-parameters" "^7.7.4" - "@babel/plugin-transform-property-literals" "^7.7.4" - "@babel/plugin-transform-regenerator" "^7.7.5" - "@babel/plugin-transform-reserved-words" "^7.7.4" - "@babel/plugin-transform-shorthand-properties" "^7.7.4" - "@babel/plugin-transform-spread" "^7.7.4" - "@babel/plugin-transform-sticky-regex" "^7.7.4" - "@babel/plugin-transform-template-literals" "^7.7.4" - "@babel/plugin-transform-typeof-symbol" "^7.7.4" - "@babel/plugin-transform-unicode-regex" "^7.7.4" - "@babel/types" "^7.7.4" - browserslist "^4.6.0" - core-js-compat "^3.4.7" - invariant "^2.2.2" - js-levenshtein "^1.1.3" + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/preset-env@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" + integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== + dependencies: + "@babel/compat-data" "^7.12.1" + "@babel/helper-compilation-targets" "^7.12.1" + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-option" "^7.12.1" + "@babel/plugin-proposal-async-generator-functions" "^7.12.1" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-dynamic-import" "^7.12.1" + "@babel/plugin-proposal-export-namespace-from" "^7.12.1" + "@babel/plugin-proposal-json-strings" "^7.12.1" + "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-numeric-separator" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.1" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.12.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.12.1" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-async-to-generator" "^7.12.1" + "@babel/plugin-transform-block-scoped-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.1" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-computed-properties" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-dotall-regex" "^7.12.1" + "@babel/plugin-transform-duplicate-keys" "^7.12.1" + "@babel/plugin-transform-exponentiation-operator" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-function-name" "^7.12.1" + "@babel/plugin-transform-literals" "^7.12.1" + "@babel/plugin-transform-member-expression-literals" "^7.12.1" + "@babel/plugin-transform-modules-amd" "^7.12.1" + "@babel/plugin-transform-modules-commonjs" "^7.12.1" + "@babel/plugin-transform-modules-systemjs" "^7.12.1" + "@babel/plugin-transform-modules-umd" "^7.12.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1" + "@babel/plugin-transform-new-target" "^7.12.1" + "@babel/plugin-transform-object-super" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-property-literals" "^7.12.1" + "@babel/plugin-transform-regenerator" "^7.12.1" + "@babel/plugin-transform-reserved-words" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/plugin-transform-sticky-regex" "^7.12.1" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/plugin-transform-typeof-symbol" "^7.12.1" + "@babel/plugin-transform-unicode-escapes" "^7.12.1" + "@babel/plugin-transform-unicode-regex" "^7.12.1" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.12.1" + core-js-compat "^3.6.2" semver "^5.5.0" -"@babel/preset-react@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.7.4.tgz#3fe2ea698d8fb536d8e7881a592c3c1ee8bf5707" +"@babel/preset-modules@^0.1.3": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" + integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.7.4" - "@babel/plugin-transform-react-jsx" "^7.7.4" - "@babel/plugin-transform-react-jsx-self" "^7.7.4" - "@babel/plugin-transform-react-jsx-source" "^7.7.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" -"@babel/preset-typescript@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.7.4.tgz#780059a78e6fa7f7a4c87f027292a86b31ce080a" +"@babel/preset-react@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.12.1.tgz#7f022b13f55b6dd82f00f16d1c599ae62985358c" + integrity sha512-euCExymHCi0qB9u5fKw7rvlw7AZSjw/NaB9h7EkdTt5+yHRrXdiRTh7fkG3uBPpJg82CqLfp1LHLqWGSCrab+g== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-transform-typescript" "^7.7.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-display-name" "^7.12.1" + "@babel/plugin-transform-react-jsx" "^7.12.1" + "@babel/plugin-transform-react-jsx-development" "^7.12.1" + "@babel/plugin-transform-react-jsx-self" "^7.12.1" + "@babel/plugin-transform-react-jsx-source" "^7.12.1" + "@babel/plugin-transform-react-pure-annotations" "^7.12.1" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.5": +"@babel/preset-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.12.1.tgz#86480b483bb97f75036e8864fe404cc782cc311b" + integrity sha512-hNK/DhmoJPsksdHuI/RVrcEws7GN5eamhi28JkO52MqIxU8Z0QpmiSOQxZHWOHV7I3P4UjHV97ay4TcamMA6Kw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typescript" "^7.12.1" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3": version "7.7.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.5.tgz#4b087f183f5d83647744d4157f66199081d17a00" dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" + integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.7.2", "@babel/runtime@^7.9.6": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.4.tgz#a6724f1a6b8d2f6ea5236dbfe58c7d7ea9c5eb99" @@ -812,13 +987,20 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/template@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" +"@babel/traverse@^7.10.4", "@babel/traverse@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.1.tgz#941395e0c5cc86d5d3e75caa095d3924526f0c1e" + integrity sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.4" - "@babel/types" "^7.7.4" + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.1" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.12.1" + "@babel/types" "^7.12.1" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" "@babel/traverse@^7.4.5": version "7.10.4" @@ -835,20 +1017,6 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.7.4": - version "7.7.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.4" - "@babel/helper-function-name" "^7.7.4" - "@babel/helper-split-export-declaration" "^7.7.4" - "@babel/parser" "^7.7.4" - "@babel/types" "^7.7.4" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - "@babel/types@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.4.tgz#369517188352e18219981efd156bfdb199fff1ee" @@ -858,6 +1026,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.12.1", "@babel/types@^7.4.4": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.1.tgz#e109d9ab99a8de735be287ee3d6a9947a190c4ae" + integrity sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@babel/types@^7.7.4": version "7.7.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" @@ -1000,10 +1177,15 @@ version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" -"@types/node@*", "@types/node@^12.6.9": +"@types/node@*": version "12.6.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.9.tgz#ffeee23afdc19ab16e979338e7b536fdebbbaeaf" +"@types/node@^14.11.10": + version "14.11.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.10.tgz#8c102aba13bf5253f35146affbf8b26275069bef" + integrity sha512-yV1nWZPlMFpoXyoknm4S56y2nlTAuFYaJuQtYRAOU7xA/FJ9RY0Xm7QOkaYMMmr8ESdHIuUb6oQgR/0+2NqlyA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -1625,9 +1807,10 @@ babel-loader@^8.0.6: mkdirp "^0.5.1" pify "^4.0.1" -babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" @@ -1846,7 +2029,7 @@ browserslist@^4.12.0: escalade "^3.0.1" node-releases "^1.1.58" -browserslist@^4.6.0, browserslist@^4.7.2, browserslist@^4.8.0: +browserslist@^4.7.2: version "4.8.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289" dependencies: @@ -1854,6 +2037,16 @@ browserslist@^4.6.0, browserslist@^4.7.2, browserslist@^4.8.0: electron-to-chromium "^1.3.322" node-releases "^1.1.42" +browserslist@^4.8.5: + version "4.14.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.5.tgz#1c751461a102ddc60e40993639b709be7f2c4015" + integrity sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA== + dependencies: + caniuse-lite "^1.0.30001135" + electron-to-chromium "^1.3.571" + escalade "^3.1.0" + node-releases "^1.1.61" + buffer-from@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" @@ -1968,6 +2161,11 @@ caniuse-lite@^1.0.30001088: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001093.tgz#833e80f64b1a0455cbceed2a4a3baf19e4abd312" integrity sha512-0+ODNoOjtWD5eS9aaIpf4K0gQqZfILNY4WSNuYzeT1sXni+lMrrVjc0odEobJt6wrODofDZUX8XYi/5y7+xl8g== +caniuse-lite@^1.0.30001135: + version "1.0.30001148" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz#dc97c7ed918ab33bf8706ddd5e387287e015d637" + integrity sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw== + chalk@^2.0, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -2262,12 +2460,13 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" -core-js-compat@^3.4.7: - version "3.4.7" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.4.7.tgz#39f8080b1d92a524d6d90505c42b9c5c1eb90611" +core-js-compat@^3.6.2: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" + integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== dependencies: - browserslist "^4.8.0" - semver "^6.3.0" + browserslist "^4.8.5" + semver "7.0.0" core-js@^2.4.0: version "2.6.11" @@ -2705,6 +2904,11 @@ electron-to-chromium@^1.3.483: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.487.tgz#8075e6ea33ee2e79a2dfb2a2467033f014017258" integrity sha512-m4QS3IDShxauFfYFpnEzRCcUI55oKB9acEnHCuY/hSCZMz9Pz2KJj+UBnGHxRxS/mS1aphqOQ5wI6gc3yDZ7ew== +electron-to-chromium@^1.3.571: + version "1.3.582" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.582.tgz#1adfac5affce84d85b3d7b3dfbc4ade293a6ffc4" + integrity sha512-0nCJ7cSqnkMC+kUuPs0YgklFHraWGl/xHqtZWWtOeVtyi+YqkoAOMGuZQad43DscXCQI/yizcTa3u6B5r+BLww== + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -2807,6 +3011,11 @@ escalade@^3.0.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.1.tgz#52568a77443f6927cd0ab9c73129137533c965ed" integrity sha512-DR6NO3h9niOT+MZs7bjxlj2a1k+POu5RN8CLTPX2+i78bRi9eLe7+0zXgUHMnGXWybYcL61E9hGhPKqedy8tQA== +escalade@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -3024,7 +3233,7 @@ estraverse@^5.1.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== -esutils@^2.0.0, esutils@^2.0.2: +esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -3449,6 +3658,11 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -3934,7 +4148,7 @@ interpret@^1.4.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -4196,10 +4410,6 @@ jquery@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" -js-levenshtein@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4241,12 +4451,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - dependencies: - minimist "^1.2.0" - json5@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" @@ -4406,6 +4610,11 @@ lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + loglevel@^1.6.8: version "1.6.8" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" @@ -4823,6 +5032,11 @@ node-releases@^1.1.58: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.58.tgz#8ee20eef30fa60e52755fcc0942def5a734fe935" integrity sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg== +node-releases@^1.1.61: + version "1.1.63" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.63.tgz#db6dbb388544c31e888216304e8fd170efee3ff5" + integrity sha512-ukW3iCfQaoxJkSPN+iK7KznTeqDGVJatAEuXsJERYHa9tn/KaT5lBdIyxQjLEVTzSkyjJEuQ17/vaEjrOauDkg== + nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -5401,10 +5615,6 @@ pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" -private@^0.1.6: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" @@ -5796,9 +6006,10 @@ redux@^4.0.5: loose-envify "^1.4.0" symbol-observable "^1.2.0" -regenerate-unicode-properties@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== dependencies: regenerate "^1.4.0" @@ -5820,11 +6031,12 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== -regenerator-transform@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== dependencies: - private "^0.1.6" + "@babel/runtime" "^7.8.4" regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" @@ -5854,24 +6066,27 @@ regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== -regexpu-core@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" +regexpu-core@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" + integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== dependencies: regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - regjsgen "^0.5.0" - regjsparser "^0.6.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" + unicode-match-property-value-ecmascript "^1.2.0" -regjsgen@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" +regjsgen@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== -regjsparser@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== dependencies: jsesc "~0.5.0" @@ -6057,6 +6272,11 @@ selfsigned@^1.10.7: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -6912,9 +7132,10 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" -unicode-match-property-value-ecmascript@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== unicode-property-aliases-ecmascript@^1.0.4: version "1.0.4" From ebe3375897eae9787c10470c2c822fbe47827353 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sat, 17 Oct 2020 14:25:44 -0600 Subject: [PATCH 26/77] Fix white highlight hiding console text (fixes #2541) --- resources/scripts/components/server/Console.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index e3720ab69..1cc919e17 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -11,9 +11,11 @@ import tw from 'twin.macro'; import 'xterm/css/xterm.css'; import useEventListener from '@/plugins/useEventListener'; import { debounce } from 'debounce'; +// @ts-ignore +import tailwindConfig from '../../../../tailwind.config.js'; const theme = { - background: 'transparent', + background: tailwindConfig.theme.colors.black, cursor: 'transparent', black: '#000000', red: '#E54B4B', From c52c5d6736022f25e60a67989f9aad9875d1836b Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sat, 17 Oct 2020 14:28:02 -0600 Subject: [PATCH 27/77] Deny certain paths for mounts --- .../Controllers/Admin/MountController.php | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/MountController.php b/app/Http/Controllers/Admin/MountController.php index 3f40e555c..d718c7371 100644 --- a/app/Http/Controllers/Admin/MountController.php +++ b/app/Http/Controllers/Admin/MountController.php @@ -105,6 +105,21 @@ class MountController extends Controller $model = (new Mount())->fill($request->validated()); $model->forceFill(['uuid' => Uuid::uuid4()->toString()]); + if (str_starts_with($model->source, '/var/lib/pterodactyl/volumes')) { + $this->alert->danger('Invalid source path: "/var/lib/pterodactyl/volumes" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts'); + } + + if (str_starts_with($model->source, '/srv/daemon-data')) { + $this->alert->danger('Invalid source path: "/srv/daemon-data" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts'); + } + + if (str_starts_with($model->target, '/home/container')) { + $this->alert->danger('Invalid target path: "/home/container" cannot be used as a target path.')->flash(); + return redirect()->route('admin.mounts'); + } + $model->saveOrFail(); $mount = $model->fresh(); @@ -128,7 +143,24 @@ class MountController extends Controller return $this->delete($mount); } - $mount->forceFill($request->validated())->save(); + $mount->forceFill($request->validated()); + + if (str_starts_with($mount->source, '/var/lib/pterodactyl/volumes')) { + $this->alert->danger('Invalid source path: "/var/lib/pterodactyl/volumes" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts.view', $mount->id); + } + + if (str_starts_with($mount->source, '/srv/daemon-data')) { + $this->alert->danger('Invalid source path: "/srv/daemon-data" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts.view', $mount->id); + } + + if (str_starts_with($mount->target, '/home/container')) { + $this->alert->danger('Invalid target path: "/home/container" cannot be used as a target path.')->flash(); + return redirect()->route('admin.mounts.view', $mount->id); + } + + $mount->save(); $this->alert->success('Mount was updated successfully.')->flash(); From f7520b721be068b574f2d6dd3c8733e655ac0504 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sat, 17 Oct 2020 14:29:29 -0600 Subject: [PATCH 28/77] Deny /etc/pterodactyl as a source path for mounts --- app/Http/Controllers/Admin/MountController.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Http/Controllers/Admin/MountController.php b/app/Http/Controllers/Admin/MountController.php index d718c7371..1985f9396 100644 --- a/app/Http/Controllers/Admin/MountController.php +++ b/app/Http/Controllers/Admin/MountController.php @@ -105,6 +105,11 @@ class MountController extends Controller $model = (new Mount())->fill($request->validated()); $model->forceFill(['uuid' => Uuid::uuid4()->toString()]); + if (str_starts_with($model->source, '/etc/pterodactyl')) { + $this->alert->danger('Invalid source path: "/etc/pterodactyl" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts'); + } + if (str_starts_with($model->source, '/var/lib/pterodactyl/volumes')) { $this->alert->danger('Invalid source path: "/var/lib/pterodactyl/volumes" cannot be used as a source path.')->flash(); return redirect()->route('admin.mounts'); @@ -145,6 +150,11 @@ class MountController extends Controller $mount->forceFill($request->validated()); + if (str_starts_with($mount->source, '/etc/pterodactyl')) { + $this->alert->danger('Invalid source path: "/etc/pterodactyl" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts.view', $mount->id); + } + if (str_starts_with($mount->source, '/var/lib/pterodactyl/volumes')) { $this->alert->danger('Invalid source path: "/var/lib/pterodactyl/volumes" cannot be used as a source path.')->flash(); return redirect()->route('admin.mounts.view', $mount->id); From 050075b8359536996a32badb2f86c54d5ef131c4 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sat, 17 Oct 2020 14:37:18 -0600 Subject: [PATCH 29/77] Cleanup code in MountController.php --- .../Controllers/Admin/MountController.php | 54 ++++++++----------- app/Models/Mount.php | 20 +++++++ 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/Admin/MountController.php b/app/Http/Controllers/Admin/MountController.php index 1985f9396..ce5dbc859 100644 --- a/app/Http/Controllers/Admin/MountController.php +++ b/app/Http/Controllers/Admin/MountController.php @@ -3,6 +3,7 @@ namespace Pterodactyl\Http\Controllers\Admin; use Ramsey\Uuid\Uuid; +use Illuminate\Support\Str; use Illuminate\Http\Request; use Pterodactyl\Models\Nest; use Pterodactyl\Models\Mount; @@ -101,28 +102,21 @@ class MountController extends Controller */ public function create(MountFormRequest $request) { - /** @var \Pterodactyl\Models\Mount $mount */ $model = (new Mount())->fill($request->validated()); $model->forceFill(['uuid' => Uuid::uuid4()->toString()]); - if (str_starts_with($model->source, '/etc/pterodactyl')) { - $this->alert->danger('Invalid source path: "/etc/pterodactyl" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts'); + foreach (Mount::$invalidSourcePaths as $path) { + if (Str::startsWith($model->source, $path)) { + $this->alert->danger('"' . $path . '" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts'); + } } - if (str_starts_with($model->source, '/var/lib/pterodactyl/volumes')) { - $this->alert->danger('Invalid source path: "/var/lib/pterodactyl/volumes" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts'); - } - - if (str_starts_with($model->source, '/srv/daemon-data')) { - $this->alert->danger('Invalid source path: "/srv/daemon-data" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts'); - } - - if (str_starts_with($model->target, '/home/container')) { - $this->alert->danger('Invalid target path: "/home/container" cannot be used as a target path.')->flash(); - return redirect()->route('admin.mounts'); + foreach (Mount::$invalidTargetPaths as $path) { + if (Str::startsWith($model->target, $path)) { + $this->alert->danger('"' . $path . '" cannot be used as a target path.')->flash(); + return redirect()->route('admin.mounts'); + } } $model->saveOrFail(); @@ -150,24 +144,18 @@ class MountController extends Controller $mount->forceFill($request->validated()); - if (str_starts_with($mount->source, '/etc/pterodactyl')) { - $this->alert->danger('Invalid source path: "/etc/pterodactyl" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts.view', $mount->id); + foreach (Mount::$invalidSourcePaths as $path) { + if (Str::startsWith($mount->source, $path)) { + $this->alert->danger('"' . $path . '" cannot be used as a source path.')->flash(); + return redirect()->route('admin.mounts.view', $mount->id); + } } - if (str_starts_with($mount->source, '/var/lib/pterodactyl/volumes')) { - $this->alert->danger('Invalid source path: "/var/lib/pterodactyl/volumes" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts.view', $mount->id); - } - - if (str_starts_with($mount->source, '/srv/daemon-data')) { - $this->alert->danger('Invalid source path: "/srv/daemon-data" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts.view', $mount->id); - } - - if (str_starts_with($mount->target, '/home/container')) { - $this->alert->danger('Invalid target path: "/home/container" cannot be used as a target path.')->flash(); - return redirect()->route('admin.mounts.view', $mount->id); + foreach (Mount::$invalidTargetPaths as $path) { + if (Str::startsWith($mount->target, $path)) { + $this->alert->danger('"' . $path . '" cannot be used as a target path.')->flash(); + return redirect()->route('admin.mounts.view', $mount->id); + } } $mount->save(); diff --git a/app/Models/Mount.php b/app/Models/Mount.php index b69c0c78d..a77181d8e 100644 --- a/app/Models/Mount.php +++ b/app/Models/Mount.php @@ -70,6 +70,26 @@ class Mount extends Model */ public $timestamps = false; + /** + * Blacklisted source paths + * + * @var string[] + */ + public static $invalidSourcePaths = [ + '/etc/pterodactyl', + '/var/lib/pterodactyl/volumes', + '/srv/daemon-data', + ]; + + /** + * Blacklisted target paths + * + * @var string[] + */ + public static $invalidTargetPaths = [ + '/home/container', + ]; + /** * Returns all eggs that have this mount assigned. * From 66b9169458edbe1c7fc70c978d35e98e38f527f2 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sat, 17 Oct 2020 14:42:08 -0600 Subject: [PATCH 30/77] Cleanup code in MountController.php, again. --- .../Controllers/Admin/MountController.php | 32 +------------------ app/Models/Mount.php | 16 ++++++++++ 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/app/Http/Controllers/Admin/MountController.php b/app/Http/Controllers/Admin/MountController.php index ce5dbc859..79c729976 100644 --- a/app/Http/Controllers/Admin/MountController.php +++ b/app/Http/Controllers/Admin/MountController.php @@ -105,20 +105,6 @@ class MountController extends Controller $model = (new Mount())->fill($request->validated()); $model->forceFill(['uuid' => Uuid::uuid4()->toString()]); - foreach (Mount::$invalidSourcePaths as $path) { - if (Str::startsWith($model->source, $path)) { - $this->alert->danger('"' . $path . '" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts'); - } - } - - foreach (Mount::$invalidTargetPaths as $path) { - if (Str::startsWith($model->target, $path)) { - $this->alert->danger('"' . $path . '" cannot be used as a target path.')->flash(); - return redirect()->route('admin.mounts'); - } - } - $model->saveOrFail(); $mount = $model->fresh(); @@ -142,23 +128,7 @@ class MountController extends Controller return $this->delete($mount); } - $mount->forceFill($request->validated()); - - foreach (Mount::$invalidSourcePaths as $path) { - if (Str::startsWith($mount->source, $path)) { - $this->alert->danger('"' . $path . '" cannot be used as a source path.')->flash(); - return redirect()->route('admin.mounts.view', $mount->id); - } - } - - foreach (Mount::$invalidTargetPaths as $path) { - if (Str::startsWith($mount->target, $path)) { - $this->alert->danger('"' . $path . '" cannot be used as a target path.')->flash(); - return redirect()->route('admin.mounts.view', $mount->id); - } - } - - $mount->save(); + $mount->forceFill($request->validated())->save(); $this->alert->success('Mount was updated successfully.')->flash(); diff --git a/app/Models/Mount.php b/app/Models/Mount.php index a77181d8e..69c47f01c 100644 --- a/app/Models/Mount.php +++ b/app/Models/Mount.php @@ -2,6 +2,8 @@ namespace Pterodactyl\Models; +use Illuminate\Validation\Rules\NotIn; + /** * @property int $id * @property string $uuid @@ -63,6 +65,20 @@ class Mount extends Model 'user_mountable' => 'sometimes|boolean', ]; + /** + * Implement language verification by overriding Eloquence's gather + * rules function. + */ + public static function getRules() + { + $rules = parent::getRules(); + + $rules['source'][] = new NotIn(Mount::$invalidSourcePaths); + $rules['target'][] = new NotIn(Mount::$invalidSourcePaths); + + return $rules; + } + /** * Disable timestamps on this model. * From 8ba291afb2c3f517cbfd7bc67f79202fd23b92ba Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sat, 17 Oct 2020 14:43:07 -0600 Subject: [PATCH 31/77] Fix Mount.php validation rules --- app/Models/Mount.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Mount.php b/app/Models/Mount.php index 69c47f01c..ee9879b19 100644 --- a/app/Models/Mount.php +++ b/app/Models/Mount.php @@ -74,7 +74,7 @@ class Mount extends Model $rules = parent::getRules(); $rules['source'][] = new NotIn(Mount::$invalidSourcePaths); - $rules['target'][] = new NotIn(Mount::$invalidSourcePaths); + $rules['target'][] = new NotIn(Mount::$invalidTargetPaths); return $rules; } From 820d8f75605d699da9fea4ed88d4b9a079d9d4a1 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 13:43:22 -0700 Subject: [PATCH 32/77] Better logic for using theme values --- package.json | 4 +- .../scripts/components/server/Console.tsx | 8 ++-- yarn.lock | 44 +++++++++++++------ 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index f7c7b8b81..29caf1f17 100644 --- a/package.json +++ b/package.json @@ -93,9 +93,9 @@ "source-map-loader": "^1.0.1", "style-loader": "^1.2.1", "svg-url-loader": "^6.0.0", - "tailwindcss": "^1.4.6", + "tailwindcss": "^1.9.4", "terser-webpack-plugin": "^3.0.6", - "twin.macro": "^1.4.1", + "twin.macro": "^1.10.0", "typescript": "^3.9.6", "typescript-plugin-tw-template": "^2.0.1", "webpack": "^4.43.0", diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 1cc919e17..f7ab5d4f9 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -7,17 +7,15 @@ import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import { ServerContext } from '@/state/server'; import styled from 'styled-components/macro'; import { usePermissions } from '@/plugins/usePermissions'; -import tw from 'twin.macro'; +import tw, { theme } from 'twin.macro'; import 'xterm/css/xterm.css'; import useEventListener from '@/plugins/useEventListener'; import { debounce } from 'debounce'; -// @ts-ignore -import tailwindConfig from '../../../../tailwind.config.js'; const theme = { - background: tailwindConfig.theme.colors.black, + background: theme`colors.black`, cursor: 'transparent', - black: '#000000', + black: theme`colors.black`, red: '#E54B4B', green: '#9ECE58', yellow: '#FAED70', diff --git a/yarn.lock b/yarn.lock index d25cfe4f8..897bad166 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2174,7 +2174,7 @@ chalk@^2.0, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +"chalk@^3.0.0 || ^4.0.0", chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== @@ -3937,6 +3937,11 @@ html-parse-stringify2@2.0.1: dependencies: void-elements "^2.0.1" +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -4610,7 +4615,7 @@ lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" -lodash@^4.17.19: +lodash@^4.17.19, lodash@^4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -5119,6 +5124,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" + integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== + object-inspect@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" @@ -5581,6 +5591,11 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" +postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + postcss@7.0.32, postcss@^7.0.27: version "7.0.32" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" @@ -6840,27 +6855,30 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tailwindcss@^1.4.4, tailwindcss@^1.4.6: - version "1.4.6" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.4.6.tgz#17b37166ccda08d7e7f9ca995ea48ce1e0089700" - integrity sha512-qV0qInUq1FWih39Bc5CWECdgObSzRrbjGD4ke4kAPSIq6WXrPhv0wwOcUWJgJ66ltT9j+XnSRYikG8WNRU/fTQ== +tailwindcss@^1.8.8, tailwindcss@^1.9.4: + version "1.9.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.9.4.tgz#5ae8ff84bc8234df22ba5f2c7feafb64bb14da55" + integrity sha512-CVeP4J1pDluBM/AF11JPku9Cx+VwQ6MbOcnlobnWVVZnq+xku8sa+XXmYzy/GvE08qD8w+OmpSdN21ZFPoVDRg== dependencies: "@fullhuman/postcss-purgecss" "^2.1.2" autoprefixer "^9.4.5" browserslist "^4.12.0" bytes "^3.0.0" - chalk "^4.0.0" + chalk "^3.0.0 || ^4.0.0" color "^3.1.2" detective "^5.2.0" fs-extra "^8.0.0" - lodash "^4.17.15" + html-tags "^3.1.0" + lodash "^4.17.20" node-emoji "^1.8.1" normalize.css "^8.0.1" + object-hash "^2.0.3" postcss "^7.0.11" postcss-functions "^3.0.0" postcss-js "^2.0.0" postcss-nested "^4.1.1" postcss-selector-parser "^6.0.0" + postcss-value-parser "^4.1.0" pretty-hrtime "^1.0.3" reduce-css-calc "^2.1.6" resolve "^1.14.2" @@ -7063,10 +7081,10 @@ tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" -twin.macro@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/twin.macro/-/twin.macro-1.4.1.tgz#48de95e52c34d068486610889a68d44c766bac1a" - integrity sha512-/hUXvyQepce26FvgfZmMl7IEgwxKy57te99FuECxeRUVjrX31A1F27lt+SSrtFNivoCN4Dk7FimUx5jqjth2jQ== +twin.macro@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/twin.macro/-/twin.macro-1.10.0.tgz#b13d0934457d4cae54f1504a3a2ed744ed603960" + integrity sha512-+K9xvBvlx7iQ+CRatqNO/3VVV2+D+rbxexViKFtkjlICd+7A9hp5/8IOQ3SUPTQp80Ouist3Zcs/89quSLaoZg== dependencies: "@babel/parser" "^7.10.2" babel-plugin-macros "^2.8.0" @@ -7077,7 +7095,7 @@ twin.macro@^1.4.1: dset "^2.0.1" lodash.merge "^4.6.2" string-similarity "^4.0.1" - tailwindcss "^1.4.4" + tailwindcss "^1.8.8" timsort "^0.3.0" type-check@^0.4.0, type-check@~0.4.0: From 0260efc966570d0cc5a65f1c90d46a1160f5bfca Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 13:47:21 -0700 Subject: [PATCH 33/77] Fix some PHPstorm nightmare --- .eslintignore | 2 ++ resources/scripts/.eslintrc.yml => .eslintrc.yml | 0 2 files changed, 2 insertions(+) create mode 100644 .eslintignore rename resources/scripts/.eslintrc.yml => .eslintrc.yml (100%) diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..fff6b3500 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +public +resources/views diff --git a/resources/scripts/.eslintrc.yml b/.eslintrc.yml similarity index 100% rename from resources/scripts/.eslintrc.yml rename to .eslintrc.yml From 35f24e7f226fc7640c2aa6e41ed88dc1d7b6dfa6 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 13:54:34 -0700 Subject: [PATCH 34/77] Fix theme stuff --- resources/scripts/components/server/Console.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index f7ab5d4f9..e978f7998 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -7,15 +7,15 @@ import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import { ServerContext } from '@/state/server'; import styled from 'styled-components/macro'; import { usePermissions } from '@/plugins/usePermissions'; -import tw, { theme } from 'twin.macro'; +import tw, { theme as th } from 'twin.macro'; import 'xterm/css/xterm.css'; import useEventListener from '@/plugins/useEventListener'; import { debounce } from 'debounce'; const theme = { - background: theme`colors.black`, + background: th`colors.black`.toString(), cursor: 'transparent', - black: theme`colors.black`, + black: th`colors.black`.toString(), red: '#E54B4B', green: '#9ECE58', yellow: '#FAED70', From 5763493c6c4f1c91d090f6cd17fe7d528bb57120 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 14:23:00 -0700 Subject: [PATCH 35/77] Allow setting the backup limit via the API; closes #2535 --- .../Api/Application/Servers/StoreServerRequest.php | 2 ++ .../Servers/UpdateServerBuildConfigurationRequest.php | 7 +++++-- app/Models/Server.php | 2 +- app/Services/Servers/BuildModificationService.php | 6 +++--- .../Services/Servers/ServerCreationServiceTest.php | 6 +++--- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php index 15780d695..5e4f9c200 100644 --- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php @@ -55,6 +55,7 @@ class StoreServerRequest extends ApplicationApiRequest 'feature_limits' => 'required|array', 'feature_limits.databases' => $rules['database_limit'], 'feature_limits.allocations' => $rules['allocation_limit'], + 'feature_limits.backups' => $rules['backup_limit'], // Placeholders for rules added in withValidator() function. 'allocation.default' => '', @@ -102,6 +103,7 @@ class StoreServerRequest extends ApplicationApiRequest 'start_on_completion' => array_get($data, 'start_on_completion', false), 'database_limit' => array_get($data, 'feature_limits.databases'), 'allocation_limit' => array_get($data, 'feature_limits.allocations'), + 'backup_limit' => array_get($data, 'feature_limits.backups'), ]; } diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php index 3f0e1c8ca..a387634c1 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerBuildConfigurationRequest.php @@ -47,6 +47,7 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest 'feature_limits' => 'required|array', 'feature_limits.databases' => $rules['database_limit'], 'feature_limits.allocations' => $rules['allocation_limit'], + 'feature_limits.backups' => $rules['backup_limit'], ]; } @@ -60,8 +61,9 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest $data = parent::validated(); $data['allocation_id'] = $data['allocation']; - $data['database_limit'] = $data['feature_limits']['databases']; - $data['allocation_limit'] = $data['feature_limits']['allocations']; + $data['database_limit'] = $data['feature_limits']['databases'] ?? null; + $data['allocation_limit'] = $data['feature_limits']['allocations'] ?? null; + $data['backup_limit'] = $data['feature_limits']['backups'] ?? null; unset($data['allocation'], $data['feature_limits']); // Adjust the limits field to match what is expected by the model. @@ -90,6 +92,7 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest 'remove_allocations.*' => 'allocation to remove', 'feature_limits.databases' => 'Database Limit', 'feature_limits.allocations' => 'Allocation Limit', + 'feature_limits.backups' => 'Backup Limit', ]; } diff --git a/app/Models/Server.php b/app/Models/Server.php index aa4a39f06..60a5de439 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -122,7 +122,7 @@ class Server extends Model 'installed' => 'in:0,1,2', 'database_limit' => 'present|nullable|integer|min:0', 'allocation_limit' => 'sometimes|nullable|integer|min:0', - 'backup_limit' => 'present|integer|min:0', + 'backup_limit' => 'present|nullable|integer|min:0', ]; /** diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php index 86ff5bb69..e56d8f9b4 100644 --- a/app/Services/Servers/BuildModificationService.php +++ b/app/Services/Servers/BuildModificationService.php @@ -101,9 +101,9 @@ class BuildModificationService 'threads' => array_get($data, 'threads'), 'disk' => array_get($data, 'disk'), 'allocation_id' => array_get($data, 'allocation_id'), - 'database_limit' => array_get($data, 'database_limit', 0), - 'allocation_limit' => array_get($data, 'allocation_limit', 0), - 'backup_limit' => array_get($data, 'backup_limit', 0), + 'database_limit' => array_get($data, 'database_limit', 0) ?? null, + 'allocation_limit' => array_get($data, 'allocation_limit', 0) ?? null, + 'backup_limit' => array_get($data, 'backup_limit', 0) ?? null, ]); $updateData = $this->structureService->handle($server); diff --git a/tests/Integration/Services/Servers/ServerCreationServiceTest.php b/tests/Integration/Services/Servers/ServerCreationServiceTest.php index 7c7126682..daf198f22 100644 --- a/tests/Integration/Services/Servers/ServerCreationServiceTest.php +++ b/tests/Integration/Services/Servers/ServerCreationServiceTest.php @@ -144,9 +144,9 @@ class ServerCreationServiceTest extends IntegrationTestCase $this->assertFalse($response->suspended); $this->assertTrue($response->oom_disabled); - $this->assertEmpty($response->database_limit); - $this->assertEmpty($response->allocation_limit); - $this->assertEmpty($response->backup_limit); + $this->assertSame(0, $response->database_limit); + $this->assertSame(0, $response->allocation_limit); + $this->assertSame(0, $response->backup_limit); } /** From 527ba1adc46335c94e798574ad4df562edfd3185 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 14:30:54 -0700 Subject: [PATCH 36/77] Fix recaptcha not resetting on login fail; closes #2397 --- .../auth/ForgotPasswordContainer.tsx | 15 +++++++++++-- .../components/auth/LoginContainer.tsx | 21 +++++++++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index f82b76e20..8b75511d0 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -30,7 +30,13 @@ export default () => { // If there is no token in the state yet, request the token and then abort this submit request // since it will be re-submitted when the recaptcha data is returned by the component. if (recaptchaEnabled && !token) { - ref.current!.execute().catch(error => console.error(error)); + ref.current!.execute().catch(error => { + console.error(error); + + setSubmitting(false); + addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); + }); + return; } @@ -43,7 +49,12 @@ export default () => { console.error(error); addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); }) - .then(() => setSubmitting(false)); + .then(() => { + setToken(''); + if (ref.current) ref.current.reset(); + + setSubmitting(false); + }); }; return ( diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index 35b2ba887..d74ada61f 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -29,7 +29,13 @@ const LoginContainer = ({ history }: RouteComponentProps) => { // If there is no token in the state yet, request the token and then abort this submit request // since it will be re-submitted when the recaptcha data is returned by the component. if (recaptchaEnabled && !token) { - ref.current!.execute().catch(error => console.error(error)); + ref.current!.execute().catch(error => { + console.error(error); + + setSubmitting(false); + clearAndAddHttpError({ error }); + }); + return; } @@ -46,6 +52,9 @@ const LoginContainer = ({ history }: RouteComponentProps) => { .catch(error => { console.error(error); + setToken(''); + if (ref.current) ref.current.reset(); + setSubmitting(false); clearAndAddHttpError({ error }); }); @@ -63,23 +72,23 @@ const LoginContainer = ({ history }: RouteComponentProps) => { {({ isSubmitting, setSubmitting, submitForm }) => (
-
From cbbe5b6fa9cd363788c21038b2b02217f0413355 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 14:45:33 -0700 Subject: [PATCH 37/77] Reset error messages between login screens; ref #2455 --- .../scripts/components/auth/ForgotPasswordContainer.tsx | 6 +++++- resources/scripts/components/auth/LoginContainer.tsx | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index 8b75511d0..57f91f4c1 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Link } from 'react-router-dom'; import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail'; import { httpErrorToHuman } from '@/api/http'; @@ -24,6 +24,10 @@ export default () => { const { clearFlashes, addFlash } = useFlash(); const { enabled: recaptchaEnabled, siteKey } = useStoreState(state => state.settings.data!.recaptcha); + useEffect(() => { + clearFlashes(); + }, []); + const handleSubmission = ({ email }: Values, { setSubmitting, resetForm }: FormikHelpers) => { clearFlashes(); diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index d74ada61f..8157111ee 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Link, RouteComponentProps } from 'react-router-dom'; import login from '@/api/auth/login'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; @@ -23,6 +23,10 @@ const LoginContainer = ({ history }: RouteComponentProps) => { const { clearFlashes, clearAndAddHttpError } = useFlash(); const { enabled: recaptchaEnabled, siteKey } = useStoreState(state => state.settings.data!.recaptcha); + useEffect(() => { + clearFlashes(); + }, []); + const onSubmit = (values: Values, { setSubmitting }: FormikHelpers) => { clearFlashes(); From c370e08f651a33085fef29bf001b149c4bb913b0 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 17 Oct 2020 14:46:10 -0700 Subject: [PATCH 38/77] [security] add login throttling to the 2FA verification endpoint --- .../Auth/LoginCheckpointController.php | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/Auth/LoginCheckpointController.php b/app/Http/Controllers/Auth/LoginCheckpointController.php index c44f18a81..70d83a323 100644 --- a/app/Http/Controllers/Auth/LoginCheckpointController.php +++ b/app/Http/Controllers/Auth/LoginCheckpointController.php @@ -2,11 +2,13 @@ namespace Pterodactyl\Http\Controllers\Auth; +use Pterodactyl\Models\User; use Illuminate\Auth\AuthManager; use Illuminate\Http\JsonResponse; use PragmaRX\Google2FA\Google2FA; use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Encryption\Encrypter; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest; use Illuminate\Contracts\Cache\Repository as CacheRepository; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; @@ -80,29 +82,31 @@ class LoginCheckpointController extends AbstractLoginController * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException - * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Exception + * @throws \Illuminate\Validation\ValidationException */ public function __invoke(LoginCheckpointRequest $request): JsonResponse { - $token = $request->input('confirmation_token'); - $recoveryToken = $request->input('recovery_token'); - - try { - /** @var \Pterodactyl\Models\User $user */ - $user = $this->repository->find($this->cache->get($token, 0)); - } catch (RecordNotFoundException $exception) { - return $this->sendFailedLoginResponse($request, null, 'The authentication token provided has expired, please refresh the page and try again.'); + if ($this->hasTooManyLoginAttempts($request)) { + $this->sendLockoutResponse($request); } - // If we got a recovery token try to find one that matches for the user and then continue - // through the process (and delete the token). - if (! is_null($recoveryToken)) { - foreach ($user->recoveryTokens as $token) { - if (password_verify($recoveryToken, $token->token)) { - $this->recoveryTokenRepository->delete($token->id); + $token = $request->input('confirmation_token'); + try { + /** @var \Pterodactyl\Models\User $user */ + $user = User::query()->findOrFail($this->cache->get($token, 0)); + } catch (ModelNotFoundException $exception) { + $this->incrementLoginAttempts($request); - return $this->sendLoginResponse($user, $request); - } + return $this->sendFailedLoginResponse( + $request, null, 'The authentication token provided has expired, please refresh the page and try again.' + ); + } + + // Recovery tokens go through a slightly different pathway for usage. + if (! is_null($recoveryToken = $request->input('recovery_token'))) { + if ($this->isValidRecoveryToken($user, $recoveryToken)) { + return $this->sendLoginResponse($user, $request); } } else { $decrypted = $this->encrypter->decrypt($user->totp_secret); @@ -114,6 +118,31 @@ class LoginCheckpointController extends AbstractLoginController } } + $this->incrementLoginAttempts($request); + return $this->sendFailedLoginResponse($request, $user, ! empty($recoveryToken) ? 'The recovery token provided is not valid.' : null); } + + /** + * Determines if a given recovery token is valid for the user account. If we find a matching token + * it will be deleted from the database. + * + * @param \Pterodactyl\Models\User $user + * @param string $value + * @return bool + * + * @throws \Exception + */ + protected function isValidRecoveryToken(User $user, string $value) + { + foreach ($user->recoveryTokens as $token) { + if (password_verify($value, $token->token)) { + $token->delete(); + + return true; + } + } + + return false; + } } From f859d37b251d4434c26f52700ca1420d91dd5788 Mon Sep 17 00:00:00 2001 From: Ward Pieters Date: Sun, 18 Oct 2020 00:02:46 +0200 Subject: [PATCH 39/77] fix: duplicate 2FA error messages (https://github.com/pterodactyl/panel/issues/2455) --- .../components/auth/LoginCheckpointContainer.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx index f45073281..4c3dff599 100644 --- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx +++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import { Link, RouteComponentProps } from 'react-router-dom'; import loginCheckpoint from '@/api/auth/loginCheckpoint'; -import { httpErrorToHuman } from '@/api/http'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; import { ActionCreator } from 'easy-peasy'; import { StaticContext } from 'react-router'; @@ -20,8 +19,7 @@ interface Values { type OwnProps = RouteComponentProps, StaticContext, { token?: string }> type Props = OwnProps & { - addError: ActionCreator; - clearFlashes: ActionCreator; + clearAndAddHttpError: ActionCreator; } const LoginCheckpointContainer = () => { @@ -79,9 +77,7 @@ const LoginCheckpointContainer = () => { }; const EnhancedForm = withFormik({ - handleSubmit: ({ code, recoveryCode }, { setSubmitting, props: { addError, clearFlashes, location } }) => { - clearFlashes(); - + handleSubmit: ({ code, recoveryCode }, { setSubmitting, props: { clearAndAddHttpError, location } }) => { loginCheckpoint(location.state?.token || '', code, recoveryCode) .then(response => { if (response.complete) { @@ -95,7 +91,7 @@ const EnhancedForm = withFormik({ .catch(error => { console.error(error); setSubmitting(false); - addError({ message: httpErrorToHuman(error) }); + clearAndAddHttpError({ error: error }); }); }, @@ -106,7 +102,7 @@ const EnhancedForm = withFormik({ })(LoginCheckpointContainer); export default ({ history, location, ...props }: OwnProps) => { - const { addError, clearFlashes } = useFlash(); + const { clearAndAddHttpError } = useFlash(); if (!location.state?.token) { history.replace('/auth/login'); @@ -115,8 +111,7 @@ export default ({ history, location, ...props }: OwnProps) => { } return Date: Sun, 18 Oct 2020 00:42:52 +0200 Subject: [PATCH 40/77] fix: duplicate disable 2FA error messages --- .../components/dashboard/forms/DisableTwoFactorModal.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx index 170cd01d3..d4f986942 100644 --- a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx @@ -7,7 +7,6 @@ import { object, string } from 'yup'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationStore } from '@/state'; import disableAccountTwoFactor from '@/api/account/disableAccountTwoFactor'; -import { httpErrorToHuman } from '@/api/http'; import tw from 'twin.macro'; import Button from '@/components/elements/Button'; @@ -16,11 +15,10 @@ interface Values { } export default ({ ...props }: RequiredModalProps) => { - const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + const { clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes); const updateUserData = useStoreActions((actions: Actions) => actions.user.updateUserData); const submit = ({ password }: Values, { setSubmitting }: FormikHelpers) => { - clearFlashes('account:two-factor'); disableAccountTwoFactor(password) .then(() => { updateUserData({ useTotp: false }); @@ -29,7 +27,7 @@ export default ({ ...props }: RequiredModalProps) => { .catch(error => { console.error(error); - addError({ message: httpErrorToHuman(error), key: 'account:two-factor' }); + clearAndAddHttpError({ error: error, key: 'account:two-factor' }); setSubmitting(false); }); }; From 1c4ee31491212ada682a4caed01c67edfc8ac945 Mon Sep 17 00:00:00 2001 From: Ward Pieters Date: Sun, 18 Oct 2020 00:46:46 +0200 Subject: [PATCH 41/77] fix: duplicate enable 2FA error messages --- .../components/dashboard/forms/SetupTwoFactorModal.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx index 619067486..eb8e1a890 100644 --- a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx @@ -6,7 +6,6 @@ import getTwoFactorTokenUrl from '@/api/account/getTwoFactorTokenUrl'; import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationStore } from '@/state'; -import { httpErrorToHuman } from '@/api/http'; import FlashMessageRender from '@/components/FlashMessageRender'; import Field from '@/components/elements/Field'; import tw from 'twin.macro'; @@ -22,20 +21,18 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => { const [ recoveryTokens, setRecoveryTokens ] = useState([]); const updateUserData = useStoreActions((actions: Actions) => actions.user.updateUserData); - const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + const { clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes); useEffect(() => { - clearFlashes('account:two-factor'); getTwoFactorTokenUrl() .then(setToken) .catch(error => { console.error(error); - addError({ message: httpErrorToHuman(error), key: 'account:two-factor' }); + clearAndAddHttpError({ error: error, key: 'account:two-factor' }); }); }, []); const submit = ({ code }: Values, { setSubmitting }: FormikHelpers) => { - clearFlashes('account:two-factor'); enableAccountTwoFactor(code) .then(tokens => { setRecoveryTokens(tokens); @@ -43,7 +40,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => { .catch(error => { console.error(error); - addError({ message: httpErrorToHuman(error), key: 'account:two-factor' }); + clearAndAddHttpError({ error: error, key: 'account:two-factor' }); }) .then(() => setSubmitting(false)); }; From b3598b3b9874fabb853c9d5f66a9ed91f67b5b1a Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 19 Oct 2020 15:27:06 -0700 Subject: [PATCH 42/77] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2bef0557f..27feb040b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f | [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. | | [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHosting’s reliable servers and network. Easy to use, provisioned in a couple of minutes. | | [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. | +| [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. | ## Documentation * [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html) From 1f5e0c033415eaf26aea1eb1b89f773ec8a0ce02 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 19 Oct 2020 21:07:07 -0700 Subject: [PATCH 43/77] Update build modification service and cover logic with test cases closes #2553 --- .../Servers/BuildModificationService.php | 145 ++++------- .../Servers/BuildModificationServiceTest.php | 241 ++++++++++++++++++ 2 files changed, 296 insertions(+), 90 deletions(-) create mode 100644 tests/Integration/Services/Servers/BuildModificationServiceTest.php diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php index e56d8f9b4..c42264f8d 100644 --- a/app/Services/Servers/BuildModificationService.php +++ b/app/Services/Servers/BuildModificationService.php @@ -4,22 +4,14 @@ namespace Pterodactyl\Services\Servers; use Illuminate\Support\Arr; use Pterodactyl\Models\Server; -use GuzzleHttp\Exception\RequestException; +use Pterodactyl\Models\Allocation; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Exceptions\DisplayException; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Repositories\Wings\DaemonServerRepository; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class BuildModificationService { - /** - * @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface - */ - private $allocationRepository; - /** * @var \Illuminate\Database\ConnectionInterface */ @@ -30,11 +22,6 @@ class BuildModificationService */ private $daemonServerRepository; - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - /** * @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService */ @@ -43,23 +30,17 @@ class BuildModificationService /** * BuildModificationService constructor. * - * @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository * @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $structureService * @param \Illuminate\Database\ConnectionInterface $connection * @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository */ public function __construct( - AllocationRepositoryInterface $allocationRepository, ServerConfigurationStructureService $structureService, ConnectionInterface $connection, - DaemonServerRepository $daemonServerRepository, - ServerRepositoryInterface $repository + DaemonServerRepository $daemonServerRepository ) { - $this->allocationRepository = $allocationRepository; $this->daemonServerRepository = $daemonServerRepository; $this->connection = $connection; - $this->repository = $repository; $this->structureService = $structureService; } @@ -70,9 +51,8 @@ class BuildModificationService * @param array $data * @return \Pterodactyl\Models\Server * + * @throws \Throwable * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ public function handle(Server $server, array $data) { @@ -82,48 +62,35 @@ class BuildModificationService if (isset($data['allocation_id']) && $data['allocation_id'] != $server->allocation_id) { try { - $this->allocationRepository->findFirstWhere([ - ['id', '=', $data['allocation_id']], - ['server_id', '=', $server->id], - ]); - } catch (RecordNotFoundException $ex) { - throw new DisplayException(trans('admin/server.exceptions.default_allocation_not_found')); + Allocation::query()->where('id', $data['allocation_id'])->where('server_id', $server->id)->firstOrFail(); + } catch (ModelNotFoundException $ex) { + throw new DisplayException('The requested default allocation is not currently assigned to this server.'); } } - /* @var \Pterodactyl\Models\Server $server */ - $server = $this->repository->withFreshModel()->update($server->id, [ - 'oom_disabled' => array_get($data, 'oom_disabled'), - 'memory' => array_get($data, 'memory'), - 'swap' => array_get($data, 'swap'), - 'io' => array_get($data, 'io'), - 'cpu' => array_get($data, 'cpu'), - 'threads' => array_get($data, 'threads'), - 'disk' => array_get($data, 'disk'), - 'allocation_id' => array_get($data, 'allocation_id'), - 'database_limit' => array_get($data, 'database_limit', 0) ?? null, - 'allocation_limit' => array_get($data, 'allocation_limit', 0) ?? null, - 'backup_limit' => array_get($data, 'backup_limit', 0) ?? null, - ]); + // If any of these values are passed through in the data array go ahead and set + // them correctly on the server model. + $merge = Arr::only($data, ['oom_disabled', 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', 'allocation_id']); + + $server->forceFill(array_merge($merge, [ + 'database_limit' => Arr::get($data, 'database_limit', 0) ?? null, + 'allocation_limit' => Arr::get($data, 'allocation_limit', 0) ?? null, + 'backup_limit' => Arr::get($data, 'backup_limit', 0) ?? 0, + ]))->saveOrFail(); + + $server = $server->fresh(); $updateData = $this->structureService->handle($server); - try { - $this->daemonServerRepository - ->setServer($server) - ->update(Arr::only($updateData, ['build'])); + $this->daemonServerRepository->setServer($server)->update($updateData['build'] ?? []); - $this->connection->commit(); - } catch (RequestException $exception) { - throw new DaemonConnectionException($exception); - } + $this->connection->commit(); return $server; } /** - * Process the allocations being assigned in the data and ensure they - * are available for a server. + * Process the allocations being assigned in the data and ensure they are available for a server. * * @param \Pterodactyl\Models\Server $server * @param array $data @@ -132,55 +99,53 @@ class BuildModificationService */ private function processAllocations(Server $server, array &$data) { - $firstAllocationId = null; - - if (! array_key_exists('add_allocations', $data) && ! array_key_exists('remove_allocations', $data)) { + if (empty($data['add_allocations']) && empty($data['remove_allocations'])) { return; } - // Handle the addition of allocations to this server. - if (array_key_exists('add_allocations', $data) && ! empty($data['add_allocations'])) { - $unassigned = $this->allocationRepository->getUnassignedAllocationIds($server->node_id); + // Handle the addition of allocations to this server. Only assign allocations that are not currently + // assigned to a different server, and only allocations on the same node as the server. + if (! empty($data['add_allocations'])) { + $query = Allocation::query() + ->where('node_id', $server->node_id) + ->whereIn('id', $data['add_allocations']) + ->whereNull('server_id'); - $updateIds = []; - foreach ($data['add_allocations'] as $allocation) { - if (! in_array($allocation, $unassigned)) { - continue; - } + // Keep track of all the allocations we're just now adding so that we can use the first + // one to reset the default allocation to. + $freshlyAllocated = $query->pluck('id')->first(); - $firstAllocationId = $firstAllocationId ?? $allocation; - $updateIds[] = $allocation; - } - - if (! empty($updateIds)) { - $this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => $server->id]); - } + $query->update(['server_id' => $server->id]); } - // Handle removal of allocations from this server. - if (array_key_exists('remove_allocations', $data) && ! empty($data['remove_allocations'])) { - $assigned = $server->allocations->pluck('id')->toArray(); - - $updateIds = []; + if (! empty($data['remove_allocations'])) { foreach ($data['remove_allocations'] as $allocation) { - if (! in_array($allocation, $assigned)) { - continue; - } - - if ($allocation == $data['allocation_id']) { - if (is_null($firstAllocationId)) { - throw new DisplayException(trans('admin/server.exceptions.no_new_default_allocation')); + // If we are attempting to remove the default allocation for the server, see if we can reassign + // to the first provided value in add_allocations. If there is no new first allocation then we + // will throw an exception back. + if ($allocation === ($data['allocation_id'] ?? $server->allocation_id)) { + if (empty($freshlyAllocated)) { + throw new DisplayException( + 'You are attempting to delete the default allocation for this server but there is no fallback allocation to use.' + ); } - $data['allocation_id'] = $firstAllocationId; + // Update the default allocation to be the first allocation that we are creating. + $data['allocation_id'] = $freshlyAllocated; } - - $updateIds[] = $allocation; } - if (! empty($updateIds)) { - $this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => null]); - } + // Remove any of the allocations we got that are currently assigned to this server on + // this node. Also set the notes to null, otherwise when re-allocated to a new server those + // notes will be carried over. + Allocation::query()->where('node_id', $server->node_id) + ->where('server_id', $server->id) + // Only remove the allocations that we didn't also attempt to add to the server... + ->whereIn('id', array_diff($data['remove_allocations'], $data['add_allocations'] ?? [])) + ->update([ + 'notes' => null, + 'server_id' => null, + ]); } } } diff --git a/tests/Integration/Services/Servers/BuildModificationServiceTest.php b/tests/Integration/Services/Servers/BuildModificationServiceTest.php new file mode 100644 index 000000000..ab3d6160f --- /dev/null +++ b/tests/Integration/Services/Servers/BuildModificationServiceTest.php @@ -0,0 +1,241 @@ +daemonServerRepository = Mockery::mock(DaemonServerRepository::class); + $this->swap(DaemonServerRepository::class, $this->daemonServerRepository); + } + + /** + * Test that allocations can be added and removed from a server. Only the allocations on the + * current node and belonging to this server should be modified. + */ + public function testAllocationsCanBeModifiedForTheServer() + { + $server = $this->createServerModel(); + $server2 = $this->createServerModel(); + + /** @var \Pterodactyl\Models\Allocation[] $allocations */ + $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id]); + + $initialAllocationId = $server->allocation_id; + $allocations[0]->update(['server_id' => $server->id, 'notes' => 'Test notes']); + + // Some additional test allocations for the other server, not the server we are attempting + // to modify. + $allocations[2]->update(['server_id' => $server2->id]); + $allocations[3]->update(['server_id' => $server2->id]); + + $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined(); + + $response = $this->getService()->handle($server, [ + // Attempt to add one new allocation, and an allocation assigned to another server. The + // other server allocation should be ignored, and only the allocation for this server should + // be used. + 'add_allocations' => [$allocations[2]->id, $allocations[1]->id], + // Remove the default server allocation, ensuring that the new allocation passed through + // in the data becomes the default allocation. + 'remove_allocations' => [$server->allocation_id, $allocations[0]->id, $allocations[3]->id], + ]); + + $this->assertInstanceOf(Server::class, $response); + + // Only one allocation should exist for this server now. + $this->assertCount(1, $response->allocations); + $this->assertSame($allocations[1]->id, $response->allocation_id); + + // These two allocations should not have been touched. + $this->assertDatabaseHas('allocations', ['id' => $allocations[2]->id, 'server_id' => $server2->id]); + $this->assertDatabaseHas('allocations', ['id' => $allocations[3]->id, 'server_id' => $server2->id]); + + // Both of these allocations should have been removed from the server, and have had their + // notes properly reset. + $this->assertDatabaseHas('allocations', ['id' => $initialAllocationId, 'server_id' => null, 'notes' => null]); + $this->assertDatabaseHas('allocations', ['id' => $allocations[0]->id, 'server_id' => null, 'notes' => null]); + } + + /** + * Test that an exception is thrown if removing the default allocation without also assigning + * new allocations to the server. + */ + public function testExceptionIsThrownIfRemovingTheDefaultAllocation() + { + $server = $this->createServerModel(); + /** @var \Pterodactyl\Models\Allocation[] $allocations */ + $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id]); + + $allocations[0]->update(['server_id' => $server->id]); + + $this->expectException(DisplayException::class); + $this->expectExceptionMessage('You are attempting to delete the default allocation for this server but there is no fallback allocation to use.'); + + $this->getService()->handle($server, [ + 'add_allocations' => [], + 'remove_allocations' => [$server->allocation_id, $allocations[0]->id], + ]); + } + + /** + * Test that the build data for the server is properly passed along to the Wings instance so that + * the server data is updated in realtime. This test also ensures that only certain fields get updated + * for the server, and not just any arbitrary field. + */ + public function testServerBuildDataIsProperlyUpdatedOnWings() + { + $server = $this->createServerModel(); + + $this->daemonServerRepository->expects('setServer')->with(Mockery::on(function (Server $s) use ($server) { + return $s->id === $server->id; + }))->andReturnSelf(); + + $this->daemonServerRepository->expects('update')->with(Mockery::on(function ($data) { + $this->assertEquals([ + 'memory_limit' => 256, + 'swap' => 128, + 'io_weight' => 600, + 'cpu_limit' => 150, + 'threads' => '1,2', + 'disk_space' => 1024, + ], $data); + + return true; + }))->andReturnUndefined(); + + $response = $this->getService()->handle($server, [ + 'oom_disabled' => false, + 'memory' => 256, + 'swap' => 128, + 'io' => 600, + 'cpu' => 150, + 'threads' => '1,2', + 'disk' => 1024, + 'backup_limit' => null, + 'database_limit' => 10, + 'allocation_limit' => 20, + ]); + + $this->assertFalse($response->oom_disabled); + $this->assertSame(256, $response->memory); + $this->assertSame(128, $response->swap); + $this->assertSame(600, $response->io); + $this->assertSame(150, $response->cpu); + $this->assertSame('1,2', $response->threads); + $this->assertSame(1024, $response->disk); + $this->assertSame(0, $response->backup_limit); + $this->assertSame(10, $response->database_limit); + $this->assertSame(20, $response->allocation_limit); + } + + /** + * Test that no exception is thrown if we are only removing an allocation. + */ + public function testNoExceptionIsThrownIfOnlyRemovingAllocation() + { + $server = $this->createServerModel(); + /** @var \Pterodactyl\Models\Allocation[] $allocations */ + $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id, 'server_id' => $server->id]); + + $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined(); + + $this->getService()->handle($server, [ + 'remove_allocations' => [$allocation->id], + ]); + + $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]); + } + + /** + * Test that allocations in both the add and remove arrays are only added, and not removed. + * This scenario wouldn't really happen in the UI, but it is possible to perform via the API + * so we want to make sure that the logic being used doesn't break if the allocation exists + * in both arrays. + * + * We'll default to adding the allocation in this case. + */ + public function testAllocationInBothAddAndRemoveIsAdded() + { + $server = $this->createServerModel(); + /** @var \Pterodactyl\Models\Allocation[] $allocations */ + $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id]); + + $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined(); + + $this->getService()->handle($server, [ + 'add_allocations' => [$allocation->id], + 'remove_allocations' => [$allocation->id], + ]); + + $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => $server->id]); + } + + /** + * Test that using the same allocation ID multiple times in the array does not cause an error. + */ + public function testUsingSameAllocationIdMultipleTimesDoesNotError() + { + $server = $this->createServerModel(); + /** @var \Pterodactyl\Models\Allocation[] $allocations */ + $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id, 'server_id' => $server->id]); + $allocation2 = factory(Allocation::class)->create(['node_id' => $server->node_id]); + + $this->daemonServerRepository->expects('setServer->update')->andReturnUndefined(); + + $this->getService()->handle($server, [ + 'add_allocations' => [$allocation2->id, $allocation2->id], + 'remove_allocations' => [$allocation->id, $allocation->id], + ]); + + $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]); + $this->assertDatabaseHas('allocations', ['id' => $allocation2->id, 'server_id' => $server->id]); + } + + /** + * Test that any changes we made to the server or allocations are rolled back if there is an + * exception while performing any action. + */ + public function testThatUpdatesAreRolledBackIfExceptionIsEncountered() + { + $server = $this->createServerModel(); + /** @var \Pterodactyl\Models\Allocation[] $allocations */ + $allocation = factory(Allocation::class)->create(['node_id' => $server->node_id]); + + $this->daemonServerRepository->expects('setServer->update')->andThrows(new DisplayException('Test')); + + $this->expectException(DisplayException::class); + + $this->getService()->handle($server, ['add_allocations' => [$allocation->id]]); + + $this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]); + } + + /** + * @return \Pterodactyl\Services\Servers\BuildModificationService + */ + private function getService() + { + return $this->app->make(BuildModificationService::class); + } +} From 26de4493dda2a7ba65a0076f02c187450805ca80 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 19 Oct 2020 21:08:40 -0700 Subject: [PATCH 44/77] Set notes to null when assigning allocation; ref #2553 --- app/Services/Servers/BuildModificationService.php | 2 +- .../Services/Servers/BuildModificationServiceTest.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php index c42264f8d..43c863831 100644 --- a/app/Services/Servers/BuildModificationService.php +++ b/app/Services/Servers/BuildModificationService.php @@ -115,7 +115,7 @@ class BuildModificationService // one to reset the default allocation to. $freshlyAllocated = $query->pluck('id')->first(); - $query->update(['server_id' => $server->id]); + $query->update(['server_id' => $server->id, 'notes' => null]); } if (! empty($data['remove_allocations'])) { diff --git a/tests/Integration/Services/Servers/BuildModificationServiceTest.php b/tests/Integration/Services/Servers/BuildModificationServiceTest.php index ab3d6160f..1a4e81e2c 100644 --- a/tests/Integration/Services/Servers/BuildModificationServiceTest.php +++ b/tests/Integration/Services/Servers/BuildModificationServiceTest.php @@ -39,7 +39,7 @@ class BuildModificationServiceTest extends IntegrationTestCase $server2 = $this->createServerModel(); /** @var \Pterodactyl\Models\Allocation[] $allocations */ - $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id]); + $allocations = factory(Allocation::class)->times(4)->create(['node_id' => $server->node_id, 'notes' => 'Random notes']); $initialAllocationId = $server->allocation_id; $allocations[0]->update(['server_id' => $server->id, 'notes' => 'Test notes']); @@ -66,6 +66,7 @@ class BuildModificationServiceTest extends IntegrationTestCase // Only one allocation should exist for this server now. $this->assertCount(1, $response->allocations); $this->assertSame($allocations[1]->id, $response->allocation_id); + $this->assertNull($response->allocation->notes); // These two allocations should not have been touched. $this->assertDatabaseHas('allocations', ['id' => $allocations[2]->id, 'server_id' => $server2->id]); From 16422ebf7b6413fed87475c8ebbc3f76d9697ba3 Mon Sep 17 00:00:00 2001 From: parkervcp Date: Mon, 19 Oct 2020 20:27:23 -0400 Subject: [PATCH 45/77] remove unused eggs --- .../egg-terraria-server--t-shock.json | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 database/seeds/eggs/terraria/egg-terraria-server--t-shock.json diff --git a/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json b/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json deleted file mode 100644 index 9381890d1..000000000 --- a/database/seeds/eggs/terraria/egg-terraria-server--t-shock.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO", - "meta": { - "version": "PTDL_v1" - }, - "exported_at": "2018-01-21T17:01:31-06:00", - "name": "Terraria Server (TShock)", - "author": "support@pterodactyl.io", - "description": "TShock is a server modification for Terraria, written in C#, and based upon the Terraria Server API. It uses JSON for configuration management, and offers several features not present in the Terraria Server normally.", - "image": "quay.io\/pterodactyl\/core:mono", - "startup": null, - "config": { - "files": "{\"tshock\/config.json\":{\"parser\": \"json\", \"find\":{\"ServerPort\": \"{{server.build.default.port}}\", \"MaxSlots\": \"{{server.build.env.MAX_SLOTS}}\"}}}", - "startup": "{\"done\": \"Type 'help' for a list of commands\", \"userInteraction\": []}", - "logs": "{\"custom\": false, \"location\": \"ServerLog.txt\"}", - "stop": "exit" - }, - "scripts": { - "installation": { - "script": "#!\/bin\/ash\n# TShock Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add curl unzip\n\ncd \/tmp\n\ncurl -sSLO https:\/\/github.com\/NyxStudios\/TShock\/releases\/download\/v${T_VERSION}\/tshock_${T_VERSION}.zip\n\nunzip -o tshock_${T_VERSION}.zip -d \/mnt\/server", - "container": "alpine:3.9", - "entrypoint": "ash" - } - }, - "variables": [ - { - "name": "TShock Version", - "description": "Which version of TShock to install and use.", - "env_variable": "T_VERSION", - "default_value": "4.3.22", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|regex:\/^([0-9_\\.-]{5,10})$\/" - }, - { - "name": "Maximum Slots", - "description": "Total number of slots to allow on the server.", - "env_variable": "MAX_SLOTS", - "default_value": "20", - "user_viewable": 1, - "user_editable": 0, - "rules": "required|numeric|digits_between:1,3" - } - ] -} From d522bc9150e6c2681ee3b074c628da15a8550cd0 Mon Sep 17 00:00:00 2001 From: parkervcp Date: Mon, 19 Oct 2020 20:29:03 -0400 Subject: [PATCH 46/77] update install scripts change all install scripts to use debian:buster-slim update mc install scripts update steamcmd install scripts update voice install scripts. --- .../seeds/eggs/minecraft/egg-bungeecord.json | 16 +-- .../eggs/minecraft/egg-forge-minecraft.json | 20 +-- database/seeds/eggs/minecraft/egg-paper.json | 24 ++-- .../minecraft/egg-sponge--sponge-vanilla.json | 18 +-- .../eggs/minecraft/egg-vanilla-minecraft.json | 16 +-- database/seeds/eggs/rust/egg-rust.json | 62 ++++----- .../egg-ark--survival-evolved.json | 124 +++++++++--------- ...egg-counter--strike--global-offensive.json | 14 +- .../egg-custom-source-engine-game.json | 18 +-- .../eggs/source-engine/egg-garrys-mod.json | 34 ++--- .../eggs/source-engine/egg-insurgency.json | 18 +-- .../source-engine/egg-team-fortress2.json | 18 +-- .../eggs/voice-servers/egg-mumble-server.json | 18 +-- .../voice-servers/egg-teamspeak3-server.json | 24 ++-- 14 files changed, 212 insertions(+), 212 deletions(-) diff --git a/database/seeds/eggs/minecraft/egg-bungeecord.json b/database/seeds/eggs/minecraft/egg-bungeecord.json index b0c07e506..3f28d96b2 100644 --- a/database/seeds/eggs/minecraft/egg-bungeecord.json +++ b/database/seeds/eggs/minecraft/egg-bungeecord.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-04-12T16:00:51-07:00", + "exported_at": "2020-10-19T23:22:26+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.", @@ -17,9 +17,9 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/ash\n# Bungeecord Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add curl\n\ncd \/mnt\/server\n\nif [ -z \"${BUNGEE_VERSION}\" ] || [ \"${BUNGEE_VERSION}\" == \"latest\" ]; then\n BUNGEE_VERSION=\"lastStableBuild\"\nfi\n\ncurl -o ${SERVER_JARFILE} https:\/\/ci.md-5.net\/job\/BungeeCord\/${BUNGEE_VERSION}\/artifact\/bootstrap\/target\/BungeeCord.jar", - "container": "alpine:3.9", - "entrypoint": "ash" + "script": "#!\/bin\/bash\r\n# Bungeecord Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl\r\n\r\ncd \/mnt\/server\r\n\r\nif [ -z \"${BUNGEE_VERSION}\" ] || [ \"${BUNGEE_VERSION}\" == \"latest\" ]; then\r\n BUNGEE_VERSION=\"lastStableBuild\"\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} https:\/\/ci.md-5.net\/job\/BungeeCord\/${BUNGEE_VERSION}\/artifact\/bootstrap\/target\/BungeeCord.jar", + "container": "debian:buster-slim", + "entrypoint": "bash" } }, "variables": [ @@ -28,8 +28,8 @@ "description": "The version of Bungeecord to download and use.", "env_variable": "BUNGEE_VERSION", "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|alpha_num|between:1,6" }, { @@ -37,8 +37,8 @@ "description": "The name of the Jarfile to use when running Bungeecord.", "env_variable": "SERVER_JARFILE", "default_value": "bungeecord.jar", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" } ] diff --git a/database/seeds/eggs/minecraft/egg-forge-minecraft.json b/database/seeds/eggs/minecraft/egg-forge-minecraft.json index 96b707e51..6f404d1c7 100644 --- a/database/seeds/eggs/minecraft/egg-forge-minecraft.json +++ b/database/seeds/eggs/minecraft/egg-forge-minecraft.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-05-24T12:15:13-04:00", + "exported_at": "2020-10-19T23:22:28+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.", @@ -17,7 +17,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\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\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\n\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\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" } @@ -28,8 +28,8 @@ "description": "The name of the Jarfile to use when running Forge Mod.", "env_variable": "SERVER_JARFILE", "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" }, { @@ -37,8 +37,8 @@ "description": "The version of minecraft you want to install for.\r\n\r\nLeaving latest will install the latest recommended version.", "env_variable": "MC_VERSION", "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:9" }, { @@ -46,8 +46,8 @@ "description": "The type of server jar to download from forge.\r\n\r\nValid types are \"recommended\" and \"latest\".", "env_variable": "BUILD_TYPE", "default_value": "recommended", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:20" }, { @@ -55,8 +55,8 @@ "description": "Gets an exact version.\r\n\r\nEx. 1.15.2-31.2.4\r\n\r\nOverrides MC_VERSION and BUILD_TYPE. If it fails to download the server files it will fail to install.", "env_variable": "FORGE_VERSION", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:20" } ] diff --git a/database/seeds/eggs/minecraft/egg-paper.json b/database/seeds/eggs/minecraft/egg-paper.json index 60fde38e0..e402cb50e 100644 --- a/database/seeds/eggs/minecraft/egg-paper.json +++ b/database/seeds/eggs/minecraft/egg-paper.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-08-01T04:49:37-04:00", + "exported_at": "2020-10-19T23:26:07+00:00", "name": "Paper", "author": "parker@pterodactyl.io", "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", @@ -17,9 +17,9 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/ash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache --update curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft_java\/server.properties\r\nfi", - "container": "alpine:3.9", - "entrypoint": "ash" + "script": "#!\/bin\/bash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft\/java\/server.properties\r\nfi", + "container": "debian:buster-slim", + "entrypoint": "bash" } }, "variables": [ @@ -28,8 +28,8 @@ "description": "The version of minecraft to download. \r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.", "env_variable": "MINECRAFT_VERSION", "default_value": "latest", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "nullable|string|max:20" }, { @@ -37,8 +37,8 @@ "description": "The name of the server jarfile to run the server with.", "env_variable": "SERVER_JARFILE", "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:20" }, { @@ -46,8 +46,8 @@ "description": "A URL to use to download a server.jar rather than the ones in the install script. This is not user viewable.", "env_variable": "DL_PATH", "default_value": "", - "user_viewable": 0, - "user_editable": 0, + "user_viewable": false, + "user_editable": false, "rules": "nullable|string" }, { @@ -55,8 +55,8 @@ "description": "The build number for the paper release.\r\n\r\nLeave at latest to always get the latest version. Invalid versions will default to latest.", "env_variable": "BUILD_NUMBER", "default_value": "latest", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|string|max:20" } ] diff --git a/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json b/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json index 2bbfba23a..cf1a9396d 100644 --- a/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json +++ b/database/seeds/eggs/minecraft/egg-sponge--sponge-vanilla.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2017-11-03T22:20:03-05:00", + "exported_at": "2020-10-19T23:26:54+00:00", "name": "Sponge (SpongeVanilla)", "author": "support@pterodactyl.io", "description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.", @@ -17,9 +17,9 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/ash\n# Sponge Installation Script\n#\n# Server Files: \/mnt\/server\n\napk update\napk add curl\n\ncd \/mnt\/server\n\ncurl -sSL \"https:\/\/repo.spongepowered.org\/maven\/org\/spongepowered\/spongevanilla\/${SPONGE_VERSION}\/spongevanilla-${SPONGE_VERSION}.jar\" -o ${SERVER_JARFILE}", - "container": "alpine:3.9", - "entrypoint": "ash" + "script": "#!\/bin\/bash\r\n# Sponge Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\n\r\napt update\r\napt install -y curl\r\n\r\ncd \/mnt\/server\r\n\r\ncurl -sSL \"https:\/\/repo.spongepowered.org\/maven\/org\/spongepowered\/spongevanilla\/${SPONGE_VERSION}\/spongevanilla-${SPONGE_VERSION}.jar\" -o ${SERVER_JARFILE}", + "container": "debian:buster-slim", + "entrypoint": "bash" } }, "variables": [ @@ -28,8 +28,8 @@ "description": "The version of SpongeVanilla to download and use.", "env_variable": "SPONGE_VERSION", "default_value": "1.11.2-6.1.0-BETA-21", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|regex:\/^([a-zA-Z0-9.\\-_]+)$\/" }, { @@ -37,9 +37,9 @@ "description": "The name of the Jarfile to use when running SpongeVanilla.", "env_variable": "SERVER_JARFILE", "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" } ] -} +} \ No newline at end of file diff --git a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json index f613c8150..80d697949 100644 --- a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json +++ b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-12-25T19:55:01-05:00", + "exported_at": "2020-10-19T23:28:18+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.", @@ -17,9 +17,9 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/ash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add curl --no-cache --update jq\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"", - "container": "alpine:3.10", - "entrypoint": "ash" + "script": "#!\/bin\/bash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y jq\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"", + "container": "debian:buster-slim", + "entrypoint": "bash" } }, "variables": [ @@ -28,8 +28,8 @@ "description": "The name of the server jarfile to run the server with.", "env_variable": "SERVER_JARFILE", "default_value": "server.jar", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" }, { @@ -37,8 +37,8 @@ "description": "The version of Minecraft Vanilla to install. Use \"latest\" to install the latest version.", "env_variable": "VANILLA_VERSION", "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|between:3,15" } ] diff --git a/database/seeds/eggs/rust/egg-rust.json b/database/seeds/eggs/rust/egg-rust.json index 7d794cc89..d805da08f 100644 --- a/database/seeds/eggs/rust/egg-rust.json +++ b/database/seeds/eggs/rust/egg-rust.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2018-01-21T16:58:36-06:00", + "exported_at": "2020-10-20T00:03:09+00:00", "name": "Rust", "author": "support@pterodactyl.io", "description": "The only aim in Rust is to survive. To do this you will need to overcome struggles such as hunger, thirst and cold. Build a fire. Build a shelter. Kill animals for meat. Protect yourself from other players, and kill them for meat. Create alliances with other players and form a town. Do whatever it takes to survive.", @@ -17,8 +17,8 @@ }, "scripts": { "installation": { - "script": "apt update\r\napt -y --no-install-recommends install curl unzip lib32gcc1 ca-certificates\r\ncd \/tmp\r\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\r\n\r\nmkdir -p \/mnt\/server\/steam\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steam\r\ncd \/mnt\/server\/steam\r\nchown -R root:root \/mnt\r\n\r\nexport HOME=\/mnt\/server\r\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 258550 +quit\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v \/mnt\/server\/steam\/linux32\/steamclient.so \/mnt\/server\/.steam\/sdk32\/steamclient.so", - "container": "ubuntu:16.04", + "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\nSRCDS_APPID=258550\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\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} 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": "debian:buster-slim", "entrypoint": "bash" } }, @@ -28,8 +28,8 @@ "description": "The name of your server in the public server list.", "env_variable": "HOSTNAME", "default_value": "A Rust Server", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:40" }, { @@ -37,8 +37,8 @@ "description": "Set whether you want the server to use and auto update OxideMod or not. Valid options are \"1\" for true and \"0\" for false.", "env_variable": "OXIDE", "default_value": "0", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|boolean" }, { @@ -46,8 +46,8 @@ "description": "The world file for Rust to use.", "env_variable": "LEVEL", "default_value": "Procedural Map", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:20" }, { @@ -55,8 +55,8 @@ "description": "The description under your server title. Commonly used for rules & info. Use \\n for newlines.", "env_variable": "DESCRIPTION", "default_value": "Powered by Pterodactyl", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string" }, { @@ -64,8 +64,8 @@ "description": "The URL for your server. This is what comes up when clicking the \"Visit Website\" button.", "env_variable": "SERVER_URL", "default_value": "http:\/\/pterodactyl.io", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "nullable|url" }, { @@ -73,8 +73,8 @@ "description": "The world size for a procedural map.", "env_variable": "WORLD_SIZE", "default_value": "3000", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|integer" }, { @@ -82,8 +82,8 @@ "description": "The seed for a procedural map.", "env_variable": "WORLD_SEED", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "nullable|string" }, { @@ -91,8 +91,8 @@ "description": "The maximum amount of players allowed in the server at once.", "env_variable": "MAX_PLAYERS", "default_value": "40", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|integer" }, { @@ -100,8 +100,8 @@ "description": "The header image for the top of your server listing.", "env_variable": "SERVER_IMG", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "nullable|url" }, { @@ -109,8 +109,8 @@ "description": "Port for RCON connections.", "env_variable": "RCON_PORT", "default_value": "28016", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|integer" }, { @@ -118,17 +118,17 @@ "description": "RCON access password.", "env_variable": "RCON_PASS", "default_value": "CHANGEME", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:64" }, { "name": "Save Interval", - "description": "Sets the server’s auto-save interval in seconds.", + "description": "Sets the server\u2019s auto-save interval in seconds.", "env_variable": "SAVEINTERVAL", "default_value": "60", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|integer" }, { @@ -136,9 +136,9 @@ "description": "Add additional startup parameters to the server.", "env_variable": "ADDITIONAL_ARGS", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "nullable|string" } ] -} +} \ No newline at end of file diff --git a/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json b/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json index e86a89a9a..627a384a8 100644 --- a/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json +++ b/database/seeds/eggs/source-engine/egg-ark--survival-evolved.json @@ -3,78 +3,87 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2018-10-29T20:51:32+01:00", + "exported_at": "2020-10-19T23:29:06+00:00", "name": "Ark: Survival Evolved", - "author": "support@pterodactyl.io", + "author": "dev@shepper.fr", "description": "As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! \u2014 Gamepedia: ARK", - "image": "quay.io\/pterodactyl\/core:source", - "startup": "\"cd ShooterGame\/Binaries\/Linux && .\/ShooterGameServer {{SERVER_MAP}}?listen?SessionName='{{SESSION_NAME}}'?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}?RCONPort={{RCON_PORT}}?QueryPort={{QUERY_PORT}}?RCONEnabled={{ENABLE_RCON}} -server -log\"", + "image": "quay.io\/parkervcp\/pterodactyl-images:debian_source", + "startup": "rmv() { echo -e \"stoppping server\"; rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c saveworld && rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD} -c DoExit; }; trap rmv 15; cd ShooterGame\/Binaries\/Linux && .\/ShooterGameServer {{SERVER_MAP}}?listen?SessionName=\"{{SESSION_NAME}}\"?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?RCONPort={{RCON_PORT}}?QueryPort={{QUERY_PORT}}?RCONEnabled={{ENABLE_RCON}}$( [ \"$BATTLE_EYE\" == \"0\" ] || printf %s '?-NoBattlEye' ) -server -log & until echo \"waiting for rcon connection...\"; rcon -a 127.0.0.1:${RCON_PORT} -p ${ARK_ADMIN_PASSWORD}; do sleep 5; done", "config": { "files": "{}", - "startup": "{\r\n \"done\": \"Setting breakpad minidump AppID = 346110\",\r\n \"userInteraction\": []\r\n}", + "startup": "{\r\n \"done\": \"Waiting commands for 127.0.0.1:\",\r\n \"userInteraction\": []\r\n}", "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/latest.log\"\r\n}", "stop": "^C" }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# ARK: Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt -y update\r\napt -y --no-install-recommends install curl lib32gcc1 ca-certificates\r\n\r\ncd \/tmp\r\ncurl -sSL -o steamcmd.tar.gz http:\/\/media.steampowered.com\/installer\/steamcmd_linux.tar.gz\r\n\r\nmkdir -p \/mnt\/server\/steamcmd\r\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\n\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/steamcmd\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\n\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\n\r\nexport HOME=\/mnt\/server\r\n.\/steamcmd.sh +login anonymous +force_install_dir \/mnt\/server +app_update 376030 +quit\r\n\r\nmkdir -p \/mnt\/server\/.steam\/sdk32\r\ncp -v linux32\/steamclient.so ..\/.steam\/sdk32\/steamclient.so\r\n\r\ncd \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\n\r\nln -sf ..\/..\/..\/..\/..\/Steam\/steamapps steamapps\r\n\r\ncd \/mnt\/server", - "container": "ubuntu:16.04", + "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 --no-install-suggests 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\n\r\nmkdir -p \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\ntar -xzvf steamcmd.tar.gz -C \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\n\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\r\n\r\n## create a symbolic link for loading mods\r\ncd \/mnt\/server\/Engine\/Binaries\/ThirdParty\/SteamCMD\/Linux\r\nln -sf ..\/..\/..\/..\/..\/Steam\/steamapps steamapps\r\ncd \/mnt\/server", + "container": "debian:buster-slim", "entrypoint": "bash" } }, "variables": [ - { - "name": "Server Name", - "description": "ARK server name", - "env_variable": "SESSION_NAME", - "default_value": "ARK SERVER", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:128" - }, { "name": "Server Password", "description": "If specified, players must provide this password to join the server.", "env_variable": "ARK_PASSWORD", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "nullable|alpha_dash|between:1,100" }, { "name": "Admin Password", "description": "If specified, players must provide this password (via the in-game console) to gain access to administrator commands on the server.", "env_variable": "ARK_ADMIN_PASSWORD", - "default_value": "", - "user_viewable": 1, - "user_editable": 1, - "rules": "nullable|alpha_dash|between:1,100" + "default_value": "PleaseChangeMe", + "user_viewable": true, + "user_editable": true, + "rules": "nullable|alpha_dash|between:1,100" }, { - "name": "Server Port", - "description": "ARK server port used by client.", - "env_variable": "PORT", - "default_value": "7777", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|numeric" + "name": "Server Map", + "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction, Valguero_P, Genesis", + "env_variable": "SERVER_MAP", + "default_value": "TheIsland", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:20" + }, + { + "name": "App ID", + "description": "ARK steam app id for auto updates. Leave blank to avoid auto update.", + "env_variable": "SRCDS_APPID", + "default_value": "376030", + "user_viewable": true, + "user_editable": false, + "rules": "nullable|numeric" + }, + { + "name": "Server Name", + "description": "ARK server name", + "env_variable": "SESSION_NAME", + "default_value": "A Pterodactyl Hosted ARK Server", + "user_viewable": true, + "user_editable": true, + "rules": "required|string|max:128" }, { "name": "Use Rcon", - "description": "Enable or disable rcon system. (true or false)", + "description": "Enable or disable rcon system. (true or false)\r\n\r\nDefault True for the console to work.", "env_variable": "ENABLE_RCON", - "default_value": "false", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:5" + "default_value": "True", + "user_viewable": true, + "user_editable": false, + "rules": "required|string|in:True,False" }, { "name": "Rcon Port", "description": "ARK rcon port used by rcon tools.", "env_variable": "RCON_PORT", "default_value": "27020", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|numeric" }, { @@ -82,36 +91,27 @@ "description": "ARK query port used by steam server browser and ark client server browser.", "env_variable": "QUERY_PORT", "default_value": "27015", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|numeric" }, { - "name": "Maximum Players", - "description": "Specifies the maximum number of players that can play on the server simultaneously.", - "env_variable": "SERVER_MAX_PLAYERS", - "default_value": "20", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|numeric|digits_between:1,4" + "name": "Auto-update server", + "description": "This is to enable auto-updating for servers.\r\n\r\nDefault is 0. Set to 1 to update", + "env_variable": "AUTO_UPDATE", + "default_value": "0", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" }, { - "name": "App ID", - "description": "ARK steam app id for auto updates. Leave blank to avoid auto update.", - "env_variable": "SRCDS_APPID", - "default_value": "376030", - "user_viewable": 1, - "user_editable": 0, - "rules": "nullable|numeric" - }, - { - "name": "Server Map", - "description": "Available Maps: TheIsland, TheCenter, Ragnarok, ScorchedEarth_P, Aberration_P, Extinction", - "env_variable": "SERVER_MAP", - "default_value": "TheIsland", - "user_viewable": 1, - "user_editable": 1, - "rules": "required|string|max:20" + "name": "Ballte Eye", + "description": "Enable BattleEye\r\n\r\n0 to disable\r\n1 to enable\r\n\r\ndefault=\"1\"", + "env_variable": "BATTLE_EYE", + "default_value": "1", + "user_viewable": true, + "user_editable": true, + "rules": "required|boolean" } ] -} +} \ No newline at end of file diff --git a/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json b/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json index 191aa99ed..b093aa793 100644 --- a/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json +++ b/database/seeds/eggs/source-engine/egg-counter--strike--global-offensive.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-12-08T10:52:19-05:00", + "exported_at": "2020-10-19T23:29:57+00: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.", @@ -28,8 +28,8 @@ "description": "The default map for the server.", "env_variable": "SRCDS_MAP", "default_value": "de_dust2", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|alpha_dash" }, { @@ -37,8 +37,8 @@ "description": "The Steam Account Token required for the server to be displayed publicly.", "env_variable": "STEAM_ACC", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|alpha_num|size:32" }, { @@ -46,8 +46,8 @@ "description": "Required for game to update on server restart. Do not modify this.", "env_variable": "SRCDS_APPID", "default_value": "740", - "user_viewable": 0, - "user_editable": 0, + "user_viewable": false, + "user_editable": false, "rules": "required|string|max:20" } ] diff --git a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json b/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json index aae3b3f35..fd6fe014c 100644 --- a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json +++ b/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-12-08T10:54:26-05:00", + "exported_at": "2020-10-19T23:33:52+00: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.", @@ -17,8 +17,8 @@ }, "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\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": "ubuntu:18.04", + "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\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": "debian:buster-slim", "entrypoint": "bash" } }, @@ -28,8 +28,8 @@ "description": "The ID corresponding to the game to download and run using SRCDS.", "env_variable": "SRCDS_APPID", "default_value": "", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|numeric|digits_between:1,6" }, { @@ -37,8 +37,8 @@ "description": "The name corresponding to the game to download and run using SRCDS.", "env_variable": "SRCDS_GAME", "default_value": "", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|alpha_dash|between:1,100" }, { @@ -46,8 +46,8 @@ "description": "The default map for the server.", "env_variable": "SRCDS_MAP", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|alpha_dash" } ] diff --git a/database/seeds/eggs/source-engine/egg-garrys-mod.json b/database/seeds/eggs/source-engine/egg-garrys-mod.json index 17940e8fb..d2201a6c3 100644 --- a/database/seeds/eggs/source-engine/egg-garrys-mod.json +++ b/database/seeds/eggs/source-engine/egg-garrys-mod.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-12-08T10:56:42-05:00", + "exported_at": "2020-10-19T23:34:44+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.", @@ -17,8 +17,8 @@ }, "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\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\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\n\r\n\/\/ Steam Server List Settings\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": "ubuntu:18.04", + "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\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\n\r\n\/\/ Steam Server List Settings\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": "debian:buster-slim", "entrypoint": "bash" } }, @@ -28,8 +28,8 @@ "description": "The default map for the server.", "env_variable": "SRCDS_MAP", "default_value": "gm_flatgrass", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|alpha_dash" }, { @@ -37,8 +37,8 @@ "description": "The Steam Account Token required for the server to be displayed publicly.", "env_variable": "STEAM_ACC", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "nullable|string|alpha_num|size:32" }, { @@ -46,8 +46,8 @@ "description": "Required for game to update on server restart. Do not modify this.", "env_variable": "SRCDS_APPID", "default_value": "4020", - "user_viewable": 0, - "user_editable": 0, + "user_viewable": false, + "user_editable": false, "rules": "required|string|max:20" }, { @@ -55,8 +55,8 @@ "description": "The ID of your workshop collection (the numbers at the end of the URL)", "env_variable": "WORKSHOP_ID", "default_value": "", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "nullable|integer" }, { @@ -64,8 +64,8 @@ "description": "The gamemode of your server.", "env_variable": "GAMEMODE", "default_value": "sandbox", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string" }, { @@ -73,8 +73,8 @@ "description": "The maximum amount of players allowed on your game server.", "env_variable": "MAX_PLAYERS", "default_value": "32", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|integer|max:128" }, { @@ -82,8 +82,8 @@ "description": "The tickrate defines how fast the server will update each entities location.", "env_variable": "TICKRATE", "default_value": "22", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|integer|max:100" } ] diff --git a/database/seeds/eggs/source-engine/egg-insurgency.json b/database/seeds/eggs/source-engine/egg-insurgency.json index 7f0c76be2..3103ac244 100644 --- a/database/seeds/eggs/source-engine/egg-insurgency.json +++ b/database/seeds/eggs/source-engine/egg-insurgency.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-12-08T10:57:32-05:00", + "exported_at": "2020-10-19T23:35:42+00:00", "name": "Insurgency", "author": "support@pterodactyl.io", "description": "Take to the streets for intense close quarters combat, where a team's survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.", @@ -17,8 +17,8 @@ }, "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\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": "ubuntu:18.04", + "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\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": "debian:buster-slim", "entrypoint": "bash" } }, @@ -28,8 +28,8 @@ "description": "The ID corresponding to the game to download and run using SRCDS.", "env_variable": "SRCDS_APPID", "default_value": "237410", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|regex:\/^(237410)$\/" }, { @@ -37,8 +37,8 @@ "description": "The name corresponding to the game to download and run using SRCDS.", "env_variable": "SRCDS_GAME", "default_value": "insurgency", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|regex:\/^(insurgency)$\/" }, { @@ -46,8 +46,8 @@ "description": "The default map to use when starting the server.", "env_variable": "SRCDS_MAP", "default_value": "sinjar", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|regex:\/^(\\w{1,20})$\/" } ] diff --git a/database/seeds/eggs/source-engine/egg-team-fortress2.json b/database/seeds/eggs/source-engine/egg-team-fortress2.json index 159e7bf9b..8b619642f 100644 --- a/database/seeds/eggs/source-engine/egg-team-fortress2.json +++ b/database/seeds/eggs/source-engine/egg-team-fortress2.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-12-08T10:58:48-05:00", + "exported_at": "2020-10-19T23:36:44+00:00", "name": "Team Fortress 2", "author": "support@pterodactyl.io", "description": "Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.", @@ -17,8 +17,8 @@ }, "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\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": "ubuntu:18.04", + "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\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": "debian:buster-slim", "entrypoint": "bash" } }, @@ -28,8 +28,8 @@ "description": "The ID corresponding to the game to download and run using SRCDS.", "env_variable": "SRCDS_APPID", "default_value": "232250", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|regex:\/^(232250)$\/" }, { @@ -37,8 +37,8 @@ "description": "The name corresponding to the game to download and run using SRCDS.", "env_variable": "SRCDS_GAME", "default_value": "tf", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|regex:\/^(tf)$\/" }, { @@ -46,8 +46,8 @@ "description": "The default map to use when starting the server.", "env_variable": "SRCDS_MAP", "default_value": "cp_dustbowl", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|regex:\/^(\\w{1,20})$\/" } ] diff --git a/database/seeds/eggs/voice-servers/egg-mumble-server.json b/database/seeds/eggs/voice-servers/egg-mumble-server.json index 00c87d21e..e9215ca9f 100644 --- a/database/seeds/eggs/voice-servers/egg-mumble-server.json +++ b/database/seeds/eggs/voice-servers/egg-mumble-server.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2018-01-21T17:01:44-06:00", + "exported_at": "2020-10-20T00:22:14+00:00", "name": "Mumble Server", "author": "support@pterodactyl.io", "description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.", @@ -17,9 +17,9 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/ash\n# Mumble Installation Script\n#\n# Server Files: \/mnt\/server\napk update\napk add tar curl\n\ncd \/tmp\n\ncurl -sSLO https:\/\/github.com\/mumble-voip\/mumble\/releases\/download\/${MUMBLE_VERSION}\/murmur-static_x86-${MUMBLE_VERSION}.tar.bz2\n\ntar -xjvf murmur-static_x86-${MUMBLE_VERSION}.tar.bz2\ncp -r murmur-static_x86-${MUMBLE_VERSION}\/* \/mnt\/server", - "container": "alpine:3.9", - "entrypoint": "ash" + "script": "#!\/bin\/bash\r\n# Mumble Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\nGITHUB_PACKAGE=mumble-voip\/mumble\r\nMATCH=murmur-static\r\n\r\napt update\r\napt install -y tar curl jq\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/tmp\r\n\r\nif [ -z \"${GITHUB_USER}\" ] && [ -z \"${GITHUB_OAUTH_TOKEN}\" ] ; then\r\n echo -e \"using anon api call\"\r\nelse\r\n echo -e \"user and oauth token set\"\r\n alias curl='curl -u ${GITHUB_USER}:${GITHUB_OAUTH_TOKEN} '\r\nfi\r\n\r\n## get release info and download links\r\nLATEST_JSON=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\/latest\")\r\nRELEASES=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\")\r\n\r\nif [ -z \"${VERSION}\" ] || [ \"${VERSION}\" == \"latest\" ]; then\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url | grep -m 1 -i ${MATCH})\r\nelse\r\n VERSION_CHECK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .tag_name')\r\n if [ \"${VERSION}\" == \"${VERSION_CHECK}\" ]; then\r\n DOWNLOAD_LINK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .assets[].browser_download_url' | grep -m 1 -i ${MATCH})\r\n else\r\n echo -e \"defaulting to latest release\"\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url)\r\n fi\r\nfi\r\n\r\ncurl -L ${DOWNLOAD_LINK} -o mumble-server.tar.bz2\r\n\r\ntar -xjvf mumble-server.tar.bz2\r\ncp -r murmur-static_x86-*\/* \/mnt\/server", + "container": "debian:buster-slim", + "entrypoint": "bash" } }, "variables": [ @@ -28,8 +28,8 @@ "description": "Maximum concurrent users on the mumble server.", "env_variable": "MAX_USERS", "default_value": "100", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|numeric|digits_between:1,5" }, { @@ -37,9 +37,9 @@ "description": "Version of Mumble Server to download and use.", "env_variable": "MUMBLE_VERSION", "default_value": "1.3.1", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|regex:\/^([0-9_\\.-]{5,8})$\/" } ] -} +} \ No newline at end of file diff --git a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json index d3d889bc0..e0ae85d8d 100644 --- a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json +++ b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json @@ -3,23 +3,23 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2019-07-05T11:59:29-04:00", + "exported_at": "2020-10-20T00:24:22+00:00", "name": "Teamspeak3 Server", "author": "support@pterodactyl.io", "description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.", - "image": "quay.io/parkervcp/pterodactyl-images:base_debian", - "startup": "./ts3server default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} license_accepted=1", + "image": "quay.io\/parkervcp\/pterodactyl-images:base_debian", + "startup": ".\/ts3server default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}} filetransfer_ip=0.0.0.0 filetransfer_port={{FILE_TRANSFER}} license_accepted=1", "config": { "files": "{}", "startup": "{\r\n \"done\": \"listening on 0.0.0.0:\",\r\n \"userInteraction\": []\r\n}", - "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs/ts3.log\"\r\n}", + "logs": "{\r\n \"custom\": true,\r\n \"location\": \"logs\/ts3.log\"\r\n}", "stop": "^C" }, "scripts": { "installation": { - "script": "#!/bin/ash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: /mnt/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https://teamspeak.com/versions/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd /mnt/server\r\n\r\n\r\necho -e \"getting files from http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http://files.teamspeak-services.com/releases/server/${TS_VERSION}/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1", - "container": "alpine:3.9", - "entrypoint": "ash" + "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https:\/\/teamspeak.com\/versions\/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1", + "container": "debian:buster-slim", + "entrypoint": "bash" } }, "variables": [ @@ -28,8 +28,8 @@ "description": "The version of Teamspeak 3 to use when running the server.", "env_variable": "TS_VERSION", "default_value": "latest", - "user_viewable": 1, - "user_editable": 1, + "user_viewable": true, + "user_editable": true, "rules": "required|string|max:6" }, { @@ -37,9 +37,9 @@ "description": "The Teamspeak file transfer port", "env_variable": "FILE_TRANSFER", "default_value": "30033", - "user_viewable": 1, - "user_editable": 0, + "user_viewable": true, + "user_editable": false, "rules": "required|integer|between:1,65535" } ] -} +} \ No newline at end of file From 65d04d0c051a8e0013d63fd43a347e06daf81e20 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 22 Oct 2020 20:54:58 -0700 Subject: [PATCH 47/77] Correctly handle schedule task deletion and avoid errors; closes #2534 --- .../Client/Servers/ScheduleTaskController.php | 16 +++- .../Schedules/ProcessScheduleService.php | 2 +- .../ScheduleTask/DeleteScheduleTaskTest.php | 84 +++++++++++++++++++ .../Schedules/ProcessScheduleServiceTest.php | 31 +++++++ 4 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php index 0d613b6b0..c6f8ee339 100644 --- a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php @@ -56,7 +56,8 @@ class ScheduleTaskController extends ClientApiController ); } - $lastTask = $schedule->tasks->last(); + /** @var \Pterodactyl\Models\Task|null $lastTask */ + $lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first(); /** @var \Pterodactyl\Models\Task $task */ $task = $this->repository->create([ @@ -102,13 +103,16 @@ class ScheduleTaskController extends ClientApiController } /** - * Determines if a user can delete the task for a given server. + * Delete a given task for a schedule. If there are subsequent tasks stored in the database + * for this schedule their sequence IDs are decremented properly. * * @param \Pterodactyl\Http\Requests\Api\Client\ClientApiRequest $request * @param \Pterodactyl\Models\Server $server * @param \Pterodactyl\Models\Schedule $schedule * @param \Pterodactyl\Models\Task $task * @return \Illuminate\Http\JsonResponse + * + * @throws \Exception */ public function delete(ClientApiRequest $request, Server $server, Schedule $schedule, Task $task) { @@ -120,8 +124,12 @@ class ScheduleTaskController extends ClientApiController throw new HttpForbiddenException('You do not have permission to perform this action.'); } - $this->repository->delete($task->id); + $schedule->tasks()->where('sequence_id', '>', $task->sequence_id)->update([ + 'sequence_id' => $schedule->tasks()->getConnection()->raw('(sequence_id - 1)'), + ]); - return JsonResponse::create(null, Response::HTTP_NO_CONTENT); + $task->delete(); + + return new JsonResponse(null, Response::HTTP_NO_CONTENT); } } diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php index 1f810d6f5..ec16bc36a 100644 --- a/app/Services/Schedules/ProcessScheduleService.php +++ b/app/Services/Schedules/ProcessScheduleService.php @@ -43,7 +43,7 @@ class ProcessScheduleService public function handle(Schedule $schedule, bool $now = false) { /** @var \Pterodactyl\Models\Task $task */ - $task = $schedule->tasks()->where('sequence_id', 1)->first(); + $task = $schedule->tasks()->orderBy('sequence_id', 'asc')->first(); if (is_null($task)) { throw new DisplayException( diff --git a/tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php b/tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php new file mode 100644 index 000000000..ef3e1dee4 --- /dev/null +++ b/tests/Integration/Api/Client/Server/ScheduleTask/DeleteScheduleTaskTest.php @@ -0,0 +1,84 @@ +createServerModel(); + [$user] = $this->generateTestAccount(); + + $schedule = factory(Schedule::class)->create(['server_id' => $server2->id]); + $task = factory(Task::class)->create(['schedule_id' => $schedule->id]); + + $this->actingAs($user)->deleteJson($this->link($task))->assertNotFound(); + } + + /** + * Test that an error is returned if the task and schedule in the URL do not line up + * with eachother. + */ + public function testTaskBelongingToDifferentScheduleReturnsError() + { + [$user, $server] = $this->generateTestAccount(); + + $schedule = factory(Schedule::class)->create(['server_id' => $server->id]); + $schedule2 = factory(Schedule::class)->create(['server_id' => $server->id]); + $task = factory(Task::class)->create(['schedule_id' => $schedule->id]); + + $this->actingAs($user)->deleteJson("/api/client/servers/{$server->uuid}/schedules/{$schedule2->id}/tasks/{$task->id}")->assertNotFound(); + } + + /** + * Test that a user without the required permissions returns an error. + */ + public function testUserWithoutPermissionReturnsError() + { + [$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]); + + $schedule = factory(Schedule::class)->create(['server_id' => $server->id]); + $task = factory(Task::class)->create(['schedule_id' => $schedule->id]); + + $this->actingAs($user)->deleteJson($this->link($task))->assertForbidden(); + + $user2 = factory(User::class)->create(); + + $this->actingAs($user2)->deleteJson($this->link($task))->assertNotFound(); + } + + /** + * Test that a schedule task is deleted and items with a higher sequence ID are decremented + * properly in the database. + */ + public function testScheduleTaskIsDeletedAndSubsequentTasksAreUpdated() + { + [$user, $server] = $this->generateTestAccount(); + + $schedule = factory(Schedule::class)->create(['server_id' => $server->id]); + $tasks = [ + factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]), + factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]), + factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]), + factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]), + ]; + + $response = $this->actingAs($user)->deleteJson($this->link($tasks[1])); + $response->assertStatus(Response::HTTP_NO_CONTENT); + + $this->assertDatabaseHas('tasks', ['id' => $tasks[0]->id, 'sequence_id' => 1]); + $this->assertDatabaseHas('tasks', ['id' => $tasks[2]->id, 'sequence_id' => 2]); + $this->assertDatabaseHas('tasks', ['id' => $tasks[3]->id, 'sequence_id' => 3]); + $this->assertDatabaseMissing('tasks', ['id' => $tasks[1]->id]); + } +} diff --git a/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php index 4941a9bd9..a5a4f65f3 100644 --- a/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php +++ b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php @@ -81,6 +81,37 @@ class ProcessScheduleServiceTest extends IntegrationTestCase $this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]); } + /** + * Test that even if a schedule's task sequence gets messed up the first task based on + * the ascending order of tasks is used. + * + * @see https://github.com/pterodactyl/panel/issues/2534 + */ + public function testFirstSequenceTaskIsFound() + { + $this->swap(Dispatcher::class, $dispatcher = Mockery::mock(Dispatcher::class)); + + $server = $this->createServerModel(); + /** @var \Pterodactyl\Models\Schedule $schedule */ + $schedule = factory(Schedule::class)->create(['server_id' => $server->id]); + + /** @var \Pterodactyl\Models\Task $task */ + $task2 = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]); + $task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]); + $task3 = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]); + + $dispatcher->expects('dispatch')->with(Mockery::on(function (RunTaskJob $job) use ($task) { + return $task->id === $job->task->id; + })); + + $this->getService()->handle($schedule); + + $this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]); + $this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]); + $this->assertDatabaseHas('tasks', ['id' => $task2->id, 'is_queued' => false]); + $this->assertDatabaseHas('tasks', ['id' => $task3->id, 'is_queued' => false]); + } + /** * @return array */ From 903b5795dbf74f1ca01093b799703792faa7d0ad Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 22 Oct 2020 21:18:46 -0700 Subject: [PATCH 48/77] Avoid breaking the entire UI when naughty characters are present in the file name or directory; closes #2575 --- .../api/server/files/saveFileContents.ts | 20 +++------- .../components/elements/ErrorBoundary.tsx | 39 +++++++++++++++++++ .../server/files/FileEditContainer.tsx | 9 +++-- .../server/files/FileManagerBreadcrumbs.tsx | 6 +-- .../server/files/FileManagerContainer.tsx | 31 ++++++++------- .../server/files/NewDirectoryButton.tsx | 7 ++-- resources/scripts/routers/ServerRouter.tsx | 5 ++- 7 files changed, 78 insertions(+), 39 deletions(-) create mode 100644 resources/scripts/components/elements/ErrorBoundary.tsx diff --git a/resources/scripts/api/server/files/saveFileContents.ts b/resources/scripts/api/server/files/saveFileContents.ts index 22f1766c3..b97e60a6b 100644 --- a/resources/scripts/api/server/files/saveFileContents.ts +++ b/resources/scripts/api/server/files/saveFileContents.ts @@ -1,18 +1,10 @@ import http from '@/api/http'; -export default (uuid: string, file: string, content: string): Promise => { - return new Promise((resolve, reject) => { - http.post( - `/api/client/servers/${uuid}/files/write`, - content, - { - params: { file }, - headers: { - 'Content-Type': 'text/plain', - }, - }, - ) - .then(() => resolve()) - .catch(reject); +export default async (uuid: string, file: string, content: string): Promise => { + await http.post(`/api/client/servers/${uuid}/files/write`, content, { + params: { file }, + headers: { + 'Content-Type': 'text/plain', + }, }); }; diff --git a/resources/scripts/components/elements/ErrorBoundary.tsx b/resources/scripts/components/elements/ErrorBoundary.tsx new file mode 100644 index 000000000..418d9e06f --- /dev/null +++ b/resources/scripts/components/elements/ErrorBoundary.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import tw from 'twin.macro'; +import Icon from '@/components/elements/Icon'; +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; + +interface State { + hasError: boolean; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +class ErrorBoundary extends React.Component<{}, State> { + state: State = { + hasError: false, + }; + + static getDerivedStateFromError () { + return { hasError: true }; + } + + componentDidCatch (error: Error) { + console.error(error); + } + + render () { + return this.state.hasError ? +
+
+ +

+ An error was encountered by the application while rendering this view. Try refreshing the page. +

+
+
+ : + this.props.children; + } +} + +export default ErrorBoundary; diff --git a/resources/scripts/components/server/files/FileEditContainer.tsx b/resources/scripts/components/server/files/FileEditContainer.tsx index 1faf99e5a..8305e04bd 100644 --- a/resources/scripts/components/server/files/FileEditContainer.tsx +++ b/resources/scripts/components/server/files/FileEditContainer.tsx @@ -16,6 +16,7 @@ import Select from '@/components/elements/Select'; import modes from '@/modes'; import useFlash from '@/plugins/useFlash'; import { ServerContext } from '@/state/server'; +import ErrorBoundary from '@/components/elements/ErrorBoundary'; const LazyCodemirrorEditor = lazy(() => import(/* webpackChunkName: "editor" */'@/components/elements/CodemirrorEditor')); @@ -60,9 +61,7 @@ export default () => { setLoading(true); clearFlashes('files:view'); fetchFileContent() - .then(content => { - return saveFileContents(uuid, name || hash.replace(/^#/, ''), content); - }) + .then(content => saveFileContents(uuid, encodeURIComponent(name || hash.replace(/^#/, '')), content)) .then(() => { if (name) { history.push(`/server/${id}/files/edit#/${name}`); @@ -87,7 +86,9 @@ export default () => { return ( - + + + {hash.replace(/^#/, '').endsWith('.pteroignore') &&

diff --git a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx index ba43dbd61..968a651db 100644 --- a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx +++ b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx @@ -33,10 +33,10 @@ export default ({ withinFileEditor, isNewFile }: Props) => { .filter(directory => !!directory) .map((directory, index, dirs) => { if (!withinFileEditor && index === dirs.length - 1) { - return { name: decodeURIComponent(directory) }; + return { name: decodeURIComponent(encodeURIComponent(directory)) }; } - return { name: decodeURIComponent(directory), path: `/${dirs.slice(0, index + 1).join('/')}` }; + return { name: decodeURIComponent(encodeURIComponent(directory)), path: `/${dirs.slice(0, index + 1).join('/')}` }; }); const onSelectAllClick = (e: React.ChangeEvent) => { @@ -79,7 +79,7 @@ export default ({ withinFileEditor, isNewFile }: Props) => { } {file && - {decodeURIComponent(file)} + {decodeURIComponent(encodeURIComponent(file))} }

diff --git a/resources/scripts/components/server/files/FileManagerContainer.tsx b/resources/scripts/components/server/files/FileManagerContainer.tsx index 4c46edc16..31985e940 100644 --- a/resources/scripts/components/server/files/FileManagerContainer.tsx +++ b/resources/scripts/components/server/files/FileManagerContainer.tsx @@ -17,6 +17,7 @@ import MassActionsBar from '@/components/server/files/MassActionsBar'; import UploadButton from '@/components/server/files/UploadButton'; import ServerContentBlock from '@/components/elements/ServerContentBlock'; import { useStoreActions } from '@/state/hooks'; +import ErrorBoundary from '@/components/elements/ErrorBoundary'; const sortFiles = (files: FileObject[]): FileObject[] => { return files.sort((a, b) => a.name.localeCompare(b.name)) @@ -50,7 +51,9 @@ export default () => { return ( - + + + { !files ? @@ -81,18 +84,20 @@ export default () => { } -
- - - - - -
+ +
+ + + + + +
+
} diff --git a/resources/scripts/components/server/files/NewDirectoryButton.tsx b/resources/scripts/components/server/files/NewDirectoryButton.tsx index 2be673db9..e43f0aff9 100644 --- a/resources/scripts/components/server/files/NewDirectoryButton.tsx +++ b/resources/scripts/components/server/files/NewDirectoryButton.tsx @@ -13,6 +13,7 @@ import useFlash from '@/plugins/useFlash'; import useFileManagerSwr from '@/plugins/useFileManagerSwr'; import { WithClassname } from '@/components/types'; import FlashMessageRender from '@/components/FlashMessageRender'; +import ErrorBoundary from '@/components/elements/ErrorBoundary'; interface Values { directoryName: string; @@ -92,9 +93,9 @@ export default ({ className }: WithClassname) => { This directory will be created as  /home/container/ - {decodeURIComponent( - join(directory, values.directoryName).replace(/^(\.\.\/|\/)+/, ''), - )} + {decodeURIComponent(encodeURIComponent( + join(directory, values.directoryName).replace(/^(\.\.\/|\/)+/, '') + ))}

diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index a90ff652b..ec6bc20b4 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -28,6 +28,7 @@ import NetworkContainer from '@/components/server/network/NetworkContainer'; import InstallListener from '@/components/server/InstallListener'; import StartupContainer from '@/components/server/startup/StartupContainer'; import requireServerPermission from '@/hoc/requireServerPermission'; +import ErrorBoundary from '@/components/elements/ErrorBoundary'; const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => { const rootAdmin = useStoreState(state => state.user.data!.rootAdmin); @@ -120,7 +121,7 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) message={'Please check back in a few minutes.'} /> : - <> + @@ -173,7 +174,7 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) - + } } From fd3b11e9cc91b9a2bf74f068ace40638df5d3d5d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 22 Oct 2020 21:27:15 -0700 Subject: [PATCH 49/77] Update CHANGELOG.md --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c594f9437..591eb44f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,33 @@ 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.0.2 +### Added +* Adds support for searching inside the file editor. +* Adds support for manually executing a schedule regardless of if it is currently queued or not. +* Adds an indicator to the schedule UI to show when a schedule is currently processing. +* Adds support for setting the `backup_limit` of a server via the API. +* **[Security]** Adds login throttling to the 2FA verification endpoint. + +### Fixed +* Fixes subuser page title missing server name. +* Fixes schedule task `sequence_id` not properly being reset when a schedule's task is deleted. +* Fixes misc. UI bugs throughout the frontend when long text overflows its bounds. +* Fixes user deletion command to properly handle email & ID searching. +* Fixes color issues in the terminal causing certain text & background combinations to be illegible. +* Fixes reCAPTCHA not properly resetting on login failure. +* Fixes error messages not properly resetting between login screens. +* Fixes a UI crash when attempting to create or view a directory or file that contained the `%` somewhere in the name. + +### Changed +* Updated the search modal to close itself when the ESC key is pressed. +* Updates the schedule view and editing UI to better display information to users. +* Changed logic powering server searching on the frontend to return more accurate results and return all servers when executing the query as an admin. +* Admin CP link no longer opens in a new tab. +* Mounts will no longer allow a user to mount certain directory combinations. This blocks mounting one server's files into another server, and blocks using the server data directory as a mount destination. +* Cleaned up assorted server build modification code. +* Updates default eggs to have improved install scripts and more consistent container usage. + ## v1.0.1 ### Fixed * Fixes 500 error when mounting a mount to a server, and other related errors when handling mounts. From 23872b844a5048959555ae9dd9547ce76d9f24e8 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Thu, 22 Oct 2020 21:33:06 -0700 Subject: [PATCH 50/77] Fix unnecessary object structuring --- .../scripts/components/auth/LoginCheckpointContainer.tsx | 2 +- .../components/dashboard/forms/DisableTwoFactorModal.tsx | 2 +- .../components/dashboard/forms/SetupTwoFactorModal.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx index 4c3dff599..df2018ee7 100644 --- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx +++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx @@ -91,7 +91,7 @@ const EnhancedForm = withFormik({ .catch(error => { console.error(error); setSubmitting(false); - clearAndAddHttpError({ error: error }); + clearAndAddHttpError({ error }); }); }, diff --git a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx index d4f986942..8500711fc 100644 --- a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx @@ -27,7 +27,7 @@ export default ({ ...props }: RequiredModalProps) => { .catch(error => { console.error(error); - clearAndAddHttpError({ error: error, key: 'account:two-factor' }); + clearAndAddHttpError({ error, key: 'account:two-factor' }); setSubmitting(false); }); }; diff --git a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx index eb8e1a890..57eefa680 100644 --- a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx @@ -28,7 +28,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => { .then(setToken) .catch(error => { console.error(error); - clearAndAddHttpError({ error: error, key: 'account:two-factor' }); + clearAndAddHttpError({ error, key: 'account:two-factor' }); }); }, []); @@ -40,7 +40,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => { .catch(error => { console.error(error); - clearAndAddHttpError({ error: error, key: 'account:two-factor' }); + clearAndAddHttpError({ error, key: 'account:two-factor' }); }) .then(() => setSubmitting(false)); }; From 404ad68e0d5485bf7b217ea12d313ba922cae177 Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Fri, 23 Oct 2020 03:37:35 -0400 Subject: [PATCH 51/77] find != fund --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27feb040b..f1cf28848 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ 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) ## Sponsors -I would like to extend my sincere thanks to the following sponsors for helping find Pterodactyl's developement. +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) | Company | About | From a271b590925fca8b05bf6bf13ed4a8db627f0ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20G=2E=20J=C3=B8rgensen?= Date: Sun, 25 Oct 2020 21:15:49 +0100 Subject: [PATCH 52/77] Change SameSite attribute on session cookies to "lax" (#2592) --- app/Console/Commands/Environment/AppSettingsCommand.php | 5 +++++ config/session.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php index 60254a9ef..01518d610 100644 --- a/app/Console/Commands/Environment/AppSettingsCommand.php +++ b/app/Console/Commands/Environment/AppSettingsCommand.php @@ -144,6 +144,11 @@ class AppSettingsCommand extends Command $this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm(trans('command/messages.environment.app.settings'), true) ? 'false' : 'true'; } + // Make sure session cookies are set as "secure" when using HTTPS + if (strpos($this->variables['APP_URL'], 'https://') === 0) { + $this->variables['SESSION_SECURE_COOKIE'] = 'true'; + } + $this->checkForRedis(); $this->writeToEnvironment($this->variables); diff --git a/config/session.php b/config/session.php index 2007acb2e..8605db59b 100644 --- a/config/session.php +++ b/config/session.php @@ -188,5 +188,5 @@ return [ | */ - 'same_site' => null, + 'same_site' => env('SESSION_SAMESITE_COOKIE', 'lax'), ]; From 3ecf14d4196e22933287e9fa9e18d92c697b184d Mon Sep 17 00:00:00 2001 From: "Michael (Parker) Parker" Date: Sun, 25 Oct 2020 16:16:18 -0400 Subject: [PATCH 53/77] fix install scripts (#2587) --- database/seeds/eggs/minecraft/egg-vanilla-minecraft.json | 4 ++-- database/seeds/eggs/voice-servers/egg-teamspeak3-server.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json index 80d697949..fadd91a4e 100644 --- a/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json +++ b/database/seeds/eggs/minecraft/egg-vanilla-minecraft.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-10-19T23:28:18+00:00", + "exported_at": "2020-10-23T23:04:17+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.", @@ -17,7 +17,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y jq\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"", + "script": "#!\/bin\/bash\r\n# Vanilla MC Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y jq curl\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nLATEST_VERSION=`curl https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq -r '.latest.release'`\r\n\r\necho -e \"latest version is $LATEST_VERSION\"\r\n\r\nif [ -z \"$VANILLA_VERSION\" ] || [ \"$VANILLA_VERSION\" == \"latest\" ]; then\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $LATEST_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nelse\r\n MANIFEST_URL=$(curl -sSL https:\/\/launchermeta.mojang.com\/mc\/game\/version_manifest.json | jq --arg VERSION $VANILLA_VERSION -r '.versions | .[] | select(.id== $VERSION )|.url')\r\nfi\r\n\r\nDOWNLOAD_URL=$(curl ${MANIFEST_URL} | jq .downloads.server | jq -r '. | .url')\r\n\r\necho -e \"running: curl -o ${SERVER_JARFILE} $DOWNLOAD_URL\"\r\ncurl -o ${SERVER_JARFILE} $DOWNLOAD_URL\r\n\r\necho -e \"Install Complete\"", "container": "debian:buster-slim", "entrypoint": "bash" } diff --git a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json index e0ae85d8d..aeba79316 100644 --- a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json +++ b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-10-20T00:24:22+00:00", + "exported_at": "2020-10-23T22:27:50+00:00", "name": "Teamspeak3 Server", "author": "support@pterodactyl.io", "description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.", @@ -17,7 +17,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napk add --no-cache tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https:\/\/teamspeak.com\/versions\/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1", + "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https:\/\/teamspeak.com\/versions\/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1", "container": "debian:buster-slim", "entrypoint": "bash" } From 8c6327fd3253e009f86313695a57469728fd7807 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 25 Oct 2020 15:06:54 -0700 Subject: [PATCH 54/77] Let MySQL do the time logic when looking for tasks --- .../Schedule/ProcessRunnableCommand.php | 60 ++++------- .../ScheduleRepositoryInterface.php | 8 -- app/Helpers/Utilities.php | 7 +- .../Eloquent/ScheduleRepository.php | 15 --- .../Schedule/ProcessRunnableCommandTest.php | 102 ------------------ 5 files changed, 24 insertions(+), 168 deletions(-) delete mode 100644 tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php diff --git a/app/Console/Commands/Schedule/ProcessRunnableCommand.php b/app/Console/Commands/Schedule/ProcessRunnableCommand.php index 67654708c..4d5981fd3 100644 --- a/app/Console/Commands/Schedule/ProcessRunnableCommand.php +++ b/app/Console/Commands/Schedule/ProcessRunnableCommand.php @@ -1,62 +1,38 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Console\Commands\Schedule; -use Cake\Chronos\Chronos; use Illuminate\Console\Command; -use Illuminate\Support\Collection; +use Pterodactyl\Models\Schedule; use Pterodactyl\Services\Schedules\ProcessScheduleService; -use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; class ProcessRunnableCommand extends Command { /** * @var string */ - protected $description = 'Process schedules in the database and determine which are ready to run.'; - - /** - * @var \Pterodactyl\Services\Schedules\ProcessScheduleService - */ - protected $processScheduleService; - - /** - * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface - */ - protected $repository; + protected $signature = 'p:schedule:process'; /** * @var string */ - protected $signature = 'p:schedule:process'; - - /** - * ProcessRunnableCommand constructor. - * - * @param \Pterodactyl\Services\Schedules\ProcessScheduleService $processScheduleService - * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository - */ - public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository) - { - parent::__construct(); - - $this->processScheduleService = $processScheduleService; - $this->repository = $repository; - } + protected $description = 'Process schedules in the database and determine which are ready to run.'; /** * Handle command execution. + * + * @param \Pterodactyl\Services\Schedules\ProcessScheduleService $service + * + * @throws \Throwable */ - public function handle() + public function handle(ProcessScheduleService $service) { - $schedules = $this->repository->getSchedulesToProcess(Chronos::now()->toAtomString()); + $schedules = Schedule::query()->with('tasks') + ->where('is_active', true) + ->where('is_processing', false) + ->whereRaw('next_run_at <= NOW()') + ->get(); + if ($schedules->count() < 1) { $this->line('There are no scheduled tasks for servers that need to be run.'); @@ -64,9 +40,9 @@ class ProcessRunnableCommand extends Command } $bar = $this->output->createProgressBar(count($schedules)); - $schedules->each(function ($schedule) use ($bar) { - if ($schedule->tasks instanceof Collection && count($schedule->tasks) > 0) { - $this->processScheduleService->handle($schedule); + foreach ($schedules as $schedule) { + if ($schedule->tasks->isNotEmpty()) { + $service->handle($schedule); if ($this->input->isInteractive()) { $bar->clear(); @@ -79,7 +55,7 @@ class ProcessRunnableCommand extends Command $bar->advance(); $bar->display(); - }); + } $this->line(''); } diff --git a/app/Contracts/Repository/ScheduleRepositoryInterface.php b/app/Contracts/Repository/ScheduleRepositoryInterface.php index 32650bdcf..b6e73a2de 100644 --- a/app/Contracts/Repository/ScheduleRepositoryInterface.php +++ b/app/Contracts/Repository/ScheduleRepositoryInterface.php @@ -24,12 +24,4 @@ interface ScheduleRepositoryInterface extends RepositoryInterface * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getScheduleWithTasks(int $schedule): Schedule; - - /** - * Return all of the schedules that should be processed. - * - * @param string $timestamp - * @return \Illuminate\Support\Collection - */ - public function getSchedulesToProcess(string $timestamp): Collection; } diff --git a/app/Helpers/Utilities.php b/app/Helpers/Utilities.php index d900425d9..d6b4e752f 100644 --- a/app/Helpers/Utilities.php +++ b/app/Helpers/Utilities.php @@ -52,7 +52,12 @@ class Utilities )->getNextRunDate()); } - public static function checked($name, $default) + /** + * @param string $name + * @param mixed $default + * @return string + */ + public static function checked(string $name, $default) { $errors = session('errors'); diff --git a/app/Repositories/Eloquent/ScheduleRepository.php b/app/Repositories/Eloquent/ScheduleRepository.php index 030939da7..cb1b2cbf0 100644 --- a/app/Repositories/Eloquent/ScheduleRepository.php +++ b/app/Repositories/Eloquent/ScheduleRepository.php @@ -47,19 +47,4 @@ class ScheduleRepository extends EloquentRepository implements ScheduleRepositor throw new RecordNotFoundException; } } - - /** - * Return all of the schedules that should be processed. - * - * @param string $timestamp - * @return \Illuminate\Support\Collection - */ - public function getSchedulesToProcess(string $timestamp): Collection - { - return $this->getBuilder()->with('tasks') - ->where('is_active', true) - ->where('is_processing', false) - ->where('next_run_at', '<=', $timestamp) - ->get($this->getColumns()); - } } diff --git a/tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php b/tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php deleted file mode 100644 index 5efbef6c4..000000000 --- a/tests/Unit/Commands/Schedule/ProcessRunnableCommandTest.php +++ /dev/null @@ -1,102 +0,0 @@ -processScheduleService = m::mock(ProcessScheduleService::class); - $this->repository = m::mock(ScheduleRepositoryInterface::class); - - $this->command = new ProcessRunnableCommand($this->processScheduleService, $this->repository); - } - - /** - * Test that a schedule can be queued up correctly. - */ - public function testScheduleIsQueued() - { - $schedule = factory(Schedule::class)->make(); - $schedule->tasks = collect([factory(Task::class)->make()]); - - $this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule])); - $this->processScheduleService->shouldReceive('handle')->with($schedule)->once()->andReturnNull(); - - $display = $this->runCommand($this->command); - - $this->assertNotEmpty($display); - $this->assertStringContainsString(trans('command/messages.schedule.output_line', [ - 'schedule' => $schedule->name, - 'hash' => $schedule->hashid, - ]), $display); - } - - /** - * If tasks is an empty collection, don't process it. - */ - public function testScheduleWithNoTasksIsNotProcessed() - { - $schedule = factory(Schedule::class)->make(); - $schedule->tasks = collect([]); - - $this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule])); - - $display = $this->runCommand($this->command); - - $this->assertNotEmpty($display); - $this->assertStringNotContainsString(trans('command/messages.schedule.output_line', [ - 'schedule' => $schedule->name, - 'hash' => $schedule->hashid, - ]), $display); - } - - /** - * If tasks isn't an instance of a collection, don't process it. - */ - public function testScheduleWithTasksObjectThatIsNotInstanceOfCollectionIsNotProcessed() - { - $schedule = factory(Schedule::class)->make(['tasks' => null]); - - $this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule])); - - $display = $this->runCommand($this->command); - - $this->assertNotEmpty($display); - $this->assertStringNotContainsString(trans('command/messages.schedule.output_line', [ - 'schedule' => $schedule->name, - 'hash' => $schedule->hashid, - ]), $display); - } -} From 996fb5b46f6ebaa9e38d58d3aeeb2feae654e814 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 25 Oct 2020 15:07:11 -0700 Subject: [PATCH 55/77] Set the DB timezone on each connection to match the APP_TIMEZONE value --- app/Helpers/Time.php | 25 +++++++++++++++++++++++++ config/database.php | 4 ++++ 2 files changed, 29 insertions(+) create mode 100644 app/Helpers/Time.php diff --git a/app/Helpers/Time.php b/app/Helpers/Time.php new file mode 100644 index 000000000..248ff55f0 --- /dev/null +++ b/app/Helpers/Time.php @@ -0,0 +1,25 @@ +getTimezone()->getOffset(CarbonImmutable::now('UTC')) / 3600); + + return sprintf('%s%s:00', $offset > 0 ? '+' : '-', str_pad(abs($offset), 2, '0', STR_PAD_LEFT)); + } +} diff --git a/config/database.php b/config/database.php index 32fca7581..5a4e8bc77 100644 --- a/config/database.php +++ b/config/database.php @@ -1,5 +1,7 @@ 'utf8mb4_unicode_ci', 'prefix' => env('DB_PREFIX', ''), 'strict' => env('DB_STRICT_MODE', false), + 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE'))) ], /* @@ -65,6 +68,7 @@ return [ 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, + 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE'))) ], ], From 39dddba1d6728c4f6bcca09f7e578afa5753aea9 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 25 Oct 2020 15:47:50 -0700 Subject: [PATCH 56/77] Refactor subuser modal and fix to be less of a code monstrosity; closes #2583 --- .../server/users/AddSubuserButton.tsx | 2 +- .../server/users/EditSubuserModal.tsx | 266 +++++------------- .../components/server/users/PermissionRow.tsx | 65 +++++ .../server/users/PermissionTitleBox.tsx | 50 ++++ .../components/server/users/UserRow.tsx | 7 +- resources/scripts/hoc/asModal.tsx | 71 +++-- 6 files changed, 228 insertions(+), 233 deletions(-) create mode 100644 resources/scripts/components/server/users/PermissionRow.tsx create mode 100644 resources/scripts/components/server/users/PermissionTitleBox.tsx diff --git a/resources/scripts/components/server/users/AddSubuserButton.tsx b/resources/scripts/components/server/users/AddSubuserButton.tsx index 210578bb9..fa8b2f8e6 100644 --- a/resources/scripts/components/server/users/AddSubuserButton.tsx +++ b/resources/scripts/components/server/users/AddSubuserButton.tsx @@ -10,7 +10,7 @@ export default () => { return ( <> - {visible && setVisible(false)}/>} + setVisible(false)}/> diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx index d81d15f08..9479b0ed8 100644 --- a/resources/scripts/components/server/users/EditSubuserModal.tsx +++ b/resources/scripts/components/server/users/EditSubuserModal.tsx @@ -1,14 +1,10 @@ -import React, { forwardRef, memo, useCallback, useEffect, useRef } from 'react'; +import React, { useContext, useEffect, useRef } from 'react'; import { Subuser } from '@/state/server/subusers'; -import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; +import { Form, Formik } from 'formik'; import { array, object, string } from 'yup'; -import Modal, { RequiredModalProps } from '@/components/elements/Modal'; import Field from '@/components/elements/Field'; import { Actions, useStoreActions, useStoreState } from 'easy-peasy'; import { ApplicationStore } from '@/state'; -import TitledGreyBox from '@/components/elements/TitledGreyBox'; -import Checkbox from '@/components/elements/Checkbox'; -import styled from 'styled-components/macro'; import createOrUpdateSubuser from '@/api/server/users/createOrUpdateSubuser'; import { ServerContext } from '@/state/server'; import FlashMessageRender from '@/components/FlashMessageRender'; @@ -17,104 +13,33 @@ import { usePermissions } from '@/plugins/usePermissions'; import { useDeepCompareMemo } from '@/plugins/useDeepCompareMemo'; import tw from 'twin.macro'; import Button from '@/components/elements/Button'; -import Label from '@/components/elements/Label'; -import Input from '@/components/elements/Input'; -import isEqual from 'react-fast-compare'; +import PermissionTitleBox from '@/components/server/users/PermissionTitleBox'; +import asModal from '@/hoc/asModal'; +import PermissionRow from '@/components/server/users/PermissionRow'; +import ModalContext from '@/context/ModalContext'; type Props = { subuser?: Subuser; -} & RequiredModalProps; +}; interface Values { email: string; permissions: string[]; } -const PermissionLabel = styled.label` - ${tw`flex items-center border border-transparent rounded md:p-2 transition-colors duration-75`}; - text-transform: none; +const EditSubuserModal = ({ subuser }: Props) => { + const ref = useRef(null); + const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser); + const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes); + const { dismiss, toggleSpinner } = useContext(ModalContext); - &:not(.disabled) { - ${tw`cursor-pointer`}; - - &:hover { - ${tw`border-neutral-500 bg-neutral-800`}; - } - } - - &:not(:first-of-type) { - ${tw`mt-4 sm:mt-2`}; - } - - &.disabled { - ${tw`opacity-50`}; - - & input[type="checkbox"]:not(:checked) { - ${tw`border-0`}; - } - } -`; - -interface TitleProps { - isEditable: boolean; - permission: string; - permissions: string[]; - children: React.ReactNode; - className?: string; -} - -const PermissionTitledBox = memo(({ isEditable, permission, permissions, className, children }: TitleProps) => { - const { values, setFieldValue } = useFormikContext(); - - const onCheckboxClicked = useCallback((e: React.ChangeEvent) => { - console.log(e.currentTarget.checked, [ - ...values.permissions, - ...permissions.filter(p => !values.permissions.includes(p)), - ]); - - if (e.currentTarget.checked) { - setFieldValue('permissions', [ - ...values.permissions, - ...permissions.filter(p => !values.permissions.includes(p)), - ]); - } else { - setFieldValue('permissions', [ - ...values.permissions.filter(p => !permissions.includes(p)), - ]); - } - }, [ permissions, values.permissions ]); - - return ( - -

{permission}

- {isEditable && - values.permissions.includes(p))} - onChange={onCheckboxClicked} - /> - } -
- } - className={className} - > - {children} - - ); -}, isEqual); - -const EditSubuserModal = forwardRef(({ subuser, ...props }, ref) => { - const { isSubmitting } = useFormikContext(); - const [ canEditUser ] = usePermissions(subuser ? [ 'user.update' ] : [ 'user.create' ]); + const isRootAdmin = useStoreState(state => state.user.data!.rootAdmin); const permissions = useStoreState(state => state.permissions.data); - - const user = useStoreState(state => state.user.data!); - // The currently logged in user's permissions. We're going to filter out any permissions // that they should not need. const loggedInPermissions = ServerContext.useStoreState(state => state.server.permissions); + const [ canEditUser ] = usePermissions(subuser ? [ 'user.update' ] : [ 'user.create' ]); // The permissions that can be modified by this user. const editablePermissions = useDeepCompareMemo(() => { @@ -123,111 +48,25 @@ const EditSubuserModal = forwardRef(({ subuser, ...pr const list: string[] = ([] as string[]).concat.apply([], Object.values(cleaned)); - if (user.rootAdmin || (loggedInPermissions.length === 1 && loggedInPermissions[0] === '*')) { + if (isRootAdmin || (loggedInPermissions.length === 1 && loggedInPermissions[0] === '*')) { return list; } return list.filter(key => loggedInPermissions.indexOf(key) >= 0); - }, [ permissions, loggedInPermissions ]); + }, [ isRootAdmin, permissions, loggedInPermissions ]); - return ( - -

- {subuser ? - `${canEditUser ? 'Modify' : 'View'} permissions for ${subuser.email}` - : - 'Create new subuser' - } -

- - {(!user.rootAdmin && loggedInPermissions[0] !== '*') && -
-

- Only permissions which your account is currently assigned may be selected when creating or - modifying other users. -

-
- } - {!subuser && -
- -
- } -
- {Object.keys(permissions).filter(key => key !== 'websocket').map((key, index) => { - const group = Object.keys(permissions[key].keys).map(pkey => `${key}.${pkey}`); - - return ( - 0 ? tw`mt-4` : undefined} - > -

- {permissions[key].description} -

- {Object.keys(permissions[key].keys).map(pkey => ( - -
- -
-
- - {permissions[key].keys[pkey].length > 0 && -

- {permissions[key].keys[pkey]} -

- } -
-
- ))} -
- ); - })} -
- -
- -
-
-
- ); -}); - -export default ({ subuser, ...props }: Props) => { - const ref = useRef(null); - const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); - const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser); - const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes); - - const submit = (values: Values, { setSubmitting }: FormikHelpers) => { + const submit = (values: Values) => { + toggleSpinner(true); clearFlashes('user:edit'); + createOrUpdateSubuser(uuid, values, subuser) .then(subuser => { appendSubuser(subuser); - props.onDismissed(); + dismiss(); }) .catch(error => { console.error(error); - setSubmitting(false); + toggleSpinner(false); clearAndAddHttpError({ key: 'user:edit', error }); if (ref.current) { @@ -236,10 +75,8 @@ export default ({ subuser, ...props }: Props) => { }); }; - useEffect(() => { - return () => { - clearFlashes('user:edit'); - }; + useEffect(() => () => { + clearFlashes('user:edit'); }, []); return ( @@ -258,8 +95,61 @@ export default ({ subuser, ...props }: Props) => { })} >
- +

+ {subuser ? `${canEditUser ? 'Modify' : 'View'} permissions for ${subuser.email}` : 'Create new subuser'} +

+ + {(!isRootAdmin && loggedInPermissions[0] !== '*') && +
+

+ Only permissions which your account is currently assigned may be selected when creating or + modifying other users. +

+
+ } + {!subuser && +
+ +
+ } +
+ {Object.keys(permissions).filter(key => key !== 'websocket').map((key, index) => ( + `${key}.${pkey}`)} + css={index > 0 ? tw`mt-4` : undefined} + > +

+ {permissions[key].description} +

+ {Object.keys(permissions[key].keys).map(pkey => ( + + ))} +
+ ))} +
+ +
+ +
+
); }; + +export default asModal({ + top: false, +})(EditSubuserModal); diff --git a/resources/scripts/components/server/users/PermissionRow.tsx b/resources/scripts/components/server/users/PermissionRow.tsx new file mode 100644 index 000000000..1f48eaaf0 --- /dev/null +++ b/resources/scripts/components/server/users/PermissionRow.tsx @@ -0,0 +1,65 @@ +import styled from 'styled-components/macro'; +import tw from 'twin.macro'; +import Checkbox from '@/components/elements/Checkbox'; +import React from 'react'; +import { useStoreState } from 'easy-peasy'; +import Label from '@/components/elements/Label'; + +const Container = styled.label` + ${tw`flex items-center border border-transparent rounded md:p-2 transition-colors duration-75`}; + text-transform: none; + + &:not(.disabled) { + ${tw`cursor-pointer`}; + + &:hover { + ${tw`border-neutral-500 bg-neutral-800`}; + } + } + + &:not(:first-of-type) { + ${tw`mt-4 sm:mt-2`}; + } + + &.disabled { + ${tw`opacity-50`}; + + & input[type="checkbox"]:not(:checked) { + ${tw`border-0`}; + } + } +`; + +interface Props { + permission: string; + disabled: boolean; +} + +const PermissionRow = ({ permission, disabled }: Props) => { + const [ key, pkey ] = permission.split('.', 2); + const permissions = useStoreState(state => state.permissions.data); + + return ( + +
+ +
+
+ + {permissions[key].keys[pkey].length > 0 && +

+ {permissions[key].keys[pkey]} +

+ } +
+
+ ); +}; + +export default PermissionRow; diff --git a/resources/scripts/components/server/users/PermissionTitleBox.tsx b/resources/scripts/components/server/users/PermissionTitleBox.tsx new file mode 100644 index 000000000..a3e1453a4 --- /dev/null +++ b/resources/scripts/components/server/users/PermissionTitleBox.tsx @@ -0,0 +1,50 @@ +import React, { memo, useCallback } from 'react'; +import { useField } from 'formik'; +import TitledGreyBox from '@/components/elements/TitledGreyBox'; +import tw from 'twin.macro'; +import Input from '@/components/elements/Input'; +import isEqual from 'react-fast-compare'; + +interface Props { + isEditable: boolean; + title: string; + permissions: string[]; + className?: string; +} + +const PermissionTitleBox: React.FC = memo(({ isEditable, title, permissions, className, children }) => { + const [ { value }, , { setValue } ] = useField('permissions'); + + const onCheckboxClicked = useCallback((e: React.ChangeEvent) => { + if (e.currentTarget.checked) { + setValue([ + ...value, + ...permissions.filter(p => !value.includes(p)), + ]); + } else { + setValue(value.filter(p => !permissions.includes(p))); + } + }, [ permissions, value ]); + + return ( + +

{title}

+ {isEditable && + value.includes(p))} + onChange={onCheckboxClicked} + /> + } +
+ } + className={className} + > + {children} + + ); +}, isEqual); + +export default PermissionTitleBox; diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx index 237621660..aa5e55a39 100644 --- a/resources/scripts/components/server/users/UserRow.tsx +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -19,14 +19,11 @@ export default ({ subuser }: Props) => { return ( - {visible && setVisible(false)} + visible={visible} + onModalDismissed={() => setVisible(false)} /> - } diff --git a/resources/scripts/hoc/asModal.tsx b/resources/scripts/hoc/asModal.tsx index 7db437c14..81027f5d3 100644 --- a/resources/scripts/hoc/asModal.tsx +++ b/resources/scripts/hoc/asModal.tsx @@ -1,7 +1,6 @@ import React from 'react'; import Modal, { ModalProps } from '@/components/elements/Modal'; import ModalContext from '@/context/ModalContext'; -import isEqual from 'react-fast-compare'; export interface AsModalProps { visible: boolean; @@ -13,7 +12,7 @@ type ExtendedModalProps = Omit interface State { render: boolean; visible: boolean; - modalProps: ExtendedModalProps | undefined; + showSpinnerOverlay?: boolean; } type ExtendedComponentType = (C: React.ComponentType) => React.ComponentType; @@ -30,17 +29,18 @@ function asModal

(modalProps?: ExtendedModalProps | ((props: P this.state = { render: props.visible, visible: props.visible, - modalProps: typeof modalProps === 'function' ? modalProps(this.props) : modalProps, + showSpinnerOverlay: undefined, + }; + } + + get modalProps () { + return { + ...(typeof modalProps === 'function' ? modalProps(this.props) : modalProps), + showSpinnerOverlay: this.state.showSpinnerOverlay, }; } componentDidUpdate (prevProps: Readonly

) { - const mapped = typeof modalProps === 'function' ? modalProps(this.props) : modalProps; - if (!isEqual(this.state.modalProps, mapped)) { - // noinspection JSPotentiallyInvalidUsageOfThis - this.setState({ modalProps: mapped }); - } - if (prevProps.visible && !this.props.visible) { // noinspection JSPotentiallyInvalidUsageOfThis this.setState({ visible: false }); @@ -52,39 +52,32 @@ function asModal

(modalProps?: ExtendedModalProps | ((props: P dismiss = () => this.setState({ visible: false }); - toggleSpinner = (value?: boolean) => this.setState(s => ({ - modalProps: { - ...s.modalProps, - showSpinnerOverlay: value || false, - }, - })); + toggleSpinner = (value?: boolean) => this.setState({ showSpinnerOverlay: value }); render () { return ( - - { - this.state.render ? - this.setState({ render: false }, () => { - if (typeof this.props.onModalDismissed === 'function') { - this.props.onModalDismissed(); - } - })} - {...this.state.modalProps} - > - - - : - null - } - + this.state.render ? + this.setState({ render: false }, () => { + if (typeof this.props.onModalDismissed === 'function') { + this.props.onModalDismissed(); + } + })} + {...this.modalProps} + > + + + + + : + null ); } }; From 092c942764e7bb8b116e8c1b1d3923e31a8b890f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 25 Oct 2020 17:29:57 -0700 Subject: [PATCH 57/77] Fix server owner filtering; improve searching for servers; closes #2581 --- .../Admin/Servers/ServerController.php | 8 +++- app/Models/Filters/AdminServerFilter.php | 40 +++++++++++++++++++ resources/views/admin/servers/index.blade.php | 2 +- resources/views/admin/users/index.blade.php | 2 +- 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 app/Models/Filters/AdminServerFilter.php diff --git a/app/Http/Controllers/Admin/Servers/ServerController.php b/app/Http/Controllers/Admin/Servers/ServerController.php index a0b73f552..c8e54b1c3 100644 --- a/app/Http/Controllers/Admin/Servers/ServerController.php +++ b/app/Http/Controllers/Admin/Servers/ServerController.php @@ -6,7 +6,9 @@ use Illuminate\Http\Request; use Pterodactyl\Models\Server; use Spatie\QueryBuilder\QueryBuilder; use Illuminate\Contracts\View\Factory; +use Spatie\QueryBuilder\AllowedFilter; use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Models\Filters\AdminServerFilter; use Pterodactyl\Repositories\Eloquent\ServerRepository; class ServerController extends Controller @@ -45,8 +47,10 @@ class ServerController extends Controller public function index(Request $request) { $servers = QueryBuilder::for(Server::query()->with('node', 'user', 'allocation')) - ->allowedFilters(['uuid', 'name', 'image']) - ->allowedSorts(['id', 'uuid']) + ->allowedFilters([ + AllowedFilter::exact('owner_id'), + AllowedFilter::custom('*', new AdminServerFilter), + ]) ->paginate(config()->get('pterodactyl.paginate.admin.servers')); return $this->view->make('admin.servers.index', ['servers' => $servers]); diff --git a/app/Models/Filters/AdminServerFilter.php b/app/Models/Filters/AdminServerFilter.php new file mode 100644 index 000000000..d523e48e0 --- /dev/null +++ b/app/Models/Filters/AdminServerFilter.php @@ -0,0 +1,40 @@ +getQuery()->from !== 'servers') { + throw new BadMethodCallException( + 'Cannot use the AdminServerFilter against a non-server model.' + ); + } + $query + ->select('servers.*') + ->leftJoin('users', 'users.id', '=', 'servers.owner_id') + ->where(function (Builder $builder) use ($value) { + $builder->where('servers.uuid', $value) + ->orWhere('servers.uuid', 'LIKE', "$value%") + ->orWhere('servers.uuidShort', $value) + ->orWhere('servers.external_id', $value) + ->orWhereRaw('LOWER(users.username) LIKE ?', ["%$value%"]) + ->orWhereRaw('LOWER(users.email) LIKE ?', ["$value%"]) + ->orWhereRaw('LOWER(servers.name) LIKE ?', ["%$value%"]); + }) + ->groupBy('servers.id'); + } +} diff --git a/resources/views/admin/servers/index.blade.php b/resources/views/admin/servers/index.blade.php index f76ab1c33..b37044103 100644 --- a/resources/views/admin/servers/index.blade.php +++ b/resources/views/admin/servers/index.blade.php @@ -26,7 +26,7 @@

- +
diff --git a/resources/views/admin/users/index.blade.php b/resources/views/admin/users/index.blade.php index 98efd1176..efde3ba4c 100644 --- a/resources/views/admin/users/index.blade.php +++ b/resources/views/admin/users/index.blade.php @@ -64,7 +64,7 @@ @endif - {{ $user->servers_count }} + {{ $user->servers_count }} {{ $user->subuser_of_count }} From 0a6cf5ba25467a6f0fde1b90f45b7a9e58f7642d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 25 Oct 2020 17:31:24 -0700 Subject: [PATCH 58/77] lint; skip ci --- .../scripts/components/server/files/NewDirectoryButton.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/scripts/components/server/files/NewDirectoryButton.tsx b/resources/scripts/components/server/files/NewDirectoryButton.tsx index e43f0aff9..0056cf688 100644 --- a/resources/scripts/components/server/files/NewDirectoryButton.tsx +++ b/resources/scripts/components/server/files/NewDirectoryButton.tsx @@ -13,7 +13,6 @@ import useFlash from '@/plugins/useFlash'; import useFileManagerSwr from '@/plugins/useFileManagerSwr'; import { WithClassname } from '@/components/types'; import FlashMessageRender from '@/components/FlashMessageRender'; -import ErrorBoundary from '@/components/elements/ErrorBoundary'; interface Values { directoryName: string; @@ -94,7 +93,7 @@ export default ({ className }: WithClassname) => {  /home/container/ {decodeURIComponent(encodeURIComponent( - join(directory, values.directoryName).replace(/^(\.\.\/|\/)+/, '') + join(directory, values.directoryName).replace(/^(\.\.\/|\/)+/, ''), ))}

From 2bd2cdcd1c0b7e55f1ea13a71334ff5ad8970797 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 25 Oct 2020 18:06:07 -0700 Subject: [PATCH 59/77] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f1cf28848..ace35378b 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,10 @@ I would like to extend my sincere thanks to the following sponsors for helping f | [**DedicatedMC**](https://dedicatedmc.io/) | DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance and giving you the best performance money can buy. | | [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | | [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. | -| [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHosting’s reliable servers and network. Easy to use, provisioned in a couple of minutes. | -| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. | -| [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. | +| [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHosting’s reliable servers and network. Easy to use, provisioned in a couple of minutes. | +| [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. | +| [**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. | ## Documentation * [Panel Documentation](https://pterodactyl.io/panel/1.0/getting_started.html) From ad4df56f7ca7271c7a973930d2e9132c67590348 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 25 Oct 2020 18:12:22 -0700 Subject: [PATCH 60/77] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 591eb44f5..0dfb9e661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ 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.0.3 +### Fixed +* Fixes bug causing subusers to not be creatable or editable via the frontend for servers. +* Fixes system timezone not being passed along properly to the MySQL connection causing scheduled tasks to run every minute when the MySQL instance and Panel timezone did not line up. +* Fixes listing servers owned by a user in the admin area to actually list their servers. + +### Changed +* Adds SameSite `lax` attribute for cookies generated by the Panel. +* Adds better filtering for searching servers in the admin area to better key off name, uuid, or owner username/email. + ## v1.0.2 ### Added * Adds support for searching inside the file editor. From e8e2206a401aa40877485dc531029a4b12254159 Mon Sep 17 00:00:00 2001 From: "Michael (Parker) Parker" Date: Sun, 25 Oct 2020 21:17:12 -0400 Subject: [PATCH 61/77] Fix install scripts (#2600) --- database/seeds/eggs/minecraft/egg-paper.json | 4 ++-- database/seeds/eggs/voice-servers/egg-mumble-server.json | 6 +++--- .../seeds/eggs/voice-servers/egg-teamspeak3-server.json | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/database/seeds/eggs/minecraft/egg-paper.json b/database/seeds/eggs/minecraft/egg-paper.json index e402cb50e..0cb09ffc3 100644 --- a/database/seeds/eggs/minecraft/egg-paper.json +++ b/database/seeds/eggs/minecraft/egg-paper.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-10-19T23:26:07+00:00", + "exported_at": "2020-10-25T21:03:55+00:00", "name": "Paper", "author": "parker@pterodactyl.io", "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", @@ -17,7 +17,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | IN($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft\/java\/server.properties\r\nfi", + "script": "#!\/bin\/bash\r\n# Paper Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y curl jq\r\n\r\nif [ -n \"${DL_PATH}\" ]; then\r\n echo -e \"using supplied download url\"\r\n DOWNLOAD_URL=`eval echo $(echo ${DL_PATH} | sed -e 's\/{{\/${\/g' -e 's\/}}\/}\/g')`\r\nelse\r\n VER_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r --arg VERSION $MINECRAFT_VERSION '.versions[] | contains($VERSION)' | grep true`\r\n LATEST_PAPER_VERSION=`curl -s https:\/\/papermc.io\/api\/v1\/paper | jq -r '.versions' | jq -r '.[0]'`\r\n \r\n if [ \"${VER_EXISTS}\" == \"true\" ]; then\r\n echo -e \"Version is valid. Using version ${MINECRAFT_VERSION}\"\r\n else\r\n echo -e \"Using the latest paper version\"\r\n MINECRAFT_VERSION=${LATEST_PAPER_VERSION}\r\n fi\r\n \r\n BUILD_EXISTS=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r --arg BUILD ${BUILD_NUMBER} '.builds.all[] | IN($BUILD)' | grep true`\r\n LATEST_PAPER_BUILD=`curl -s https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION} | jq -r '.builds.latest'`\r\n \r\n if [ \"${BUILD_EXISTS}\" == \"true\" ] || [ ${BUILD_NUMBER} == \"latest\" ]; then\r\n echo -e \"Build is valid. Using version ${BUILD_NUMBER}\"\r\n else\r\n echo -e \"Using the latest paper build\"\r\n BUILD_NUMBER=${LATEST_PAPER_BUILD}\r\n fi\r\n \r\n echo \"Version being downloaded\"\r\n echo -e \"MC Version: ${MINECRAFT_VERSION}\"\r\n echo -e \"Build: ${BUILD_NUMBER}\"\r\n DOWNLOAD_URL=https:\/\/papermc.io\/api\/v1\/paper\/${MINECRAFT_VERSION}\/${BUILD_NUMBER}\/download \r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"running curl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\"\r\n\r\nif [ -f ${SERVER_JARFILE} ]; then\r\n mv ${SERVER_JARFILE} ${SERVER_JARFILE}.old\r\nfi\r\n\r\ncurl -o ${SERVER_JARFILE} ${DOWNLOAD_URL}\r\n\r\nif [ ! -f server.properties ]; then\r\n echo -e \"Downloading MC server.properties\"\r\n curl -o server.properties https:\/\/raw.githubusercontent.com\/parkervcp\/eggs\/master\/minecraft\/java\/server.properties\r\nfi", "container": "debian:buster-slim", "entrypoint": "bash" } diff --git a/database/seeds/eggs/voice-servers/egg-mumble-server.json b/database/seeds/eggs/voice-servers/egg-mumble-server.json index e9215ca9f..4f3acf7df 100644 --- a/database/seeds/eggs/voice-servers/egg-mumble-server.json +++ b/database/seeds/eggs/voice-servers/egg-mumble-server.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-10-20T00:22:14+00:00", + "exported_at": "2020-10-25T22:34:06+00:00", "name": "Mumble Server", "author": "support@pterodactyl.io", "description": "Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.", @@ -17,7 +17,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# Mumble Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\nGITHUB_PACKAGE=mumble-voip\/mumble\r\nMATCH=murmur-static\r\n\r\napt update\r\napt install -y tar curl jq\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/tmp\r\n\r\nif [ -z \"${GITHUB_USER}\" ] && [ -z \"${GITHUB_OAUTH_TOKEN}\" ] ; then\r\n echo -e \"using anon api call\"\r\nelse\r\n echo -e \"user and oauth token set\"\r\n alias curl='curl -u ${GITHUB_USER}:${GITHUB_OAUTH_TOKEN} '\r\nfi\r\n\r\n## get release info and download links\r\nLATEST_JSON=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\/latest\")\r\nRELEASES=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\")\r\n\r\nif [ -z \"${VERSION}\" ] || [ \"${VERSION}\" == \"latest\" ]; then\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url | grep -m 1 -i ${MATCH})\r\nelse\r\n VERSION_CHECK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .tag_name')\r\n if [ \"${VERSION}\" == \"${VERSION_CHECK}\" ]; then\r\n DOWNLOAD_LINK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .assets[].browser_download_url' | grep -m 1 -i ${MATCH})\r\n else\r\n echo -e \"defaulting to latest release\"\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url)\r\n fi\r\nfi\r\n\r\ncurl -L ${DOWNLOAD_LINK} -o mumble-server.tar.bz2\r\n\r\ntar -xjvf mumble-server.tar.bz2\r\ncp -r murmur-static_x86-*\/* \/mnt\/server", + "script": "#!\/bin\/bash\r\n# Mumble Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\nGITHUB_PACKAGE=mumble-voip\/mumble\r\nMATCH=murmur-static\r\n\r\napt update\r\napt install -y tar curl jq bzip2\r\n\r\nif [ ! -d \/mnt\/server\/ ]; then\r\n mkdir \/mnt\/server\/\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\nif [ -z \"${GITHUB_USER}\" ] && [ -z \"${GITHUB_OAUTH_TOKEN}\" ] ; then\r\n echo -e \"using anon api call\"\r\nelse\r\n echo -e \"user and oauth token set\"\r\n alias curl='curl -u ${GITHUB_USER}:${GITHUB_OAUTH_TOKEN} '\r\nfi\r\n\r\n## get release info and download links\r\nLATEST_JSON=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\/latest\")\r\nRELEASES=$(curl --silent \"https:\/\/api.github.com\/repos\/${GITHUB_PACKAGE}\/releases\")\r\n\r\nif [ -z \"${VERSION}\" ] || [ \"${VERSION}\" == \"latest\" ]; then\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url | grep -m 1 -i ${MATCH})\r\nelse\r\n VERSION_CHECK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .tag_name')\r\n if [ \"${VERSION}\" == \"${VERSION_CHECK}\" ]; then\r\n DOWNLOAD_LINK=$(echo ${RELEASES} | jq -r --arg VERSION \"${VERSION}\" '.[] | select(.tag_name==$VERSION) | .assets[].browser_download_url' | grep -m 1 -i ${MATCH})\r\n else\r\n echo -e \"defaulting to latest release\"\r\n DOWNLOAD_LINK=$(echo ${LATEST_JSON} | jq .assets | jq -r .[].browser_download_url)\r\n fi\r\nfi\r\n\r\ncurl -L ${DOWNLOAD_LINK} | tar xjv --strip-components=1", "container": "debian:buster-slim", "entrypoint": "bash" } @@ -36,7 +36,7 @@ "name": "Server Version", "description": "Version of Mumble Server to download and use.", "env_variable": "MUMBLE_VERSION", - "default_value": "1.3.1", + "default_value": "latest", "user_viewable": true, "user_editable": true, "rules": "required|regex:\/^([0-9_\\.-]{5,8})$\/" diff --git a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json index aeba79316..d14d8eea9 100644 --- a/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json +++ b/database/seeds/eggs/voice-servers/egg-teamspeak3-server.json @@ -3,7 +3,7 @@ "meta": { "version": "PTDL_v1" }, - "exported_at": "2020-10-23T22:27:50+00:00", + "exported_at": "2020-10-25T22:24:15+00:00", "name": "Teamspeak3 Server", "author": "support@pterodactyl.io", "description": "VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.", @@ -17,7 +17,7 @@ }, "scripts": { "installation": { - "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y tar curl jq\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(wget https:\/\/teamspeak.com\/versions\/server.json -qO - | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\"\r\ncurl http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar xj --strip-components=1", + "script": "#!\/bin\/bash\r\n# TS3 Installation Script\r\n#\r\n# Server Files: \/mnt\/server\r\napt update\r\napt install -y tar curl jq bzip2\r\n\r\nif [ -z ${TS_VERSION} ] || [ ${TS_VERSION} == latest ]; then\r\n TS_VERSION=$(curl -sSL https:\/\/teamspeak.com\/versions\/server.json | jq -r '.linux.x86_64.version')\r\nfi\r\n\r\ncd \/mnt\/server\r\n\r\necho -e \"getting files from http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2\" \r\ncurl -L http:\/\/files.teamspeak-services.com\/releases\/server\/${TS_VERSION}\/teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2 | tar -xvj --strip-components=1\r\n\r\nrm teamspeak3-server_linux_amd64-${TS_VERSION}.tar.bz2", "container": "debian:buster-slim", "entrypoint": "bash" } From a4d3e7db1bec57676f440b578cac6b607d052e00 Mon Sep 17 00:00:00 2001 From: Griffin T Date: Mon, 26 Oct 2020 20:30:30 +0800 Subject: [PATCH 62/77] Add console command history. --- .../scripts/components/server/Console.tsx | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index e978f7998..516224a05 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useRef } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { ITerminalOptions, Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import { SearchAddon } from 'xterm-addon-search'; @@ -11,6 +11,7 @@ import tw, { theme as th } from 'twin.macro'; import 'xterm/css/xterm.css'; import useEventListener from '@/plugins/useEventListener'; import { debounce } from 'debounce'; +import { usePersistedState } from '@/plugins/usePersistedState'; const theme = { background: th`colors.black`.toString(), @@ -63,6 +64,9 @@ export default () => { const searchBar = new SearchBarAddon({ searchAddon }); const { connected, instance } = ServerContext.useStoreState(state => state.socket); const [ canSendCommands ] = usePermissions([ 'control.console' ]); + const serverId = ServerContext.useStoreState(state => state.server.data!.id); + const [ history, setHistory ] = usePersistedState(`${serverId}:command_history`, []); + const [ historyIndex, setHistoryIndex ] = useState(-1); const handleConsoleOutput = (line: string, prelude = false) => terminal.writeln( (prelude ? TERMINAL_PRELUDE : '') + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m', @@ -77,12 +81,28 @@ export default () => { ); const handleCommandKeydown = (e: React.KeyboardEvent) => { - if (e.key !== 'Enter' || (e.key === 'Enter' && e.currentTarget.value.length < 1)) { - return; + if (e.key === 'ArrowUp') { + const newIndex = Math.min(historyIndex + 1, history!.length - 1); + + setHistoryIndex(newIndex); + e.currentTarget.value = history![newIndex] || ''; } - instance && instance.send('send command', e.currentTarget.value); - e.currentTarget.value = ''; + if (e.key === 'ArrowDown') { + const newIndex = Math.max(historyIndex - 1, -1); + + setHistoryIndex(newIndex); + e.currentTarget.value = history![newIndex] || ''; + } + + const command = e.currentTarget.value; + if (e.key === 'Enter' && command.length > 0) { + setHistory(prevHistory => [ command, ...prevHistory! ]); + setHistoryIndex(-1); + + instance && instance.send('send command', command); + e.currentTarget.value = ''; + } }; useEffect(() => { From 200a78d77b806f6b3382739fb8846ed96501c87d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 26 Oct 2020 19:57:08 -0700 Subject: [PATCH 63/77] Don't allow null schedule names anymore; ref #2609 --- ...move_nullable_from_schedule_name_field.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php diff --git a/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php b/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php new file mode 100644 index 000000000..fc1f01baa --- /dev/null +++ b/database/migrations/2020_10_26_194904_remove_nullable_from_schedule_name_field.php @@ -0,0 +1,35 @@ +string('name')->nullable(false)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('schedules', function (Blueprint $table) { + $table->string('name')->nullable()->change(); + }); + } +} From bffec5b3dcb520ad1f2c9a80d4b7a11bd0f2a612 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 26 Oct 2020 20:16:39 -0700 Subject: [PATCH 64/77] Don't abort the entire schedule running process if one schedule encounters an exception; closes #2609 --- .../Schedule/ProcessRunnableCommand.php | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/app/Console/Commands/Schedule/ProcessRunnableCommand.php b/app/Console/Commands/Schedule/ProcessRunnableCommand.php index 4d5981fd3..310b2f3b1 100644 --- a/app/Console/Commands/Schedule/ProcessRunnableCommand.php +++ b/app/Console/Commands/Schedule/ProcessRunnableCommand.php @@ -2,8 +2,11 @@ namespace Pterodactyl\Console\Commands\Schedule; +use Throwable; +use Exception; use Illuminate\Console\Command; use Pterodactyl\Models\Schedule; +use Illuminate\Support\Facades\Log; use Pterodactyl\Services\Schedules\ProcessScheduleService; class ProcessRunnableCommand extends Command @@ -20,12 +23,8 @@ class ProcessRunnableCommand extends Command /** * Handle command execution. - * - * @param \Pterodactyl\Services\Schedules\ProcessScheduleService $service - * - * @throws \Throwable */ - public function handle(ProcessScheduleService $service) + public function handle() { $schedules = Schedule::query()->with('tasks') ->where('is_active', true) @@ -41,22 +40,40 @@ class ProcessRunnableCommand extends Command $bar = $this->output->createProgressBar(count($schedules)); foreach ($schedules as $schedule) { - if ($schedule->tasks->isNotEmpty()) { - $service->handle($schedule); - - if ($this->input->isInteractive()) { - $bar->clear(); - $this->line(trans('command/messages.schedule.output_line', [ - 'schedule' => $schedule->name, - 'hash' => $schedule->hashid, - ])); - } - } - + $bar->clear(); + $this->processSchedule($schedule); $bar->advance(); $bar->display(); } $this->line(''); } + + /** + * Processes a given schedule and logs and errors encountered the console output. This should + * never throw an exception out, otherwise you'll end up killing the entire run group causing + * any other schedules to not process correctly. + * + * @param \Pterodactyl\Models\Schedule $schedule + * @see https://github.com/pterodactyl/panel/issues/2609 + */ + protected function processSchedule(Schedule $schedule) + { + if ($schedule->tasks->isEmpty()) { + return; + } + + try { + $this->getLaravel()->make(ProcessScheduleService::class)->handle($schedule); + + $this->line(trans('command/messages.schedule.output_line', [ + 'schedule' => $schedule->name, + 'hash' => $schedule->hashid, + ])); + } catch (Throwable | Exception $exception) { + Log::error($exception, ['schedule_id' => $schedule->id]); + + $this->error("An error was encountered while processing Schedule #{$schedule->id}: " . $exception->getMessage()); + } + } } From 73b795fabab7cb78558e147857f5a312fc7b2e29 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 26 Oct 2020 20:54:15 -0700 Subject: [PATCH 65/77] Correctly reset a schedule if there is an exception during the run stage; closes #2550 --- app/Jobs/Schedule/RunTaskJob.php | 3 +- .../Schedules/ProcessScheduleService.php | 21 ++++++- .../Server/Schedule/ExecuteScheduleTest.php | 3 +- .../Schedules/ProcessScheduleServiceTest.php | 59 +++++++++++++++---- 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php index fe44058a0..8784a37cb 100644 --- a/app/Jobs/Schedule/RunTaskJob.php +++ b/app/Jobs/Schedule/RunTaskJob.php @@ -7,11 +7,12 @@ use Pterodactyl\Jobs\Job; use Carbon\CarbonImmutable; use Pterodactyl\Models\Task; use InvalidArgumentException; +use Pterodactyl\Models\Schedule; +use Illuminate\Support\Facades\Log; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\DispatchesJobs; -use Pterodactyl\Repositories\Eloquent\TaskRepository; use Pterodactyl\Services\Backups\InitiateBackupService; use Pterodactyl\Repositories\Wings\DaemonPowerRepository; use Pterodactyl\Repositories\Wings\DaemonCommandRepository; diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php index ec16bc36a..86775430b 100644 --- a/app/Services/Schedules/ProcessScheduleService.php +++ b/app/Services/Schedules/ProcessScheduleService.php @@ -2,6 +2,7 @@ namespace Pterodactyl\Services\Schedules; +use Exception; use Pterodactyl\Models\Schedule; use Illuminate\Contracts\Bus\Dispatcher; use Pterodactyl\Jobs\Schedule\RunTaskJob; @@ -60,8 +61,22 @@ class ProcessScheduleService $task->update(['is_queued' => true]); }); - $this->dispatcher->{$now ? 'dispatchNow' : 'dispatch'}( - (new RunTaskJob($task))->delay($task->time_offset) - ); + $job = new RunTaskJob($task); + + if (! $now) { + $this->dispatcher->dispatch($job->delay($task->time_offset)); + } else { + // When using dispatchNow the RunTaskJob::failed() function is not called automatically + // so we need to manually trigger it and then continue with the exception throw. + // + // @see https://github.com/pterodactyl/panel/issues/2550 + try { + $this->dispatcher->dispatchNow($job); + } catch (Exception $exception) { + $job->failed($exception); + + throw $exception; + } + } } } diff --git a/tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php b/tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php index 00c57d4a4..d83faae3c 100644 --- a/tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php +++ b/tests/Integration/Api/Client/Server/Schedule/ExecuteScheduleTest.php @@ -44,7 +44,8 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase $this->actingAs($user)->postJson($this->link($schedule, '/execute'))->assertStatus(Response::HTTP_ACCEPTED); Bus::assertDispatched(function (RunTaskJob $job) use ($task) { - $this->assertSame($task->time_offset, $job->delay); + // A task executed right now should not have any job delay associated with it. + $this->assertNull($job->delay); $this->assertSame($task->id, $job->task->id); return true; diff --git a/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php index a5a4f65f3..93c793d6c 100644 --- a/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php +++ b/tests/Integration/Services/Schedules/ProcessScheduleServiceTest.php @@ -3,6 +3,8 @@ namespace Pterodactyl\Tests\Integration\Services\Schedules; use Mockery; +use Exception; +use Carbon\CarbonImmutable; use Pterodactyl\Models\Task; use InvalidArgumentException; use Pterodactyl\Models\Schedule; @@ -61,7 +63,7 @@ class ProcessScheduleServiceTest extends IntegrationTestCase */ public function testJobCanBeDispatchedWithExpectedInitialDelay($now) { - $this->swap(Dispatcher::class, $dispatcher = Mockery::mock(Dispatcher::class)); + Bus::fake(); $server = $this->createServerModel(); @@ -71,12 +73,17 @@ class ProcessScheduleServiceTest extends IntegrationTestCase /** @var \Pterodactyl\Models\Task $task */ $task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'time_offset' => 10, 'sequence_id' => 1]); - $dispatcher->expects($now ? 'dispatchNow' : 'dispatch')->with(Mockery::on(function (RunTaskJob $job) use ($task) { - return $task->id === $job->task->id && $job->delay === 10; - })); - $this->getService()->handle($schedule, $now); + Bus::assertDispatched(RunTaskJob::class, function ($job) use ($now, $task) { + $this->assertInstanceOf(RunTaskJob::class, $job); + $this->assertSame($task->id, $job->task->id); + // Jobs using dispatchNow should not have a delay associated with them. + $this->assertSame($now ? null : 10, $job->delay); + + return true; + }); + $this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]); $this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]); } @@ -89,7 +96,7 @@ class ProcessScheduleServiceTest extends IntegrationTestCase */ public function testFirstSequenceTaskIsFound() { - $this->swap(Dispatcher::class, $dispatcher = Mockery::mock(Dispatcher::class)); + Bus::fake(); $server = $this->createServerModel(); /** @var \Pterodactyl\Models\Schedule $schedule */ @@ -100,18 +107,50 @@ class ProcessScheduleServiceTest extends IntegrationTestCase $task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]); $task3 = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]); - $dispatcher->expects('dispatch')->with(Mockery::on(function (RunTaskJob $job) use ($task) { - return $task->id === $job->task->id; - })); - $this->getService()->handle($schedule); + Bus::assertDispatched(RunTaskJob::class, function (RunTaskJob $job) use ($task) { + return $task->id === $job->task->id; + }); + $this->assertDatabaseHas('schedules', ['id' => $schedule->id, 'is_processing' => true]); $this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => true]); $this->assertDatabaseHas('tasks', ['id' => $task2->id, 'is_queued' => false]); $this->assertDatabaseHas('tasks', ['id' => $task3->id, 'is_queued' => false]); } + /** + * Tests that a task's processing state is reset correctly if using "dispatchNow" and there is + * an exception encountered while running it. + * + * @see https://github.com/pterodactyl/panel/issues/2550 + */ + public function testTaskDispatchedNowIsResetProperlyIfErrorIsEncountered() + { + $this->swap(Dispatcher::class, $dispatcher = Mockery::mock(Dispatcher::class)); + + $server = $this->createServerModel(); + /** @var \Pterodactyl\Models\Schedule $schedule */ + $schedule = factory(Schedule::class)->create(['server_id' => $server->id, 'last_run_at' => null]); + /** @var \Pterodactyl\Models\Task $task */ + $task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]); + + $dispatcher->expects('dispatchNow')->andThrows(new Exception('Test thrown exception')); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Test thrown exception'); + + $this->getService()->handle($schedule, true); + + $this->assertDatabaseHas('schedules', [ + 'id' => $schedule->id, + 'is_processing' => false, + 'last_run_at' => CarbonImmutable::now()->toAtomString(), + ]); + + $this->assertDatabaseHas('tasks', ['id' => $task->id, 'is_queued' => false]); + } + /** * @return array */ From 6e4261b3a7b3efb673cd71d8c9f3160a8b7959dd Mon Sep 17 00:00:00 2001 From: Griffin T Date: Tue, 27 Oct 2020 18:19:33 +0800 Subject: [PATCH 66/77] Limit per server command history to 32. --- resources/scripts/components/server/Console.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 516224a05..abf423d34 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -97,7 +97,7 @@ export default () => { const command = e.currentTarget.value; if (e.key === 'Enter' && command.length > 0) { - setHistory(prevHistory => [ command, ...prevHistory! ]); + setHistory(prevHistory => [ command, ...prevHistory! ].slice(0, 32)); setHistoryIndex(-1); instance && instance.send('send command', command); From fcdf154a12f7b87617d0d49205fa417ee8e8a773 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 29 Oct 2020 19:39:55 -0600 Subject: [PATCH 67/77] Add more environment variables for S3 filesystem options --- config/backups.php | 2 ++ config/filesystems.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/backups.php b/config/backups.php index fde856215..ec9bd175e 100644 --- a/config/backups.php +++ b/config/backups.php @@ -33,6 +33,8 @@ return [ // backup for that server lives within that folder. 'prefix' => env('AWS_BACKUPS_BUCKET') ?? '', + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 'use_accelerate_endpoint' => env('AWS_BACKUPS_USE_ACCELERATE', false), ], ], diff --git a/config/filesystems.php b/config/filesystems.php index 809742bed..bffcc6514 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -58,6 +58,8 @@ return [ 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), ], ], ]; From 48013eddcc55a97a8012aab637c109e5fcd20d96 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 30 Oct 2020 11:02:29 -0600 Subject: [PATCH 68/77] admin: update transfer server box on manage tab --- .../views/admin/servers/view/manage.blade.php | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/resources/views/admin/servers/view/manage.blade.php b/resources/views/admin/servers/view/manage.blade.php index aad14209b..93d7b6390 100644 --- a/resources/views/admin/servers/view/manage.blade.php +++ b/resources/views/admin/servers/view/manage.blade.php @@ -96,30 +96,33 @@
@endif - @if($canTransfer) -
-
-
-

Transfer Server

-
-
-

- Hopefully, you will soon be able to move servers around without needing to do a bunch of confusing - operations manually and it will work fluidly and with no problems. -

-
-