From ec99859590336d6ce71b8be9ba45727a1f63d96d Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Sat, 28 Nov 2020 14:48:35 -0500 Subject: [PATCH 01/10] Don't show delete button if its own user Don't show the delete button if a sub-user is looking at sub-users, as they cannot delete themselves. --- resources/scripts/components/server/users/UserRow.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx index aa5e55a39..656a69510 100644 --- a/resources/scripts/components/server/users/UserRow.tsx +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -61,7 +61,9 @@ export default ({ subuser }: Props) => { } + {subuser.uuid !== uuid && + } ); From 947fdf72ed12ddbf5aea3fd04d4d969ad78db2ee Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Sun, 29 Nov 2020 18:13:20 -0500 Subject: [PATCH 02/10] Update UserRow.tsx --- .../components/server/users/UserRow.tsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx index 656a69510..08adecd78 100644 --- a/resources/scripts/components/server/users/UserRow.tsx +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -48,23 +48,23 @@ export default ({ subuser }: Props) => {

Permissions

- - {subuser.uuid !== uuid && - - } - - - {subuser.uuid !== uuid && - - } - + {subuser.uuid !== uuid && + + + + } + {subuser.uuid !== uuid && + + + + } ); }; From 76d671aa8aaf6b370f2a046613c339b1bf099bc7 Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Sun, 29 Nov 2020 19:35:16 -0500 Subject: [PATCH 03/10] Update UserRow.tsx --- .../components/server/users/UserRow.tsx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx index 08adecd78..ec20baa8e 100644 --- a/resources/scripts/components/server/users/UserRow.tsx +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -49,21 +49,21 @@ export default ({ subuser }: Props) => {

Permissions

{subuser.uuid !== uuid && - - - - } - {subuser.uuid !== uuid && - - - + <> + + + + + + + } ); From de1f7ea90649624ec1c54615b91ca6d4bbce79d2 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 3 Dec 2020 21:10:41 -0700 Subject: [PATCH 04/10] Move file manager buttons beside the breadcrumb, other tweaks --- .../components/elements/SubNavigation.tsx | 15 +++---- .../server/files/FileManagerBreadcrumbs.tsx | 2 +- .../server/files/FileManagerContainer.tsx | 41 ++++++++++--------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/resources/scripts/components/elements/SubNavigation.tsx b/resources/scripts/components/elements/SubNavigation.tsx index 6b8379697..8da2cf44a 100644 --- a/resources/scripts/components/elements/SubNavigation.tsx +++ b/resources/scripts/components/elements/SubNavigation.tsx @@ -5,23 +5,24 @@ import config from '../../../../tailwind.config'; const SubNavigation = styled.div` ${tw`w-full bg-neutral-700 shadow overflow-x-auto`}; - + & > div { ${tw`flex items-center text-sm mx-auto px-2`}; max-width: 1200px; - + & > a, & > div { ${tw`inline-block py-3 px-4 text-neutral-300 no-underline whitespace-no-wrap transition-all duration-150`}; - + &:not(:first-of-type) { ${tw`ml-2`}; } - - &:active, &:hover { + + &:hover { ${tw`text-neutral-100`}; } - - &:active, &:hover, &.active { + + &:active, &.active { + ${tw`text-neutral-100`}; box-shadow: inset 0 -2px ${config.theme.colors.cyan['500']}; } } diff --git a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx index 11b7abde7..9b1596f5c 100644 --- a/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx +++ b/resources/scripts/components/server/files/FileManagerBreadcrumbs.tsx @@ -44,7 +44,7 @@ export default ({ withinFileEditor, isNewFile }: Props) => { }; return ( -
+
{(files && files.length > 0 && !params?.action) ? { return ( - - - +
+ + + + + + +
+ + + + + +
+
+
+
{ !files ? @@ -83,22 +102,6 @@ export default () => {
} - - -
- - - - - -
-
-
} From 8611ebb2d6ee6ced6d4b698b41ebdefdd97266f5 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 29 Nov 2020 13:49:29 -0700 Subject: [PATCH 05/10] Add /api/client/servers/{server}/files/chmod endpoint --- .../Api/Client/Servers/FileController.php | 20 ++++++++++++ .../Servers/Files/ChmodFilesRequest.php | 31 +++++++++++++++++++ .../Wings/DaemonFileRepository.php | 28 +++++++++++++++++ routes/api-client.php | 1 + 4 files changed, 80 insertions(+) create mode 100644 app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php diff --git a/app/Http/Controllers/Api/Client/Servers/FileController.php b/app/Http/Controllers/Api/Client/Servers/FileController.php index 9e705651b..0e3a62f2e 100644 --- a/app/Http/Controllers/Api/Client/Servers/FileController.php +++ b/app/Http/Controllers/Api/Client/Servers/FileController.php @@ -16,6 +16,7 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CopyFileRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\ListFilesRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DeleteFileRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest; +use Pterodactyl\Http\Requests\Api\Client\Servers\Files\ChmodFilesRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CompressFilesRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DecompressFilesRequest; @@ -263,6 +264,25 @@ class FileController extends ClientApiController return new JsonResponse([], Response::HTTP_NO_CONTENT); } + /** + * Updates file permissions for file(s) in the given root directory. + * + * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\ChmodFilesRequest $request + * @param \Pterodactyl\Models\Server $server + * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function chmod(ChmodFilesRequest $request, Server $server): JsonResponse + { + $this->fileRepository->setServer($server) + ->chmodFiles( + $request->input('root'), $request->input('files') + ); + + return new JsonResponse([], Response::HTTP_NO_CONTENT); + } + /** * Encodes a given file name & path in a format that should work for a good majority * of file names without too much confusing logic. diff --git a/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php new file mode 100644 index 000000000..ed6540219 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php @@ -0,0 +1,31 @@ + 'required|nullable|string', + 'files' => 'required|array', + 'files.file' => 'string', + 'files.mode' => 'integer', + ]; + } +} diff --git a/app/Repositories/Wings/DaemonFileRepository.php b/app/Repositories/Wings/DaemonFileRepository.php index 1ae424585..c36a8abb0 100644 --- a/app/Repositories/Wings/DaemonFileRepository.php +++ b/app/Repositories/Wings/DaemonFileRepository.php @@ -269,4 +269,32 @@ class DaemonFileRepository extends DaemonRepository throw new DaemonConnectionException($exception); } } + + /** + * Chmods the given files. + * + * @param string|null $root + * @param array $files + * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function chmodFiles(?string $root, array $files): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/chmod', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } } diff --git a/routes/api-client.php b/routes/api-client.php index 170828be1..35a2938dd 100644 --- a/routes/api-client.php +++ b/routes/api-client.php @@ -64,6 +64,7 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ Route::post('/decompress', 'Servers\FileController@decompress'); Route::post('/delete', 'Servers\FileController@delete'); Route::post('/create-folder', 'Servers\FileController@create'); + Route::post('/chmod', 'Servers\FileController@chmod'); Route::get('/upload', 'Servers\FileUploadController'); }); From ed5613e20770c92f9006549f1687e549d9921a73 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 29 Nov 2020 14:46:35 -0700 Subject: [PATCH 06/10] Show file mode on file listing, add ability to change file mode --- .../Daemon/FileObjectTransformer.php | 1 + .../scripts/api/server/files/chmodFiles.ts | 14 ++++ .../scripts/api/server/files/loadDirectory.ts | 1 + resources/scripts/api/transformers.ts | 1 + .../server/files/ChmodFileModal.tsx | 75 +++++++++++++++++++ .../server/files/FileDropdownMenu.tsx | 30 +++++--- .../components/server/files/FileObjectRow.tsx | 6 +- .../server/files/NewDirectoryButton.tsx | 3 +- resources/scripts/helpers.ts | 30 ++++++++ 9 files changed, 150 insertions(+), 11 deletions(-) create mode 100644 resources/scripts/api/server/files/chmodFiles.ts create mode 100644 resources/scripts/components/server/files/ChmodFileModal.tsx diff --git a/app/Transformers/Daemon/FileObjectTransformer.php b/app/Transformers/Daemon/FileObjectTransformer.php index 84fcaf2d4..f19d9028c 100644 --- a/app/Transformers/Daemon/FileObjectTransformer.php +++ b/app/Transformers/Daemon/FileObjectTransformer.php @@ -25,6 +25,7 @@ class FileObjectTransformer extends BaseDaemonTransformer return [ 'name' => Arr::get($item, 'name'), 'mode' => Arr::get($item, 'mode'), + 'mode_bits' => Arr::get($item, 'mode_bits'), 'size' => Arr::get($item, 'size'), 'is_file' => Arr::get($item, 'file', true), 'is_symlink' => Arr::get($item, 'symlink', false), diff --git a/resources/scripts/api/server/files/chmodFiles.ts b/resources/scripts/api/server/files/chmodFiles.ts new file mode 100644 index 000000000..8bafd6d6e --- /dev/null +++ b/resources/scripts/api/server/files/chmodFiles.ts @@ -0,0 +1,14 @@ +import http from '@/api/http'; + +interface Data { + file: string; + mode: string; +} + +export default (uuid: string, directory: string, files: Data[]): Promise => { + return new Promise((resolve, reject) => { + http.post(`/api/client/servers/${uuid}/files/chmod`, { root: directory, files }) + .then(() => resolve()) + .catch(reject); + }); +}; diff --git a/resources/scripts/api/server/files/loadDirectory.ts b/resources/scripts/api/server/files/loadDirectory.ts index d29cc1605..52bf8853e 100644 --- a/resources/scripts/api/server/files/loadDirectory.ts +++ b/resources/scripts/api/server/files/loadDirectory.ts @@ -5,6 +5,7 @@ export interface FileObject { key: string; name: string; mode: string; + modeBits: string, size: number; isFile: boolean; isSymlink: boolean; diff --git a/resources/scripts/api/transformers.ts b/resources/scripts/api/transformers.ts index f17787e03..e08c00076 100644 --- a/resources/scripts/api/transformers.ts +++ b/resources/scripts/api/transformers.ts @@ -16,6 +16,7 @@ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({ key: `${data.attributes.is_file ? 'file' : 'dir'}_${data.attributes.name}`, name: data.attributes.name, mode: data.attributes.mode, + modeBits: data.attributes.mode_bits, size: Number(data.attributes.size), isFile: data.attributes.is_file, isSymlink: data.attributes.is_symlink, diff --git a/resources/scripts/components/server/files/ChmodFileModal.tsx b/resources/scripts/components/server/files/ChmodFileModal.tsx new file mode 100644 index 000000000..a7997469b --- /dev/null +++ b/resources/scripts/components/server/files/ChmodFileModal.tsx @@ -0,0 +1,75 @@ +import { fileBitsToString } from '@/helpers'; +import useFileManagerSwr from '@/plugins/useFileManagerSwr'; +import React from 'react'; +import Modal, { RequiredModalProps } from '@/components/elements/Modal'; +import { Form, Formik, FormikHelpers } from 'formik'; +import Field from '@/components/elements/Field'; +import chmodFiles from '@/api/server/files/chmodFiles'; +import { ServerContext } from '@/state/server'; +import tw from 'twin.macro'; +import Button from '@/components/elements/Button'; +import useFlash from '@/plugins/useFlash'; + +interface FormikValues { + mode: string; +} + +interface File { + file: string, + mode: string, +} + +type OwnProps = RequiredModalProps & { files: File[] }; + +const ChmodFileModal = ({ files, ...props }: OwnProps) => { + const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const { mutate } = useFileManagerSwr(); + const { clearFlashes, clearAndAddHttpError } = useFlash(); + const directory = ServerContext.useStoreState(state => state.files.directory); + const setSelectedFiles = ServerContext.useStoreActions(actions => actions.files.setSelectedFiles); + + const submit = ({ mode }: FormikValues, { setSubmitting }: FormikHelpers) => { + clearFlashes('files'); + + mutate(data => data.map(f => f.name === files[0].file ? { ...f, mode: fileBitsToString(mode, !f.isFile), modeBits: mode } : f), false); + + const data = files.map(f => ({ file: f.file, mode: mode })); + + chmodFiles(uuid, directory, data) + .then((): Promise => files.length > 0 ? mutate() : Promise.resolve()) + .then(() => setSelectedFiles([])) + .catch(error => { + mutate(); + setSubmitting(false); + clearAndAddHttpError({ key: 'files', error }); + }) + .then(() => props.onDismissed()); + }; + + return ( + 1 ? '' : (files[0].mode || '') }}> + {({ isSubmitting }) => ( + +
+
+
+ +
+
+ +
+
+
+
+ )} +
+ ); +}; + +export default ChmodFileModal; diff --git a/resources/scripts/components/server/files/FileDropdownMenu.tsx b/resources/scripts/components/server/files/FileDropdownMenu.tsx index 48d53eeb2..603ed80ea 100644 --- a/resources/scripts/components/server/files/FileDropdownMenu.tsx +++ b/resources/scripts/components/server/files/FileDropdownMenu.tsx @@ -5,6 +5,7 @@ import { faCopy, faEllipsisH, faFileArchive, + faFileCode, faFileDownload, faLevelUpAlt, faPencilAlt, @@ -30,8 +31,9 @@ import compressFiles from '@/api/server/files/compressFiles'; import decompressFiles from '@/api/server/files/decompressFiles'; import isEqual from 'react-fast-compare'; import ConfirmationModal from '@/components/elements/ConfirmationModal'; +import ChmodFileModal from '@/components/server/files/ChmodFileModal'; -type ModalType = 'rename' | 'move'; +type ModalType = 'rename' | 'move' | 'chmod'; const StyledRow = styled.div<{ $danger?: boolean }>` ${tw`p-2 flex items-center rounded`}; @@ -140,14 +142,23 @@ const FileDropdownMenu = ({ file }: { file: FileObject }) => { renderToggle={onClick => (
- {!!modal && - setModal(null)} - /> + {modal ? + modal === 'chmod' ? + setModal(null)} + /> + : + setModal(null)} + /> + : null }
@@ -156,6 +167,7 @@ const FileDropdownMenu = ({ file }: { file: FileObject }) => { setModal('rename')} icon={faPencilAlt} title={'Rename'}/> setModal('move')} icon={faLevelUpAlt} title={'Move'}/> + setModal('chmod')} icon={faFileCode} title={'Permissions'}/> {file.isFile && diff --git a/resources/scripts/components/server/files/FileObjectRow.tsx b/resources/scripts/components/server/files/FileObjectRow.tsx index fccef1ec6..89a080759 100644 --- a/resources/scripts/components/server/files/FileObjectRow.tsx +++ b/resources/scripts/components/server/files/FileObjectRow.tsx @@ -64,7 +64,11 @@ const FileObjectRow = ({ file }: { file: FileObject }) => ( > -
+ + +
{file.isFile ? : diff --git a/resources/scripts/components/server/files/NewDirectoryButton.tsx b/resources/scripts/components/server/files/NewDirectoryButton.tsx index dc91377bc..56b58e17e 100644 --- a/resources/scripts/components/server/files/NewDirectoryButton.tsx +++ b/resources/scripts/components/server/files/NewDirectoryButton.tsx @@ -25,7 +25,8 @@ const schema = object().shape({ const generateDirectoryData = (name: string): FileObject => ({ key: `dir_${name.split('/', 1)[0] ?? name}`, name: name.replace(/^(\/*)/, '').split('/', 1)[0] ?? name, - mode: '0644', + mode: 'drwxr-xr-x', + modeBits: '0755', size: 0, isFile: false, isSymlink: false, diff --git a/resources/scripts/helpers.ts b/resources/scripts/helpers.ts index 84358193b..fdeef7ecd 100644 --- a/resources/scripts/helpers.ts +++ b/resources/scripts/helpers.ts @@ -20,3 +20,33 @@ export const randomInt = (low: number, high: number) => Math.floor(Math.random() export const cleanDirectoryPath = (path: string) => path.replace(/(^#\/*)|(\/(\/*))|(^$)/g, '/'); export const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(); + +export function fileBitsToString (mode: string, directory: boolean): string { + const m = parseInt(mode, 8); + + let buf = ''; + 'dalTLDpSugct?'.split('').forEach((c, i) => { + if ((m & (1 << (32 - 1 - i))) !== 0) { + buf = buf + c; + } + }); + + if (buf.length === 0) { + // If the file is directory, make sure it has the directory flag. + if (directory) { + buf = 'd'; + } else { + buf = '-'; + } + } + + 'rwxrwxrwx'.split('').forEach((c, i) => { + if ((m & (1 << (9 - 1 - i))) !== 0) { + buf = buf + c; + } else { + buf = buf + '-'; + } + }); + + return buf; +} From bd0b7127d23426758fb3b6f8ee6860ba31d4a478 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 29 Nov 2020 14:47:18 -0700 Subject: [PATCH 07/10] Fix validation rules for ChmodFilesRequest.php --- .../Requests/Api/Client/Servers/Files/ChmodFilesRequest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php index ed6540219..03a90564b 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php @@ -24,8 +24,7 @@ class ChmodFilesRequest extends ClientApiRequest implements ClientPermissionsReq return [ 'root' => 'required|nullable|string', 'files' => 'required|array', - 'files.file' => 'string', - 'files.mode' => 'integer', + 'files.*' => 'string', ]; } } From 3e1dbbaeddc800500c7e97054472a7b1e63051c2 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 29 Nov 2020 16:02:20 -0700 Subject: [PATCH 08/10] Fix validation rules for ChmodFilesRequest.php, again.. --- .../Requests/Api/Client/Servers/Files/ChmodFilesRequest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php index 03a90564b..158a0a7fd 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/ChmodFilesRequest.php @@ -24,7 +24,8 @@ class ChmodFilesRequest extends ClientApiRequest implements ClientPermissionsReq return [ 'root' => 'required|nullable|string', 'files' => 'required|array', - 'files.*' => 'string', + 'files.*.file' => 'required|string', + 'files.*.mode' => 'required|numeric', ]; } } From 7c2888641f888d0c10492364d198c2eaac8c33e6 Mon Sep 17 00:00:00 2001 From: Stepan Fedotov Date: Fri, 4 Dec 2020 19:56:44 +0200 Subject: [PATCH 09/10] Fix application API's ServerVariableTransformer --- .../Api/Application/ServerVariableTransformer.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Transformers/Api/Application/ServerVariableTransformer.php b/app/Transformers/Api/Application/ServerVariableTransformer.php index 73cd169b2..3f7eeac49 100644 --- a/app/Transformers/Api/Application/ServerVariableTransformer.php +++ b/app/Transformers/Api/Application/ServerVariableTransformer.php @@ -2,7 +2,7 @@ namespace Pterodactyl\Transformers\Api\Application; -use Pterodactyl\Models\ServerVariable; +use Pterodactyl\Models\EggVariable; use Pterodactyl\Services\Acl\Api\AdminAcl; class ServerVariableTransformer extends BaseTransformer @@ -27,10 +27,10 @@ class ServerVariableTransformer extends BaseTransformer /** * Return a generic transformed server variable array. * - * @param \Pterodactyl\Models\ServerVariable $variable + * @param \Pterodactyl\Models\EggVariable $variable * @return array */ - public function transform(ServerVariable $variable) + public function transform(EggVariable $variable) { return $variable->toArray(); } @@ -38,11 +38,11 @@ class ServerVariableTransformer extends BaseTransformer /** * Return the parent service variable data. * - * @param \Pterodactyl\Models\ServerVariable $variable + * @param \Pterodactyl\Models\EggVariable $variable * @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource * @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException */ - public function includeParent(ServerVariable $variable) + public function includeParent(EggVariable $variable) { if (! $this->authorize(AdminAcl::RESOURCE_EGGS)) { return $this->null(); From e32c4d4f0518cb0344c372c872c1aa4f31c096a7 Mon Sep 17 00:00:00 2001 From: Stepan Fedotov Date: Fri, 4 Dec 2020 19:58:09 +0200 Subject: [PATCH 10/10] Documentate fix --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc6c0f9c..43ed825c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## Unreleased +### Fixed +* Fixes the application API unable to return server's variables. + ## v1.1.3 ### Fixed * Server bulk power actions command will no longer attempt to run commands against installing or suspended servers.