From 854270e4e8704891597b495aa2a6087dc35a67e0 Mon Sep 17 00:00:00 2001 From: Alex <11708877+acl1704@users.noreply.github.com> Date: Sun, 6 Dec 2020 17:55:41 -0800 Subject: [PATCH 01/21] fix required version variable fixes required FORGE_VERSION and renames minecraft version variable to avoid confusion --- database/seeds/eggs/minecraft/egg-forge-minecraft.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database/seeds/eggs/minecraft/egg-forge-minecraft.json b/database/seeds/eggs/minecraft/egg-forge-minecraft.json index 2020d53a4..75a23d147 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-11-03T04:22:56+00:00", + "exported_at": "2020-12-06T17:39:27-08: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.", @@ -34,7 +34,7 @@ "rules": "required|regex:\/^([\\w\\d._-]+)(\\.jar)$\/" }, { - "name": "Forge version", + "name": "Minecraft Version", "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", @@ -58,7 +58,7 @@ "default_value": "", "user_viewable": true, "user_editable": true, - "rules": "required|string|max:20" + "rules": "nullable|string|max:20" } ] -} +} \ No newline at end of file From 2fafd4fbbfe82cdf9d80a0c3cc7cc063ca8b2379 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 6 Dec 2020 20:42:05 -0700 Subject: [PATCH 02/21] Add mb-4 to File Manager Breadcrumb when editing a file --- .../scripts/components/server/files/FileEditContainer.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/scripts/components/server/files/FileEditContainer.tsx b/resources/scripts/components/server/files/FileEditContainer.tsx index 4dd519f8d..bf12909b8 100644 --- a/resources/scripts/components/server/files/FileEditContainer.tsx +++ b/resources/scripts/components/server/files/FileEditContainer.tsx @@ -87,7 +87,9 @@ export default () => { - +
+ +
{hash.replace(/^#/, '').endsWith('.pteroignore') &&
From 911d85c230993a8717cd87007a8b13a2378b6e0c Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 7 Dec 2020 09:31:44 -0700 Subject: [PATCH 03/21] Delete the oldest backup, not the newest backup, closes #2800 --- app/Services/Backups/InitiateBackupService.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php index 210e92342..0857478ba 100644 --- a/app/Services/Backups/InitiateBackupService.php +++ b/app/Services/Backups/InitiateBackupService.php @@ -13,7 +13,6 @@ use Pterodactyl\Repositories\Eloquent\BackupRepository; use Pterodactyl\Repositories\Wings\DaemonBackupRepository; use Pterodactyl\Exceptions\Service\Backup\TooManyBackupsException; use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; -use Pterodactyl\Services\Backups\DeleteBackupService; class InitiateBackupService { @@ -124,8 +123,11 @@ class InitiateBackupService throw new TooManyBackupsException($server->backup_limit); } - // Remove oldest backup - $oldestBackup = $server->backups()->where('is_successful', true)->orderByDesc('created_at')->first(); + // Get the oldest backup the server has. + /** @var \Pterodactyl\Models\Backup $oldestBackup */ + $oldestBackup = $server->backups()->where('is_successful', true)->orderBy('created_at')->first(); + + // Delete the oldest backup. $this->deleteBackupService->handle($oldestBackup); } From 6e7dd36a8956c6f90c06855eef8cddb55341d5ef Mon Sep 17 00:00:00 2001 From: Omar Kamel <30291302+TekExplorer@users.noreply.github.com> Date: Tue, 8 Dec 2020 22:25:07 -0500 Subject: [PATCH 04/21] Correct Typo in Bug Report template installating > installing smh lol. I find the typo funny but also let's just fix that real quick. fixes #2808 --- .github/ISSUE_TEMPLATE/---bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 640d6def5..f707992d2 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -6,7 +6,7 @@ about: For reporting code or design bugs with the software. DO NOT REPORT APACHE 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 +For assistance installing this software, as well as debugging issues with dependencies, please use our discord server: https://discord.gg/pterodactyl You MUST complete all of the below information when reporting a bug, failure to do so will result in closure of your issue. PLEASE stop spamming our tracker with "bugs" that are not related to this project. From f31a1288af3baa130559cd5cf37a09186f8f8bf2 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 8 Dec 2020 19:54:32 -0800 Subject: [PATCH 05/21] Don't show file mode in the manager --- resources/scripts/components/server/files/FileObjectRow.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/resources/scripts/components/server/files/FileObjectRow.tsx b/resources/scripts/components/server/files/FileObjectRow.tsx index 89a080759..65e784828 100644 --- a/resources/scripts/components/server/files/FileObjectRow.tsx +++ b/resources/scripts/components/server/files/FileObjectRow.tsx @@ -64,11 +64,7 @@ const FileObjectRow = ({ file }: { file: FileObject }) => ( > - - -
+
{file.isFile ? : From 24417ac5168f8d0ad886a3b12f5cc95b3be15edb Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 8 Dec 2020 20:34:31 -0800 Subject: [PATCH 06/21] Slightly less obtuse way of handling this little checkbox --- .../server/files/FileManagerBreadcrumbs.tsx | 27 +++---------------- .../server/files/FileManagerContainer.tsx | 19 +++++++++++-- .../scripts/plugins/useFileManagerSwr.ts | 2 ++ 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx index 9b1596f5c..00dcd949d 100644 --- a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx +++ b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx @@ -1,26 +1,20 @@ import React, { useEffect, useState } from 'react'; import { ServerContext } from '@/state/server'; -import { NavLink, useRouteMatch } from 'react-router-dom'; +import { NavLink } from 'react-router-dom'; import { cleanDirectoryPath } from '@/helpers'; import tw from 'twin.macro'; -import { FileActionCheckbox } from '@/components/server/files/SelectFileCheckbox'; -import useFileManagerSwr from '@/plugins/useFileManagerSwr'; interface Props { + renderLeft?: JSX.Element; withinFileEditor?: boolean; isNewFile?: boolean; } -export default ({ withinFileEditor, isNewFile }: Props) => { +export default ({ renderLeft, withinFileEditor, isNewFile }: Props) => { const [ file, setFile ] = useState(null); - const { params } = useRouteMatch>(); const id = ServerContext.useStoreState(state => state.server.data!.id); const directory = ServerContext.useStoreState(state => state.files.directory); - const { data: files } = useFileManagerSwr(); - const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles); - const selectedFilesLength = ServerContext.useStoreState(state => state.files.selectedFiles.length); - useEffect(() => { const parts = cleanDirectoryPath(window.location.hash).split('/'); @@ -39,22 +33,9 @@ export default ({ withinFileEditor, isNewFile }: Props) => { return { name: directory, path: `/${dirs.slice(0, index + 1).join('/')}` }; }); - const onSelectAllClick = (e: React.ChangeEvent) => { - setSelectedFiles(e.currentTarget.checked ? (files?.map(file => file.name) || []) : []); - }; - return (
- {(files && files.length > 0 && !params?.action) ? - - : -
- } + {renderLeft ||
} /home/ { return files.sort((a, b) => a.name.localeCompare(b.name)) @@ -31,7 +32,9 @@ export default () => { const directory = ServerContext.useStoreState(state => state.files.directory); const clearFlashes = useStoreActions(actions => actions.flashes.clearFlashes); const setDirectory = ServerContext.useStoreActions(actions => actions.files.setDirectory); + const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles); + const selectedFilesLength = ServerContext.useStoreState(state => state.files.selectedFiles.length); useEffect(() => { clearFlashes('files'); @@ -43,6 +46,10 @@ export default () => { mutate(); }, [ directory ]); + const onSelectAllClick = (e: React.ChangeEvent) => { + setSelectedFiles(e.currentTarget.checked ? (files?.map(file => file.name) || []) : []); + }; + if (error) { return ( mutate()}/> @@ -53,9 +60,17 @@ export default () => {
- + + } + /> -
diff --git a/resources/scripts/plugins/useFileManagerSwr.ts b/resources/scripts/plugins/useFileManagerSwr.ts index 390ce839b..7d1bb7edc 100644 --- a/resources/scripts/plugins/useFileManagerSwr.ts +++ b/resources/scripts/plugins/useFileManagerSwr.ts @@ -7,6 +7,8 @@ export default () => { const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const directory = ServerContext.useStoreState(state => state.files.directory); + console.log('firing'); + return useSWR( `${uuid}:files:${directory}`, () => loadDirectory(uuid, cleanDirectoryPath(directory)), From 3e65a2d05512da7c77c90d9f2768a3538f99f26e Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 8 Dec 2020 21:24:17 -0800 Subject: [PATCH 07/21] Pass one at unfucking the stupid file encoding issues --- .../Api/Client/Servers/FileController.php | 4 +-- .../Daemon/FileObjectTransformer.php | 2 +- .../api/server/files/getFileContents.ts | 2 +- .../scripts/api/server/files/loadDirectory.ts | 4 ++- .../api/server/files/saveFileContents.ts | 2 +- .../server/files/FileEditContainer.tsx | 2 +- .../server/files/FileManagerBreadcrumbs.tsx | 28 ++++++++++++++----- .../server/files/FileManagerContainer.tsx | 2 +- .../components/server/files/FileObjectRow.tsx | 4 +-- .../scripts/plugins/useFileManagerSwr.ts | 2 -- 10 files changed, 33 insertions(+), 19 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/FileController.php b/app/Http/Controllers/Api/Client/Servers/FileController.php index 0e3a62f2e..a4b0ae283 100644 --- a/app/Http/Controllers/Api/Client/Servers/FileController.php +++ b/app/Http/Controllers/Api/Client/Servers/FileController.php @@ -72,7 +72,7 @@ class FileController extends ClientApiController { $contents = $this->fileRepository ->setServer($server) - ->getDirectory($this->encode($request->get('directory') ?? '/')); + ->getDirectory(rawurldecode($request->get('directory') ?? '/')); return $this->fractal->collection($contents) ->transformWith($this->getTransformer(FileObjectTransformer::class)) @@ -93,7 +93,7 @@ class FileController extends ClientApiController { return new Response( $this->fileRepository->setServer($server)->getContent( - $this->encode($request->get('file')), config('pterodactyl.files.max_edit_size') + rawurldecode($request->get('file')), config('pterodactyl.files.max_edit_size') ), Response::HTTP_OK, ['Content-Type' => 'text/plain'] diff --git a/app/Transformers/Daemon/FileObjectTransformer.php b/app/Transformers/Daemon/FileObjectTransformer.php index f19d9028c..b2c1deed3 100644 --- a/app/Transformers/Daemon/FileObjectTransformer.php +++ b/app/Transformers/Daemon/FileObjectTransformer.php @@ -23,7 +23,7 @@ class FileObjectTransformer extends BaseDaemonTransformer public function transform(array $item) { return [ - 'name' => Arr::get($item, 'name'), + 'name' => rawurlencode(Arr::get($item, 'name')), 'mode' => Arr::get($item, 'mode'), 'mode_bits' => Arr::get($item, 'mode_bits'), 'size' => Arr::get($item, 'size'), diff --git a/resources/scripts/api/server/files/getFileContents.ts b/resources/scripts/api/server/files/getFileContents.ts index da380362d..ef25b1dbc 100644 --- a/resources/scripts/api/server/files/getFileContents.ts +++ b/resources/scripts/api/server/files/getFileContents.ts @@ -3,7 +3,7 @@ import http from '@/api/http'; export default (server: string, file: string): Promise => { return new Promise((resolve, reject) => { http.get(`/api/client/servers/${server}/files/contents`, { - params: { file: encodeURI(decodeURI(file)) }, + params: { file }, transformResponse: res => res, responseType: 'text', }) diff --git a/resources/scripts/api/server/files/loadDirectory.ts b/resources/scripts/api/server/files/loadDirectory.ts index 52bf8853e..985b5c94d 100644 --- a/resources/scripts/api/server/files/loadDirectory.ts +++ b/resources/scripts/api/server/files/loadDirectory.ts @@ -18,7 +18,9 @@ export interface FileObject { export default async (uuid: string, directory?: string): Promise => { const { data } = await http.get(`/api/client/servers/${uuid}/files/list`, { - params: { directory: encodeURI(directory ?? '/') }, + // At this point the directory is still encoded so we need to decode it since axios + // will automatically re-encode this value before sending it along in the request. + params: { directory: decodeURI(directory ?? '/') }, }); return (data.data || []).map(rawDataToFileObject); diff --git a/resources/scripts/api/server/files/saveFileContents.ts b/resources/scripts/api/server/files/saveFileContents.ts index 7f6f44efc..b97e60a6b 100644 --- a/resources/scripts/api/server/files/saveFileContents.ts +++ b/resources/scripts/api/server/files/saveFileContents.ts @@ -2,7 +2,7 @@ import http from '@/api/http'; export default async (uuid: string, file: string, content: string): Promise => { await http.post(`/api/client/servers/${uuid}/files/write`, content, { - params: { file: encodeURI(decodeURI(file)) }, + params: { file }, headers: { 'Content-Type': 'text/plain', }, diff --git a/resources/scripts/components/server/files/FileEditContainer.tsx b/resources/scripts/components/server/files/FileEditContainer.tsx index bf12909b8..f26baf8b0 100644 --- a/resources/scripts/components/server/files/FileEditContainer.tsx +++ b/resources/scripts/components/server/files/FileEditContainer.tsx @@ -61,7 +61,7 @@ export default () => { setLoading(true); clearFlashes('files:view'); fetchFileContent() - .then(content => saveFileContents(uuid, name || hash.replace(/^#/, ''), content)) + .then(content => saveFileContents(uuid, name || decodeURI(hash.replace(/^#/, '')), content)) .then(() => { if (name) { history.push(`/server/${id}/files/edit#/${name}`); diff --git a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx index 00dcd949d..7353edadf 100644 --- a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx +++ b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { ServerContext } from '@/state/server'; -import { NavLink } from 'react-router-dom'; +import { NavLink, useLocation } from 'react-router-dom'; import { cleanDirectoryPath } from '@/helpers'; import tw from 'twin.macro'; @@ -14,14 +14,28 @@ export default ({ renderLeft, withinFileEditor, isNewFile }: Props) => { const [ file, setFile ] = useState(null); const id = ServerContext.useStoreState(state => state.server.data!.id); const directory = ServerContext.useStoreState(state => state.files.directory); + const { hash } = useLocation(); useEffect(() => { - const parts = cleanDirectoryPath(window.location.hash).split('/'); + let pathHash = cleanDirectoryPath(hash); + try { + pathHash = decodeURI(pathHash); + } catch (e) { + console.warn('Error decoding URL parts in hash:', e); + } if (withinFileEditor && !isNewFile) { - setFile(parts.pop() || null); + let name = pathHash.split('/').pop() || null; + if (name) { + try { + name = decodeURIComponent(name); + } catch (e) { + console.warn('Error decoding filename:', e); + } + } + setFile(name); } - }, [ withinFileEditor, isNewFile ]); + }, [ withinFileEditor, isNewFile, hash ]); const breadcrumbs = (): { name: string; path?: string }[] => directory.split('/') .filter(directory => !!directory) @@ -51,16 +65,16 @@ export default ({ renderLeft, withinFileEditor, isNewFile }: Props) => { to={`/server/${id}/files#${crumb.path}`} css={tw`px-1 text-neutral-200 no-underline hover:text-neutral-100`} > - {crumb.name} + {decodeURIComponent(crumb.name)} / : - {crumb.name} + {decodeURIComponent(crumb.name)} )) } {file && - {decodeURI(file)} + {file} }
diff --git a/resources/scripts/components/server/files/FileManagerContainer.tsx b/resources/scripts/components/server/files/FileManagerContainer.tsx index 692f4d9ea..613a1baaa 100644 --- a/resources/scripts/components/server/files/FileManagerContainer.tsx +++ b/resources/scripts/components/server/files/FileManagerContainer.tsx @@ -39,7 +39,7 @@ export default () => { useEffect(() => { clearFlashes('files'); setSelectedFiles([]); - setDirectory(hash.length > 0 ? decodeURI(hash) : '/'); + setDirectory(hash.length > 0 ? hash : '/'); }, [ hash ]); useEffect(() => { diff --git a/resources/scripts/components/server/files/FileObjectRow.tsx b/resources/scripts/components/server/files/FileObjectRow.tsx index 65e784828..fd18bd7ba 100644 --- a/resources/scripts/components/server/files/FileObjectRow.tsx +++ b/resources/scripts/components/server/files/FileObjectRow.tsx @@ -24,7 +24,7 @@ const Clickable: React.FC<{ file: FileObject }> = memo(({ file, children }) => { const history = useHistory(); const match = useRouteMatch(); - const destination = cleanDirectoryPath(`${directory}/${file.name}`).split('/').map(v => encodeURI(v)).join('/'); + const destination = cleanDirectoryPath(`${directory}/${file.name}`).split('/').join('/'); const onRowClick = (e: React.MouseEvent) => { // Don't rely on the onClick to work with the generated URL. Because of the way this @@ -72,7 +72,7 @@ const FileObjectRow = ({ file }: { file: FileObject }) => ( }
- {file.name} + {decodeURIComponent(file.name)}
{file.isFile &&