From dc84af99375d0e4086cbda3012ff3e5e28d80959 Mon Sep 17 00:00:00 2001 From: DaneEveritt Date: Sun, 26 Jun 2022 15:13:52 -0400 Subject: [PATCH] Apply new eslint rules; default to prettier for styling --- .eslintrc.js | 57 ++ .eslintrc.yml | 2 - package.json | 16 +- resources/scripts/TransitionRouter.tsx | 6 +- resources/scripts/api/account/activity.ts | 29 +- resources/scripts/api/account/createApiKey.ts | 12 +- resources/scripts/api/account/ssh-keys.ts | 18 +- resources/scripts/api/auth/login.ts | 14 +- resources/scripts/api/auth/loginCheckpoint.ts | 12 +- .../scripts/api/auth/performPasswordReset.ts | 10 +- .../api/auth/requestPasswordResetEmail.ts | 2 +- resources/scripts/api/definitions/helpers.ts | 22 +- resources/scripts/api/definitions/index.d.ts | 2 +- .../scripts/api/definitions/user/models.d.ts | 4 +- .../api/definitions/user/transformers.ts | 11 +- resources/scripts/api/getServers.ts | 10 +- resources/scripts/api/http.ts | 31 +- resources/scripts/api/interceptors.ts | 17 +- resources/scripts/api/server/activity.ts | 31 +- .../server/databases/createServerDatabase.ts | 18 +- .../server/databases/getServerDatabases.ts | 9 +- .../scripts/api/server/files/compressFiles.ts | 13 +- .../api/server/files/decompressFiles.ts | 13 +- .../api/server/files/getFileContents.ts | 2 +- .../scripts/api/server/files/loadDirectory.ts | 2 +- resources/scripts/api/server/getServer.ts | 24 +- .../api/server/getServerResourceUsage.ts | 22 +- .../scripts/api/server/getWebsocketToken.ts | 10 +- .../server/network/deleteServerAllocation.ts | 3 +- .../schedules/createOrUpdateSchedule.ts | 2 +- .../schedules/createOrUpdateScheduleTask.ts | 15 +- .../api/server/schedules/getServerSchedule.ts | 2 +- .../server/schedules/getServerSchedules.ts | 2 +- .../api/server/updateStartupVariable.ts | 4 +- .../api/server/users/createOrUpdateSubuser.ts | 2 +- .../api/server/users/getServerSubusers.ts | 2 +- .../scripts/api/swr/getServerAllocations.ts | 14 +- resources/scripts/api/swr/getServerBackups.ts | 8 +- resources/scripts/api/swr/getServerStartup.ts | 23 +- resources/scripts/api/transformers.ts | 40 +- resources/scripts/components/App.tsx | 20 +- resources/scripts/components/Avatar.tsx | 10 +- .../scripts/components/FlashMessageRender.tsx | 35 +- resources/scripts/components/MessageBox.tsx | 28 +- .../scripts/components/NavigationBar.tsx | 31 +- .../auth/ForgotPasswordContainer.tsx | 59 +- .../auth/LoginCheckpointContainer.tsx | 32 +- .../components/auth/LoginContainer.tsx | 56 +- .../components/auth/LoginFormContainer.tsx | 18 +- .../auth/ResetPasswordContainer.tsx | 30 +- .../dashboard/AccountApiContainer.tsx | 86 +- .../dashboard/AccountOverviewContainer.tsx | 37 +- .../components/dashboard/ApiKeyModal.tsx | 4 +- .../dashboard/DashboardContainer.tsx | 73 +- .../components/dashboard/ServerRow.tsx | 104 ++- .../activity/ActivityLogContainer.tsx | 44 +- .../forms/ConfigureTwoFactorForm.tsx | 22 +- .../dashboard/forms/CreateApiKeyForm.tsx | 24 +- .../dashboard/forms/DisableTwoFactorModal.tsx | 8 +- .../dashboard/forms/SetupTwoFactorModal.tsx | 78 +- .../forms/UpdateEmailAddressForm.tsx | 77 +- .../dashboard/forms/UpdatePasswordForm.tsx | 92 +- .../dashboard/search/SearchContainer.tsx | 14 +- .../dashboard/search/SearchModal.tsx | 48 +- .../dashboard/ssh/AccountSSHContainer.tsx | 57 +- .../dashboard/ssh/CreateSSHKeyForm.tsx | 10 +- .../dashboard/ssh/DeleteSSHKeyButton.tsx | 11 +- .../elements/AuthenticatedRoute.tsx | 8 +- .../scripts/components/elements/Button.tsx | 145 +-- resources/scripts/components/elements/Can.tsx | 9 +- .../scripts/components/elements/Checkbox.tsx | 2 +- .../components/elements/CodemirrorEditor.tsx | 16 +- .../components/elements/ConfirmationModal.tsx | 6 +- .../components/elements/ContentBox.tsx | 32 +- .../components/elements/CopyOnClick.tsx | 10 +- .../components/elements/DropdownMenu.tsx | 19 +- .../components/elements/ErrorBoundary.tsx | 15 +- .../scripts/components/elements/Fade.tsx | 22 +- .../scripts/components/elements/Field.tsx | 35 +- .../elements/FormikFieldWrapper.tsx | 20 +- .../components/elements/GreyRowBox.tsx | 4 +- .../scripts/components/elements/Icon.tsx | 6 +- .../scripts/components/elements/Input.tsx | 42 +- .../components/elements/InputError.tsx | 20 +- .../components/elements/InputSpinner.tsx | 18 +- .../scripts/components/elements/Label.tsx | 2 +- .../scripts/components/elements/Modal.tsx | 109 ++- .../components/elements/PageContentBlock.tsx | 8 +- .../components/elements/Pagination.tsx | 46 +- .../components/elements/PermissionRoute.tsx | 11 +- .../components/elements/ProgressBar.tsx | 24 +- .../components/elements/ScreenBlock.tsx | 34 +- .../scripts/components/elements/Select.tsx | 25 +- .../elements/ServerContentBlock.tsx | 2 +- .../scripts/components/elements/Spinner.tsx | 44 +- .../components/elements/SpinnerOverlay.tsx | 2 +- .../components/elements/SubNavigation.tsx | 6 +- .../scripts/components/elements/Switch.tsx | 53 +- .../components/elements/TitledGreyBox.tsx | 13 +- .../elements/activity/ActivityLogEntry.tsx | 30 +- .../activity/ActivityLogMetaButton.tsx | 15 +- .../components/elements/button/Button.tsx | 4 +- .../components/elements/button/types.ts | 2 +- .../elements/dialog/ConfirmationDialog.tsx | 2 +- .../components/elements/dialog/Dialog.tsx | 38 +- .../components/elements/dialog/DialogIcon.tsx | 12 +- .../components/elements/dropdown/Dropdown.tsx | 16 +- .../elements/dropdown/DropdownButton.tsx | 8 +- .../elements/dropdown/DropdownItem.tsx | 53 +- .../components/elements/inputs/Checkbox.tsx | 6 +- .../components/elements/inputs/InputField.tsx | 6 +- .../elements/table/PaginationFooter.tsx | 17 +- .../components/elements/tooltip/Tooltip.tsx | 27 +- .../elements/transitions/FadeTransition.tsx | 8 +- .../server/ConflictStateRenderer.tsx | 46 +- .../components/server/InstallListener.tsx | 12 +- .../server/ServerActivityLogContainer.tsx | 55 +- .../components/server/TransferListener.tsx | 12 +- .../components/server/UptimeDuration.tsx | 18 +- .../components/server/WebsocketHandler.tsx | 76 +- .../server/backups/BackupContainer.tsx | 68 +- .../server/backups/BackupContextMenu.tsx | 105 ++- .../components/server/backups/BackupRow.tsx | 91 +- .../server/backups/CreateBackupButton.tsx | 45 +- .../components/server/console/ChartBlock.tsx | 14 +- .../components/server/console/Console.tsx | 76 +- .../server/console/PowerButtons.tsx | 13 +- .../server/console/ServerConsoleContainer.tsx | 47 +- .../server/console/ServerDetailsBlock.tsx | 75 +- .../components/server/console/StatBlock.tsx | 2 +- .../components/server/console/StatGraphs.tsx | 20 +- .../components/server/console/chart.ts | 73 +- .../server/databases/CreateDatabaseButton.tsx | 107 +-- .../server/databases/DatabaseRow.tsx | 140 ++- .../server/databases/DatabasesContainer.tsx | 56 +- .../server/databases/RotatePasswordButton.tsx | 13 +- resources/scripts/components/server/events.ts | 2 +- .../components/server/features/Features.tsx | 12 +- .../server/features/GSLTokenModalFeature.tsx | 44 +- .../features/JavaVersionModalFeature.tsx | 35 +- .../server/features/PIDLimitModalFeature.tsx | 52 +- .../server/features/SteamDiskSpaceFeature.tsx | 48 +- .../server/features/eula/EulaModalFeature.tsx | 36 +- .../server/files/ChmodFileModal.tsx | 34 +- .../server/files/FileDropdownMenu.tsx | 83 +- .../server/files/FileEditContainer.tsx | 69 +- .../server/files/FileManagerBreadcrumbs.tsx | 79 +- .../server/files/FileManagerContainer.tsx | 87 +- .../components/server/files/FileNameModal.tsx | 2 +- .../components/server/files/FileObjectRow.tsx | 68 +- .../server/files/MassActionsBar.tsx | 42 +- .../server/files/NewDirectoryButton.tsx | 34 +- .../server/files/RenameFileModal.tsx | 48 +- .../server/files/SelectFileCheckbox.tsx | 8 +- .../components/server/files/UploadButton.tsx | 78 +- .../server/network/AllocationRow.tsx | 53 +- .../server/network/DeleteAllocationButton.tsx | 21 +- .../server/network/NetworkContainer.tsx | 51 +- .../server/schedules/DeleteScheduleButton.tsx | 8 +- .../server/schedules/EditScheduleModal.tsx | 52 +- .../server/schedules/NewTaskButton.tsx | 4 +- .../server/schedules/RunScheduleButton.tsx | 10 +- .../server/schedules/ScheduleContainer.tsx | 67 +- .../schedules/ScheduleEditContainer.tsx | 109 ++- .../server/schedules/ScheduleRow.tsx | 13 +- .../server/schedules/ScheduleTaskRow.tsx | 93 +- .../server/schedules/TaskDetailsModal.tsx | 95 +- .../server/settings/ReinstallServerBox.tsx | 10 +- .../server/settings/RenameServerBox.tsx | 21 +- .../server/settings/SettingsContainer.tsx | 28 +- .../server/startup/StartupContainer.tsx | 162 ++-- .../components/server/startup/VariableBox.tsx | 67 +- .../server/users/AddSubuserButton.tsx | 8 +- .../server/users/EditSubuserModal.tsx | 104 ++- .../components/server/users/PermissionRow.tsx | 52 +- .../server/users/PermissionTitleBox.tsx | 36 +- .../server/users/RemoveSubuserButton.tsx | 12 +- .../components/server/users/UserRow.tsx | 18 +- .../server/users/UsersContainer.tsx | 34 +- resources/scripts/context/ModalContext.ts | 7 +- resources/scripts/easy-peasy.d.ts | 8 +- resources/scripts/helpers.ts | 11 +- .../scripts/hoc/RequireServerPermission.tsx | 7 +- resources/scripts/hoc/asModal.tsx | 35 +- resources/scripts/i18n.ts | 3 +- resources/scripts/index.tsx | 2 +- resources/scripts/lib/formatters.spec.ts | 38 +- resources/scripts/lib/formatters.ts | 14 +- resources/scripts/lib/helpers.ts | 4 +- resources/scripts/lib/objects.spec.ts | 20 +- resources/scripts/lib/objects.ts | 6 +- resources/scripts/lib/strings.ts | 2 +- resources/scripts/macros.d.ts | 8 +- resources/scripts/modes.ts | 136 ++- resources/scripts/plugins/Websocket.ts | 31 +- .../plugins/XtermScrollDownHelperAddon.ts | 13 +- .../scripts/plugins/useDeepCompareMemo.ts | 2 +- resources/scripts/plugins/useDeepMemoize.ts | 2 +- resources/scripts/plugins/useEventListener.ts | 10 +- .../scripts/plugins/useFileManagerSwr.ts | 6 +- .../scripts/plugins/useFilteredObject.ts | 6 +- resources/scripts/plugins/useLocationHash.ts | 8 +- resources/scripts/plugins/usePermissions.ts | 16 +- .../scripts/plugins/usePersistedState.ts | 29 +- .../scripts/plugins/useUserSWRContentKey.ts | 8 +- .../scripts/plugins/useWebsocketEvent.ts | 6 +- .../scripts/routers/AuthenticationRouter.tsx | 12 +- resources/scripts/routers/DashboardRouter.tsx | 30 +- resources/scripts/routers/ServerRouter.tsx | 100 +- resources/scripts/routers/routes.ts | 2 +- resources/scripts/state/flashes.ts | 16 +- resources/scripts/state/progress.ts | 4 +- resources/scripts/state/server/databases.ts | 8 +- resources/scripts/state/server/files.ts | 4 +- resources/scripts/state/server/index.ts | 65 +- resources/scripts/state/server/schedules.ts | 8 +- resources/scripts/state/server/subusers.ts | 45 +- yarn.lock | 859 ++++++++++-------- 218 files changed, 3876 insertions(+), 3564 deletions(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..02c407cba --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,57 @@ +const prettier = { + singleQuote: true, + jsxSingleQuote: true, + printWidth: 120, +}; + +/** @type {import('eslint').Linter.Config} */ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + }, + project: './tsconfig.json', + tsconfigRootDir: './', + }, + settings: { + react: { + pragma: 'React', + version: 'detect', + }, + linkComponents: [ + {name: 'Link', linkAttribute: 'to'}, + {name: 'NavLink', linkAttribute: 'to'}, + ], + }, + env: { + browser: true, + es6: true, + }, + plugins: [ + 'react', + 'react-hooks', + 'prettier', + '@typescript-eslint', + ], + extends: [ + // 'standard', + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:jest-dom/recommended', + ], + rules: { + eqeqeq: 'error', + 'prettier/prettier': ['error', prettier], + // This setup is required to avoid a spam of errors when running eslint about React being + // used before it is defined. + // + // @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md#how-to-use + 'no-use-before-define': 0, + '@typescript-eslint/no-use-before-define': 'warn', + '@typescript-eslint/no-unused-vars': ['warn', {argsIgnorePattern: '^_', varsIgnorePattern: '^_'}], + '@typescript-eslint/ban-ts-comment': ['error', {'ts-expect-error': 'allow-with-description'}], + } +}; diff --git a/.eslintrc.yml b/.eslintrc.yml index e286c93ce..a8fe0e2ad 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -53,8 +53,6 @@ rules: multiline-ternary: 0 "react-hooks/rules-of-hooks": - error - "react-hooks/exhaustive-deps": 0 - "@typescript-eslint/explicit-function-return-type": 0 "@typescript-eslint/explicit-member-accessibility": 0 "@typescript-eslint/ban-ts-ignore": 0 "@typescript-eslint/no-explicit-any": 0 diff --git a/package.json b/package.json index 500e2d898..633e7f759 100644 --- a/package.json +++ b/package.json @@ -99,14 +99,17 @@ "babel-plugin-styled-components": "^2.0.7", "cross-env": "^7.0.2", "css-loader": "^5.2.7", - "eslint": "^7.27.0", - "eslint-config-standard": "^16.0.3", - "eslint-plugin-import": "^2.23.3", + "eslint": "^8.18.0", + "eslint-config-prettier": "^8.5.0", + "eslint-config-standard": "^17.0.0", + "eslint-plugin-import": "^2.26.0", "eslint-plugin-jest-dom": "^4.0.2", + "eslint-plugin-n": "^15.2.3", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.1.0", - "eslint-plugin-react": "^7.23.2", - "eslint-plugin-react-hooks": "^4.2.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-react": "^7.30.1", + "eslint-plugin-react-hooks": "^4.6.0", "fork-ts-checker-webpack-plugin": "^6.2.10", "identity-obj-proxy": "^3.0.0", "jest": "^28.1.1", @@ -115,6 +118,7 @@ "postcss-loader": "^4.0.0", "postcss-nesting": "^10.1.8", "postcss-preset-env": "^7.7.1", + "prettier": "^2.7.1", "redux-devtools-extension": "^2.13.8", "source-map-loader": "^1.1.3", "style-loader": "^2.0.0", diff --git a/resources/scripts/TransitionRouter.tsx b/resources/scripts/TransitionRouter.tsx index 5e5777fee..040097eaa 100644 --- a/resources/scripts/TransitionRouter.tsx +++ b/resources/scripts/TransitionRouter.tsx @@ -7,7 +7,7 @@ import tw from 'twin.macro'; const StyledSwitchTransition = styled(SwitchTransition)` ${tw`relative`}; - + & section { ${tw`absolute w-full top-0 left-0`}; } @@ -19,9 +19,7 @@ const TransitionRouter: React.FC = ({ children }) => { render={({ location }) => ( -
- {children} -
+
{children}
)} diff --git a/resources/scripts/api/account/activity.ts b/resources/scripts/api/account/activity.ts index 7d56061ed..12740b39f 100644 --- a/resources/scripts/api/account/activity.ts +++ b/resources/scripts/api/account/activity.ts @@ -8,19 +8,26 @@ import useFilteredObject from '@/plugins/useFilteredObject'; export type ActivityLogFilters = QueryBuilderParams<'ip' | 'event', 'timestamp'>; -const useActivityLogs = (filters?: ActivityLogFilters, config?: ConfigInterface, AxiosError>): responseInterface, AxiosError> => { - const key = useUserSWRContentKey([ 'account', 'activity', JSON.stringify(useFilteredObject(filters || {})) ]); +const useActivityLogs = ( + filters?: ActivityLogFilters, + config?: ConfigInterface, AxiosError> +): responseInterface, AxiosError> => { + const key = useUserSWRContentKey(['account', 'activity', JSON.stringify(useFilteredObject(filters || {}))]); - return useSWR>(key, async () => { - const { data } = await http.get('/api/client/account/activity', { - params: { - ...withQueryBuilderParams(filters), - include: [ 'actor' ], - }, - }); + return useSWR>( + key, + async () => { + const { data } = await http.get('/api/client/account/activity', { + params: { + ...withQueryBuilderParams(filters), + include: ['actor'], + }, + }); - return toPaginatedSet(data, Transformers.toActivityLog); - }, { revalidateOnMount: false, ...(config || {}) }); + return toPaginatedSet(data, Transformers.toActivityLog); + }, + { revalidateOnMount: false, ...(config || {}) } + ); }; export { useActivityLogs }; diff --git a/resources/scripts/api/account/createApiKey.ts b/resources/scripts/api/account/createApiKey.ts index 7067ec145..b20760e62 100644 --- a/resources/scripts/api/account/createApiKey.ts +++ b/resources/scripts/api/account/createApiKey.ts @@ -7,11 +7,13 @@ export default (description: string, allowedIps: string): Promise 0 ? allowedIps.split('\n') : [], }) - .then(({ data }) => resolve({ - ...rawDataToApiKey(data.attributes), - // eslint-disable-next-line camelcase - secretToken: data.meta?.secret_token ?? '', - })) + .then(({ data }) => + resolve({ + ...rawDataToApiKey(data.attributes), + // eslint-disable-next-line camelcase + secretToken: data.meta?.secret_token ?? '', + }) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/account/ssh-keys.ts b/resources/scripts/api/account/ssh-keys.ts index 53571c669..e85f9b65f 100644 --- a/resources/scripts/api/account/ssh-keys.ts +++ b/resources/scripts/api/account/ssh-keys.ts @@ -5,15 +5,19 @@ import { SSHKey, Transformers } from '@definitions/user'; import { AxiosError } from 'axios'; const useSSHKeys = (config?: ConfigInterface) => { - const key = useUserSWRContentKey([ 'account', 'ssh-keys' ]); + const key = useUserSWRContentKey(['account', 'ssh-keys']); - return useSWR(key, async () => { - const { data } = await http.get('/api/client/account/ssh-keys'); + return useSWR( + key, + async () => { + const { data } = await http.get('/api/client/account/ssh-keys'); - return (data as FractalResponseList).data.map((datum: any) => { - return Transformers.toSSHKey(datum.attributes); - }); - }, { revalidateOnMount: false, ...(config || {}) }); + return (data as FractalResponseList).data.map((datum: any) => { + return Transformers.toSSHKey(datum.attributes); + }); + }, + { revalidateOnMount: false, ...(config || {}) } + ); }; const createSSHKey = async (name: string, publicKey: string): Promise => { diff --git a/resources/scripts/api/auth/login.ts b/resources/scripts/api/auth/login.ts index 379b6590e..ff0de7ac6 100644 --- a/resources/scripts/api/auth/login.ts +++ b/resources/scripts/api/auth/login.ts @@ -15,12 +15,14 @@ export interface LoginData { export default ({ username, password, recaptchaData }: LoginData): Promise => { return new Promise((resolve, reject) => { http.get('/sanctum/csrf-cookie') - .then(() => http.post('/auth/login', { - user: username, - password, - 'g-recaptcha-response': recaptchaData, - })) - .then(response => { + .then(() => + http.post('/auth/login', { + user: username, + password, + 'g-recaptcha-response': recaptchaData, + }) + ) + .then((response) => { if (!(response.data instanceof Object)) { return reject(new Error('An error occurred while processing the login request.')); } diff --git a/resources/scripts/api/auth/loginCheckpoint.ts b/resources/scripts/api/auth/loginCheckpoint.ts index 2d139fa52..73ffb2111 100644 --- a/resources/scripts/api/auth/loginCheckpoint.ts +++ b/resources/scripts/api/auth/loginCheckpoint.ts @@ -6,12 +6,14 @@ export default (token: string, code: string, recoveryToken?: string): Promise 0) ? recoveryToken : undefined, + recovery_token: recoveryToken && recoveryToken.length > 0 ? recoveryToken : undefined, }) - .then(response => resolve({ - complete: response.data.data.complete, - intended: response.data.data.intended || undefined, - })) + .then((response) => + resolve({ + complete: response.data.data.complete, + intended: response.data.data.intended || undefined, + }) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/auth/performPasswordReset.ts b/resources/scripts/api/auth/performPasswordReset.ts index 6695099ee..0dd12f470 100644 --- a/resources/scripts/api/auth/performPasswordReset.ts +++ b/resources/scripts/api/auth/performPasswordReset.ts @@ -19,10 +19,12 @@ export default (email: string, data: Data): Promise => { password: data.password, password_confirmation: data.passwordConfirmation, }) - .then(response => resolve({ - redirectTo: response.data.redirect_to, - sendToLogin: response.data.send_to_login, - })) + .then((response) => + resolve({ + redirectTo: response.data.redirect_to, + sendToLogin: response.data.send_to_login, + }) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/auth/requestPasswordResetEmail.ts b/resources/scripts/api/auth/requestPasswordResetEmail.ts index 2168160c2..d68fa4447 100644 --- a/resources/scripts/api/auth/requestPasswordResetEmail.ts +++ b/resources/scripts/api/auth/requestPasswordResetEmail.ts @@ -3,7 +3,7 @@ import http from '@/api/http'; export default (email: string, recaptchaData?: string): Promise => { return new Promise((resolve, reject) => { http.post('/auth/password', { email, 'g-recaptcha-response': recaptchaData }) - .then(response => resolve(response.data.status || '')) + .then((response) => resolve(response.data.status || '')) .catch(reject); }); }; diff --git a/resources/scripts/api/definitions/helpers.ts b/resources/scripts/api/definitions/helpers.ts index 8130e2b14..eeb933f5a 100644 --- a/resources/scripts/api/definitions/helpers.ts +++ b/resources/scripts/api/definitions/helpers.ts @@ -12,9 +12,21 @@ type TransformerFunc = (callback: FractalResponseData) => T; const isList = (data: FractalResponseList | FractalResponseData): data is FractalResponseList => data.object === 'list'; function transform(data: null | undefined, transformer: TransformerFunc, missing?: M): M; -function transform(data: FractalResponseData | null | undefined, transformer: TransformerFunc, missing?: M): T | M; -function transform(data: FractalResponseList | FractalPaginatedResponse | null | undefined, transformer: TransformerFunc, missing?: M): T[] | M; -function transform (data: FractalResponseData | FractalResponseList | FractalPaginatedResponse | null | undefined, transformer: TransformerFunc, missing = undefined) { +function transform( + data: FractalResponseData | null | undefined, + transformer: TransformerFunc, + missing?: M +): T | M; +function transform( + data: FractalResponseList | FractalPaginatedResponse | null | undefined, + transformer: TransformerFunc, + missing?: M +): T[] | M; +function transform( + data: FractalResponseData | FractalResponseList | FractalPaginatedResponse | null | undefined, + transformer: TransformerFunc, + missing = undefined +) { if (data === undefined || data === null) { return missing; } @@ -30,9 +42,9 @@ function transform (data: FractalResponseData | FractalResponseList | Fractal return transformer(data); } -function toPaginatedSet> ( +function toPaginatedSet>( response: FractalPaginatedResponse, - transformer: T, + transformer: T ): PaginatedResult> { return { items: transform(response, transformer) as ReturnType[], diff --git a/resources/scripts/api/definitions/index.d.ts b/resources/scripts/api/definitions/index.d.ts index 161287384..d041884f6 100644 --- a/resources/scripts/api/definitions/index.d.ts +++ b/resources/scripts/api/definitions/index.d.ts @@ -22,7 +22,7 @@ interface ModelWithRelationships extends Model { */ type WithLoaded = M & { relationships: MarkRequired; -} +}; /** * Helper type that allows you to infer the type of an object by giving diff --git a/resources/scripts/api/definitions/user/models.d.ts b/resources/scripts/api/definitions/user/models.d.ts index e6769345d..944d1b81e 100644 --- a/resources/scripts/api/definitions/user/models.d.ts +++ b/resources/scripts/api/definitions/user/models.d.ts @@ -9,7 +9,7 @@ interface User extends Model { twoFactorEnabled: boolean; createdAt: Date; permissions: SubuserPermission[]; - can (permission: SubuserPermission): boolean; + can(permission: SubuserPermission): boolean; } interface SSHKey extends Model { @@ -30,5 +30,5 @@ interface ActivityLog extends Model<'actor'> { timestamp: Date; relationships: { actor: User | null; - } + }; } diff --git a/resources/scripts/api/definitions/user/transformers.ts b/resources/scripts/api/definitions/user/transformers.ts index 05d089351..bc747a936 100644 --- a/resources/scripts/api/definitions/user/transformers.ts +++ b/resources/scripts/api/definitions/user/transformers.ts @@ -10,7 +10,7 @@ export default class Transformers { fingerprint: data.fingerprint, createdAt: new Date(data.created_at), }; - } + }; static toUser = ({ attributes }: FractalResponseData): Models.User => { return { @@ -21,11 +21,11 @@ export default class Transformers { twoFactorEnabled: attributes['2fa_enabled'], permissions: attributes.permissions || [], createdAt: new Date(attributes.created_at), - can (permission): boolean { + can(permission): boolean { return this.permissions.includes(permission); }, }; - } + }; static toActivityLog = ({ attributes }: FractalResponseData): Models.ActivityLog => { const { actor } = attributes.relationships || {}; @@ -43,8 +43,7 @@ export default class Transformers { actor: transform(actor as FractalResponseData, this.toUser, null), }, }; - } + }; } -export class MetaTransformers { -} +export class MetaTransformers {} diff --git a/resources/scripts/api/getServers.ts b/resources/scripts/api/getServers.ts index 6094b1706..4e29f1532 100644 --- a/resources/scripts/api/getServers.ts +++ b/resources/scripts/api/getServers.ts @@ -15,10 +15,12 @@ export default ({ query, ...params }: QueryParams): Promise resolve({ - items: (data.data || []).map((datum: any) => rawDataToServerObject(datum)), - pagination: getPaginationSet(data.meta.pagination), - })) + .then(({ data }) => + resolve({ + items: (data.data || []).map((datum: any) => rawDataToServerObject(datum)), + pagination: getPaginationSet(data.meta.pagination), + }) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/http.ts b/resources/scripts/api/http.ts index 119f074aa..382c1119a 100644 --- a/resources/scripts/api/http.ts +++ b/resources/scripts/api/http.ts @@ -11,7 +11,7 @@ const http: AxiosInstance = axios.create({ }, }); -http.interceptors.request.use(req => { +http.interceptors.request.use((req) => { if (!req.url?.endsWith('/resources')) { store.getActions().progress.startContinuous(); } @@ -19,17 +19,20 @@ http.interceptors.request.use(req => { return req; }); -http.interceptors.response.use(resp => { - if (!resp.request?.url?.endsWith('/resources')) { +http.interceptors.response.use( + (resp) => { + if (!resp.request?.url?.endsWith('/resources')) { + store.getActions().progress.setComplete(); + } + + return resp; + }, + (error) => { store.getActions().progress.setComplete(); + + throw error; } - - return resp; -}, error => { - store.getActions().progress.setComplete(); - - throw error; -}); +); export default http; @@ -37,7 +40,7 @@ export default http; * Converts an error into a human readable response. Mostly just a generic helper to * make sure we display the message from the server back to the user if we can. */ -export function httpErrorToHuman (error: any): string { +export function httpErrorToHuman(error: any): string { if (error.response && error.response.data) { let { data } = error.response; @@ -104,7 +107,7 @@ export interface PaginationDataSet { totalPages: number; } -export function getPaginationSet (data: any): PaginationDataSet { +export function getPaginationSet(data: any): PaginationDataSet { return { total: data.total, count: data.count, @@ -142,11 +145,11 @@ export const withQueryBuilderParams = (data?: QueryBuilderParams): Record { const value = data.sorts?.[key]; - if (!value || ![ 'asc', 'desc', 1, -1 ].includes(value)) { + if (!value || !['asc', 'desc', 1, -1].includes(value)) { return arr; } - return [ ...arr, (value === -1 || value === 'desc' ? '-' : '') + key ]; + return [...arr, (value === -1 || value === 'desc' ? '-' : '') + key]; }, [] as string[]); return { diff --git a/resources/scripts/api/interceptors.ts b/resources/scripts/api/interceptors.ts index 8ff03fbf0..6fcaeb566 100644 --- a/resources/scripts/api/interceptors.ts +++ b/resources/scripts/api/interceptors.ts @@ -3,14 +3,17 @@ import { AxiosError } from 'axios'; import { History } from 'history'; export const setupInterceptors = (history: History) => { - http.interceptors.response.use(resp => resp, (error: AxiosError) => { - if (error.response?.status === 400) { - if (error.response?.data.errors?.[0].code === 'TwoFactorAuthRequiredException') { - if (!window.location.pathname.startsWith('/account')) { - history.replace('/account', { twoFactorRedirect: true }); + http.interceptors.response.use( + (resp) => resp, + (error: AxiosError) => { + if (error.response?.status === 400) { + if (error.response?.data.errors?.[0].code === 'TwoFactorAuthRequiredException') { + if (!window.location.pathname.startsWith('/account')) { + history.replace('/account', { twoFactorRedirect: true }); + } } } + throw error; } - throw error; - }); + ); }; diff --git a/resources/scripts/api/server/activity.ts b/resources/scripts/api/server/activity.ts index 880496e46..cd28bcf02 100644 --- a/resources/scripts/api/server/activity.ts +++ b/resources/scripts/api/server/activity.ts @@ -9,20 +9,27 @@ import { ServerContext } from '@/state/server'; export type ActivityLogFilters = QueryBuilderParams<'ip' | 'event', 'timestamp'>; -const useActivityLogs = (filters?: ActivityLogFilters, config?: ConfigInterface, AxiosError>): responseInterface, AxiosError> => { - const uuid = ServerContext.useStoreState(state => state.server.data?.uuid); - const key = useUserSWRContentKey([ 'server', 'activity', useFilteredObject(filters || {}) ]); +const useActivityLogs = ( + filters?: ActivityLogFilters, + config?: ConfigInterface, AxiosError> +): responseInterface, AxiosError> => { + const uuid = ServerContext.useStoreState((state) => state.server.data?.uuid); + const key = useUserSWRContentKey(['server', 'activity', useFilteredObject(filters || {})]); - return useSWR>(key, async () => { - const { data } = await http.get(`/api/client/servers/${uuid}/activity`, { - params: { - ...withQueryBuilderParams(filters), - include: [ 'actor' ], - }, - }); + return useSWR>( + key, + async () => { + const { data } = await http.get(`/api/client/servers/${uuid}/activity`, { + params: { + ...withQueryBuilderParams(filters), + include: ['actor'], + }, + }); - return toPaginatedSet(data, Transformers.toActivityLog); - }, { revalidateOnMount: false, ...(config || {}) }); + return toPaginatedSet(data, Transformers.toActivityLog); + }, + { revalidateOnMount: false, ...(config || {}) } + ); }; export { useActivityLogs }; diff --git a/resources/scripts/api/server/databases/createServerDatabase.ts b/resources/scripts/api/server/databases/createServerDatabase.ts index cf036f91f..cb0c25b9e 100644 --- a/resources/scripts/api/server/databases/createServerDatabase.ts +++ b/resources/scripts/api/server/databases/createServerDatabase.ts @@ -3,13 +3,17 @@ import http from '@/api/http'; export default (uuid: string, data: { connectionsFrom: string; databaseName: string }): Promise => { return new Promise((resolve, reject) => { - http.post(`/api/client/servers/${uuid}/databases`, { - database: data.databaseName, - remote: data.connectionsFrom, - }, { - params: { include: 'password' }, - }) - .then(response => resolve(rawDataToServerDatabase(response.data.attributes))) + http.post( + `/api/client/servers/${uuid}/databases`, + { + database: data.databaseName, + remote: data.connectionsFrom, + }, + { + params: { include: 'password' }, + } + ) + .then((response) => resolve(rawDataToServerDatabase(response.data.attributes))) .catch(reject); }); }; diff --git a/resources/scripts/api/server/databases/getServerDatabases.ts b/resources/scripts/api/server/databases/getServerDatabases.ts index cf7c9037d..373c6c1c2 100644 --- a/resources/scripts/api/server/databases/getServerDatabases.ts +++ b/resources/scripts/api/server/databases/getServerDatabases.ts @@ -15,7 +15,8 @@ export const rawDataToServerDatabase = (data: any): ServerDatabase => ({ username: data.username, connectionString: `${data.host.address}:${data.host.port}`, allowConnectionsFrom: data.connections_from, - password: data.relationships && data.relationships.password ? data.relationships.password.attributes.password : undefined, + password: + data.relationships && data.relationships.password ? data.relationships.password.attributes.password : undefined, }); export default (uuid: string, includePassword = true): Promise => { @@ -23,9 +24,9 @@ export default (uuid: string, includePassword = true): Promise http.get(`/api/client/servers/${uuid}/databases`, { params: includePassword ? { include: 'password' } : undefined, }) - .then(response => resolve( - (response.data.data || []).map((item: any) => rawDataToServerDatabase(item.attributes)) - )) + .then((response) => + resolve((response.data.data || []).map((item: any) => rawDataToServerDatabase(item.attributes))) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/server/files/compressFiles.ts b/resources/scripts/api/server/files/compressFiles.ts index 4204f0884..b4c0a251e 100644 --- a/resources/scripts/api/server/files/compressFiles.ts +++ b/resources/scripts/api/server/files/compressFiles.ts @@ -3,10 +3,15 @@ import http from '@/api/http'; import { rawDataToFileObject } from '@/api/transformers'; export default async (uuid: string, directory: string, files: string[]): Promise => { - const { data } = await http.post(`/api/client/servers/${uuid}/files/compress`, { root: directory, files }, { - timeout: 60000, - timeoutErrorMessage: 'It looks like this archive is taking a long time to generate. It will appear once completed.', - }); + const { data } = await http.post( + `/api/client/servers/${uuid}/files/compress`, + { root: directory, files }, + { + timeout: 60000, + timeoutErrorMessage: + 'It looks like this archive is taking a long time to generate. It will appear once completed.', + } + ); return rawDataToFileObject(data); }; diff --git a/resources/scripts/api/server/files/decompressFiles.ts b/resources/scripts/api/server/files/decompressFiles.ts index d674eadb0..37557a671 100644 --- a/resources/scripts/api/server/files/decompressFiles.ts +++ b/resources/scripts/api/server/files/decompressFiles.ts @@ -1,8 +1,13 @@ import http from '@/api/http'; export default async (uuid: string, directory: string, file: string): Promise => { - await http.post(`/api/client/servers/${uuid}/files/decompress`, { root: directory, file }, { - timeout: 300000, - timeoutErrorMessage: 'It looks like this archive is taking a long time to be unarchived. Once completed the unarchived files will appear.', - }); + await http.post( + `/api/client/servers/${uuid}/files/decompress`, + { root: directory, file }, + { + timeout: 300000, + timeoutErrorMessage: + 'It looks like this archive is taking a long time to be unarchived. Once completed the unarchived files will appear.', + } + ); }; diff --git a/resources/scripts/api/server/files/getFileContents.ts b/resources/scripts/api/server/files/getFileContents.ts index ef25b1dbc..66df376d3 100644 --- a/resources/scripts/api/server/files/getFileContents.ts +++ b/resources/scripts/api/server/files/getFileContents.ts @@ -4,7 +4,7 @@ export default (server: string, file: string): Promise => { return new Promise((resolve, reject) => { http.get(`/api/client/servers/${server}/files/contents`, { params: { file }, - transformResponse: res => res, + transformResponse: (res) => res, responseType: 'text', }) .then(({ data }) => resolve(data)) diff --git a/resources/scripts/api/server/files/loadDirectory.ts b/resources/scripts/api/server/files/loadDirectory.ts index ba137e285..1f90b8ac3 100644 --- a/resources/scripts/api/server/files/loadDirectory.ts +++ b/resources/scripts/api/server/files/loadDirectory.ts @@ -5,7 +5,7 @@ export interface FileObject { key: string; name: string; mode: string; - modeBits: string, + modeBits: string; size: number; isFile: boolean; isSymlink: boolean; diff --git a/resources/scripts/api/server/getServer.ts b/resources/scripts/api/server/getServer.ts index 3d74ee2ab..0badbd363 100644 --- a/resources/scripts/api/server/getServer.ts +++ b/resources/scripts/api/server/getServer.ts @@ -58,24 +58,30 @@ export const rawDataToServerObject = ({ attributes: data }: FractalResponseData) ip: data.sftp_details.ip, port: data.sftp_details.port, }, - description: data.description ? ((data.description.length > 0) ? data.description : null) : null, + description: data.description ? (data.description.length > 0 ? data.description : null) : null, limits: { ...data.limits }, eggFeatures: data.egg_features || [], featureLimits: { ...data.feature_limits }, isInstalling: data.status === 'installing' || data.status === 'install_failed', isTransferring: data.is_transferring, - variables: ((data.relationships?.variables as FractalResponseList | undefined)?.data || []).map(rawDataToServerEggVariable), - allocations: ((data.relationships?.allocations as FractalResponseList | undefined)?.data || []).map(rawDataToServerAllocation), + variables: ((data.relationships?.variables as FractalResponseList | undefined)?.data || []).map( + rawDataToServerEggVariable + ), + allocations: ((data.relationships?.allocations as FractalResponseList | undefined)?.data || []).map( + rawDataToServerAllocation + ), }); -export default (uuid: string): Promise<[ Server, string[] ]> => { +export default (uuid: string): Promise<[Server, string[]]> => { return new Promise((resolve, reject) => { http.get(`/api/client/servers/${uuid}`) - .then(({ data }) => resolve([ - rawDataToServerObject(data), - // eslint-disable-next-line camelcase - data.meta?.is_server_owner ? [ '*' ] : (data.meta?.user_permissions || []), - ])) + .then(({ data }) => + resolve([ + rawDataToServerObject(data), + // eslint-disable-next-line camelcase + data.meta?.is_server_owner ? ['*'] : data.meta?.user_permissions || [], + ]) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/server/getServerResourceUsage.ts b/resources/scripts/api/server/getServerResourceUsage.ts index 2a4c01cf6..200ceb017 100644 --- a/resources/scripts/api/server/getServerResourceUsage.ts +++ b/resources/scripts/api/server/getServerResourceUsage.ts @@ -16,16 +16,18 @@ export interface ServerStats { export default (server: string): Promise => { return new Promise((resolve, reject) => { http.get(`/api/client/servers/${server}/resources`) - .then(({ data: { attributes } }) => resolve({ - status: attributes.current_state, - isSuspended: attributes.is_suspended, - memoryUsageInBytes: attributes.resources.memory_bytes, - cpuUsagePercent: attributes.resources.cpu_absolute, - diskUsageInBytes: attributes.resources.disk_bytes, - networkRxInBytes: attributes.resources.network_rx_bytes, - networkTxInBytes: attributes.resources.network_tx_bytes, - uptime: attributes.resources.uptime, - })) + .then(({ data: { attributes } }) => + resolve({ + status: attributes.current_state, + isSuspended: attributes.is_suspended, + memoryUsageInBytes: attributes.resources.memory_bytes, + cpuUsagePercent: attributes.resources.cpu_absolute, + diskUsageInBytes: attributes.resources.disk_bytes, + networkRxInBytes: attributes.resources.network_rx_bytes, + networkTxInBytes: attributes.resources.network_tx_bytes, + uptime: attributes.resources.uptime, + }) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/server/getWebsocketToken.ts b/resources/scripts/api/server/getWebsocketToken.ts index da78dfd05..4769e188b 100644 --- a/resources/scripts/api/server/getWebsocketToken.ts +++ b/resources/scripts/api/server/getWebsocketToken.ts @@ -8,10 +8,12 @@ interface Response { export default (server: string): Promise => { return new Promise((resolve, reject) => { http.get(`/api/client/servers/${server}/websocket`) - .then(({ data }) => resolve({ - token: data.data.token, - socket: data.data.socket, - })) + .then(({ data }) => + resolve({ + token: data.data.token, + socket: data.data.socket, + }) + ) .catch(reject); }); }; diff --git a/resources/scripts/api/server/network/deleteServerAllocation.ts b/resources/scripts/api/server/network/deleteServerAllocation.ts index 92fd4b30a..6e45e1d3d 100644 --- a/resources/scripts/api/server/network/deleteServerAllocation.ts +++ b/resources/scripts/api/server/network/deleteServerAllocation.ts @@ -1,4 +1,5 @@ import { Allocation } from '@/api/server/getServer'; import http from '@/api/http'; -export default async (uuid: string, id: number): Promise => await http.delete(`/api/client/servers/${uuid}/network/allocations/${id}`); +export default async (uuid: string, id: number): Promise => + await http.delete(`/api/client/servers/${uuid}/network/allocations/${id}`); diff --git a/resources/scripts/api/server/schedules/createOrUpdateSchedule.ts b/resources/scripts/api/server/schedules/createOrUpdateSchedule.ts index de1bc075b..d864dee59 100644 --- a/resources/scripts/api/server/schedules/createOrUpdateSchedule.ts +++ b/resources/scripts/api/server/schedules/createOrUpdateSchedule.ts @@ -1,7 +1,7 @@ import { rawDataToServerSchedule, Schedule } from '@/api/server/schedules/getServerSchedules'; import http from '@/api/http'; -type Data = Pick & { id?: number } +type Data = Pick & { id?: number }; export default async (uuid: string, schedule: Data): Promise => { const { data } = await http.post(`/api/client/servers/${uuid}/schedules${schedule.id ? `/${schedule.id}` : ''}`, { diff --git a/resources/scripts/api/server/schedules/createOrUpdateScheduleTask.ts b/resources/scripts/api/server/schedules/createOrUpdateScheduleTask.ts index c2bfc807b..388d8d6d5 100644 --- a/resources/scripts/api/server/schedules/createOrUpdateScheduleTask.ts +++ b/resources/scripts/api/server/schedules/createOrUpdateScheduleTask.ts @@ -9,12 +9,15 @@ interface Data { } export default async (uuid: string, schedule: number, task: number | undefined, data: Data): Promise => { - const { data: response } = await http.post(`/api/client/servers/${uuid}/schedules/${schedule}/tasks${task ? `/${task}` : ''}`, { - action: data.action, - payload: data.payload, - continue_on_failure: data.continueOnFailure, - time_offset: data.timeOffset, - }); + const { data: response } = await http.post( + `/api/client/servers/${uuid}/schedules/${schedule}/tasks${task ? `/${task}` : ''}`, + { + action: data.action, + payload: data.payload, + continue_on_failure: data.continueOnFailure, + time_offset: data.timeOffset, + } + ); return rawDataToServerTask(response.attributes); }; diff --git a/resources/scripts/api/server/schedules/getServerSchedule.ts b/resources/scripts/api/server/schedules/getServerSchedule.ts index 63e3d6f98..537124bd6 100644 --- a/resources/scripts/api/server/schedules/getServerSchedule.ts +++ b/resources/scripts/api/server/schedules/getServerSchedule.ts @@ -5,7 +5,7 @@ export default (uuid: string, schedule: number): Promise => { return new Promise((resolve, reject) => { http.get(`/api/client/servers/${uuid}/schedules/${schedule}`, { params: { - include: [ 'tasks' ], + include: ['tasks'], }, }) .then(({ data }) => resolve(rawDataToServerSchedule(data.attributes))) diff --git a/resources/scripts/api/server/schedules/getServerSchedules.ts b/resources/scripts/api/server/schedules/getServerSchedules.ts index c052e7a3c..aef756a0e 100644 --- a/resources/scripts/api/server/schedules/getServerSchedules.ts +++ b/resources/scripts/api/server/schedules/getServerSchedules.ts @@ -69,7 +69,7 @@ export const rawDataToServerSchedule = (data: any): Schedule => ({ export default async (uuid: string): Promise => { const { data } = await http.get(`/api/client/servers/${uuid}/schedules`, { params: { - include: [ 'tasks' ], + include: ['tasks'], }, }); diff --git a/resources/scripts/api/server/updateStartupVariable.ts b/resources/scripts/api/server/updateStartupVariable.ts index 74498caa8..510217282 100644 --- a/resources/scripts/api/server/updateStartupVariable.ts +++ b/resources/scripts/api/server/updateStartupVariable.ts @@ -2,8 +2,8 @@ import http from '@/api/http'; import { ServerEggVariable } from '@/api/server/types'; import { rawDataToServerEggVariable } from '@/api/transformers'; -export default async (uuid: string, key: string, value: string): Promise<[ ServerEggVariable, string ]> => { +export default async (uuid: string, key: string, value: string): Promise<[ServerEggVariable, string]> => { const { data } = await http.put(`/api/client/servers/${uuid}/startup/variable`, { key, value }); - return [ rawDataToServerEggVariable(data), data.meta.startup_command ]; + return [rawDataToServerEggVariable(data), data.meta.startup_command]; }; diff --git a/resources/scripts/api/server/users/createOrUpdateSubuser.ts b/resources/scripts/api/server/users/createOrUpdateSubuser.ts index 93303a2db..019d35c7a 100644 --- a/resources/scripts/api/server/users/createOrUpdateSubuser.ts +++ b/resources/scripts/api/server/users/createOrUpdateSubuser.ts @@ -12,7 +12,7 @@ export default (uuid: string, params: Params, subuser?: Subuser): Promise resolve(rawDataToServerSubuser(data.data))) + .then((data) => resolve(rawDataToServerSubuser(data.data))) .catch(reject); }); }; diff --git a/resources/scripts/api/server/users/getServerSubusers.ts b/resources/scripts/api/server/users/getServerSubusers.ts index 177bde815..dae2ce580 100644 --- a/resources/scripts/api/server/users/getServerSubusers.ts +++ b/resources/scripts/api/server/users/getServerSubusers.ts @@ -9,7 +9,7 @@ export const rawDataToServerSubuser = (data: FractalResponseData): Subuser => ({ twoFactorEnabled: data.attributes['2fa_enabled'], createdAt: new Date(data.attributes.created_at), permissions: data.attributes.permissions || [], - can: permission => (data.attributes.permissions || []).indexOf(permission) >= 0, + can: (permission) => (data.attributes.permissions || []).indexOf(permission) >= 0, }); export default (uuid: string): Promise => { diff --git a/resources/scripts/api/swr/getServerAllocations.ts b/resources/scripts/api/swr/getServerAllocations.ts index a5591a396..b254b6505 100644 --- a/resources/scripts/api/swr/getServerAllocations.ts +++ b/resources/scripts/api/swr/getServerAllocations.ts @@ -5,11 +5,15 @@ import { rawDataToServerAllocation } from '@/api/transformers'; import { Allocation } from '@/api/server/getServer'; export default () => { - const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid); - return useSWR([ 'server:allocations', uuid ], async () => { - const { data } = await http.get(`/api/client/servers/${uuid}/network/allocations`); + return useSWR( + ['server:allocations', uuid], + async () => { + const { data } = await http.get(`/api/client/servers/${uuid}/network/allocations`); - return (data.data || []).map(rawDataToServerAllocation); - }, { revalidateOnFocus: false, revalidateOnMount: false }); + return (data.data || []).map(rawDataToServerAllocation); + }, + { revalidateOnFocus: false, revalidateOnMount: false } + ); }; diff --git a/resources/scripts/api/swr/getServerBackups.ts b/resources/scripts/api/swr/getServerBackups.ts index 6b76ddcd1..dbc7f5d98 100644 --- a/resources/scripts/api/swr/getServerBackups.ts +++ b/resources/scripts/api/swr/getServerBackups.ts @@ -16,15 +16,15 @@ type BackupResponse = PaginatedResult & { backupCount: number }; export default () => { const { page } = useContext(Context); - const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid); - return useSWR([ 'server:backups', uuid, page ], async () => { + return useSWR(['server:backups', uuid, page], async () => { const { data } = await http.get(`/api/client/servers/${uuid}/backups`, { params: { page } }); - return ({ + return { items: (data.data || []).map(rawDataToServerBackup), pagination: getPaginationSet(data.meta.pagination), backupCount: data.meta.backup_count, - }); + }; }); }; diff --git a/resources/scripts/api/swr/getServerStartup.ts b/resources/scripts/api/swr/getServerStartup.ts index 94a4faf55..efecefe08 100644 --- a/resources/scripts/api/swr/getServerStartup.ts +++ b/resources/scripts/api/swr/getServerStartup.ts @@ -9,14 +9,19 @@ interface Response { dockerImages: Record; } -export default (uuid: string, initialData?: Response | null, config?: ConfigInterface) => useSWR([ uuid, '/startup' ], async (): Promise => { - const { data } = await http.get(`/api/client/servers/${uuid}/startup`); +export default (uuid: string, initialData?: Response | null, config?: ConfigInterface) => + useSWR( + [uuid, '/startup'], + async (): Promise => { + const { data } = await http.get(`/api/client/servers/${uuid}/startup`); - const variables = ((data as FractalResponseList).data || []).map(rawDataToServerEggVariable); + const variables = ((data as FractalResponseList).data || []).map(rawDataToServerEggVariable); - return { - variables, - invocation: data.meta.startup_command, - dockerImages: data.meta.docker_images || {}, - }; -}, { initialData: initialData || undefined, errorRetryCount: 3, ...(config || {}) }); + return { + variables, + invocation: data.meta.startup_command, + dockerImages: data.meta.docker_images || {}, + }; + }, + { initialData: initialData || undefined, errorRetryCount: 3, ...(config || {}) } + ); diff --git a/resources/scripts/api/transformers.ts b/resources/scripts/api/transformers.ts index 069baf126..4ed9a5df5 100644 --- a/resources/scripts/api/transformers.ts +++ b/resources/scripts/api/transformers.ts @@ -25,33 +25,31 @@ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({ modifiedAt: new Date(data.attributes.modified_at), isArchiveType: function () { - return this.isFile && [ - 'application/vnd.rar', // .rar - 'application/x-rar-compressed', // .rar (2) - 'application/x-tar', // .tar - 'application/x-br', // .tar.br - 'application/x-bzip2', // .tar.bz2, .bz2 - 'application/gzip', // .tar.gz, .gz - 'application/x-gzip', - 'application/x-lzip', // .tar.lz4, .lz4 (not sure if this mime type is correct) - 'application/x-sz', // .tar.sz, .sz (not sure if this mime type is correct) - 'application/x-xz', // .tar.xz, .xz - 'application/zstd', // .tar.zst, .zst - 'application/zip', // .zip - ].indexOf(this.mimetype) >= 0; + return ( + this.isFile && + [ + 'application/vnd.rar', // .rar + 'application/x-rar-compressed', // .rar (2) + 'application/x-tar', // .tar + 'application/x-br', // .tar.br + 'application/x-bzip2', // .tar.bz2, .bz2 + 'application/gzip', // .tar.gz, .gz + 'application/x-gzip', + 'application/x-lzip', // .tar.lz4, .lz4 (not sure if this mime type is correct) + 'application/x-sz', // .tar.sz, .sz (not sure if this mime type is correct) + 'application/x-xz', // .tar.xz, .xz + 'application/zstd', // .tar.zst, .zst + 'application/zip', // .zip + ].indexOf(this.mimetype) >= 0 + ); }, isEditable: function () { if (this.isArchiveType() || !this.isFile) return false; - const matches = [ - 'application/jar', - 'application/octet-stream', - 'inode/directory', - /^image\//, - ]; + const matches = ['application/jar', 'application/octet-stream', 'inode/directory', /^image\//]; - return matches.every(m => !this.mimetype.match(m)); + return matches.every((m) => !this.mimetype.match(m)); }, }); diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 1e638f30c..935400030 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -15,9 +15,9 @@ import { ServerContext } from '@/state/server'; import '@/assets/tailwind.css'; import Spinner from '@/components/elements/Spinner'; -const DashboardRouter = lazy(() => import(/* webpackChunkName: "dashboard" */'@/routers/DashboardRouter')); -const ServerRouter = lazy(() => import(/* webpackChunkName: "server" */'@/routers/ServerRouter')); -const AuthenticationRouter = lazy(() => import(/* webpackChunkName: "auth" */'@/routers/AuthenticationRouter')); +const DashboardRouter = lazy(() => import(/* webpackChunkName: "dashboard" */ '@/routers/DashboardRouter')); +const ServerRouter = lazy(() => import(/* webpackChunkName: "server" */ '@/routers/ServerRouter')); +const AuthenticationRouter = lazy(() => import(/* webpackChunkName: "auth" */ '@/routers/AuthenticationRouter')); interface ExtendedWindow extends Window { SiteConfiguration?: SiteSettings; @@ -38,7 +38,7 @@ interface ExtendedWindow extends Window { setupInterceptors(history); const App = () => { - const { PterodactylUser, SiteConfiguration } = (window as ExtendedWindow); + const { PterodactylUser, SiteConfiguration } = window as ExtendedWindow; if (PterodactylUser && !store.getState().user.data) { store.getActions().user.setUserData({ uuid: PterodactylUser.uuid, @@ -58,31 +58,31 @@ const App = () => { return ( <> - + - +
- + - + - + - + diff --git a/resources/scripts/components/Avatar.tsx b/resources/scripts/components/Avatar.tsx index 77cdd2985..4d9d254db 100644 --- a/resources/scripts/components/Avatar.tsx +++ b/resources/scripts/components/Avatar.tsx @@ -2,20 +2,18 @@ import React from 'react'; import BoringAvatar, { AvatarProps } from 'boring-avatars'; import { useStoreState } from '@/state/hooks'; -const palette = [ '#FFAD08', '#EDD75A', '#73B06F', '#0C8F8F', '#587291' ]; +const palette = ['#FFAD08', '#EDD75A', '#73B06F', '#0C8F8F', '#587291']; type Props = Omit; const _Avatar = ({ variant = 'beam', ...props }: AvatarProps) => ( - + ); const _UserAvatar = ({ variant = 'beam', ...props }: Omit) => { - const uuid = useStoreState(state => state.user.data?.uuid); + const uuid = useStoreState((state) => state.user.data?.uuid); - return ( - - ); + return ; }; _Avatar.displayName = 'Avatar'; diff --git a/resources/scripts/components/FlashMessageRender.tsx b/resources/scripts/components/FlashMessageRender.tsx index 6bd22f891..8d0b43f2b 100644 --- a/resources/scripts/components/FlashMessageRender.tsx +++ b/resources/scripts/components/FlashMessageRender.tsx @@ -9,27 +9,22 @@ type Props = Readonly<{ }>; const FlashMessageRender = ({ byKey, className }: Props) => { - const flashes = useStoreState(state => state.flashes.items.filter( - flash => byKey ? flash.key === byKey : true, - )); - - return ( - flashes.length ? -
- { - flashes.map((flash, index) => ( - - {index > 0 &&
} - - {flash.message} - -
- )) - } -
- : - null + const flashes = useStoreState((state) => + state.flashes.items.filter((flash) => (byKey ? flash.key === byKey : true)) ); + + return flashes.length ? ( +
+ {flashes.map((flash, index) => ( + + {index > 0 &&
} + + {flash.message} + +
+ ))} +
+ ) : null; }; export default FlashMessageRender; diff --git a/resources/scripts/components/MessageBox.tsx b/resources/scripts/components/MessageBox.tsx index e16986ed5..57a5cacb5 100644 --- a/resources/scripts/components/MessageBox.tsx +++ b/resources/scripts/components/MessageBox.tsx @@ -42,26 +42,24 @@ const getBackground = (type?: FlashMessageType): TwStyle | string => { const Container = styled.div<{ $type?: FlashMessageType }>` ${tw`p-2 border items-center leading-normal rounded flex w-full text-sm text-white`}; - ${props => styling(props.$type)}; + ${(props) => styling(props.$type)}; `; Container.displayName = 'MessageBox.Container'; const MessageBox = ({ title, children, type }: Props) => ( - {title && - - {title} - - } - - {children} - + {title && ( + + {title} + + )} + {children} ); MessageBox.displayName = 'MessageBox'; diff --git a/resources/scripts/components/NavigationBar.tsx b/resources/scripts/components/NavigationBar.tsx index 8ce627549..e3c9c98b7 100644 --- a/resources/scripts/components/NavigationBar.tsx +++ b/resources/scripts/components/NavigationBar.tsx @@ -14,14 +14,19 @@ import Tooltip from '@/components/elements/tooltip/Tooltip'; import Avatar from '@/components/Avatar'; const RightNavigation = styled.div` - & > a, & > button, & > .navigation-link { + & > a, + & > button, + & > .navigation-link { ${tw`flex items-center h-full no-underline text-neutral-300 px-6 cursor-pointer transition-all duration-150`}; - &:active, &:hover { + &:active, + &:hover { ${tw`text-neutral-100 bg-black`}; } - &:active, &:hover, &.active { + &:active, + &:hover, + &.active { box-shadow: inset 0 -2px ${theme`colors.cyan.600`.toString()}; } } @@ -30,7 +35,7 @@ const RightNavigation = styled.div` export default () => { const name = useStoreState((state: ApplicationStore) => state.settings.data!.name); const rootAdmin = useStoreState((state: ApplicationStore) => state.user.data!.rootAdmin); - const [ isLoggingOut, setIsLoggingOut ] = useState(false); + const [isLoggingOut, setIsLoggingOut] = useState(false); const onTriggerLogout = () => { setIsLoggingOut(true); @@ -42,30 +47,32 @@ export default () => { return (
- +
{name}
- + - + - {rootAdmin && + {rootAdmin && ( - + - } + )} @@ -75,7 +82,7 @@ export default () => { diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index 57f91f4c1..76ddf1992 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -19,10 +19,10 @@ interface Values { export default () => { const ref = useRef(null); - const [ token, setToken ] = useState(''); + const [token, setToken] = useState(''); const { clearFlashes, addFlash } = useFlash(); - const { enabled: recaptchaEnabled, siteKey } = useStoreState(state => state.settings.data!.recaptcha); + const { enabled: recaptchaEnabled, siteKey } = useStoreState((state) => state.settings.data!.recaptcha); useEffect(() => { clearFlashes(); @@ -34,7 +34,7 @@ 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 => { + ref.current!.execute().catch((error) => { console.error(error); setSubmitting(false); @@ -45,11 +45,11 @@ export default () => { } requestPasswordResetEmail(email, token) - .then(response => { + .then((response) => { resetForm(); addFlash({ type: 'success', title: 'Success', message: response }); }) - .catch(error => { + .catch((error) => { console.error(error); addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); }) @@ -66,47 +66,42 @@ export default () => { onSubmit={handleSubmission} initialValues={{ email: '' }} validationSchema={object().shape({ - email: string().email('A valid email address must be provided to continue.') + email: string() + .email('A valid email address must be provided to continue.') .required('A valid email address must be provided to continue.'), })} > {({ isSubmitting, setSubmitting, submitForm }) => ( - +
-
- {recaptchaEnabled && - { - setToken(response); - submitForm(); - }} - onExpire={() => { - setSubmitting(false); - setToken(''); - }} - /> - } + {recaptchaEnabled && ( + { + setToken(response); + submitForm(); + }} + onExpire={() => { + setSubmitting(false); + setToken(''); + }} + /> + )}
, StaticContext, { token?: string }> +type OwnProps = RouteComponentProps, StaticContext, { token?: string }>; type Props = OwnProps & { clearAndAddHttpError: ActionCreator; -} +}; const LoginCheckpointContainer = () => { const { isSubmitting, setFieldValue } = useFormikContext(); - const [ isMissingDevice, setIsMissingDevice ] = useState(false); + const [isMissingDevice, setIsMissingDevice] = useState(false); return ( @@ -44,12 +44,7 @@ const LoginCheckpointContainer = () => { />
-
@@ -58,11 +53,11 @@ const LoginCheckpointContainer = () => { onClick={() => { setFieldValue('code', ''); setFieldValue('recoveryCode', ''); - setIsMissingDevice(s => !s); + setIsMissingDevice((s) => !s); }} css={tw`cursor-pointer text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700`} > - {!isMissingDevice ? 'I\'ve Lost My Device' : 'I Have My Device'} + {!isMissingDevice ? "I've Lost My Device" : 'I Have My Device'}
@@ -80,7 +75,7 @@ const LoginCheckpointContainer = () => { const EnhancedForm = withFormik({ handleSubmit: ({ code, recoveryCode }, { setSubmitting, props: { clearAndAddHttpError, location } }) => { loginCheckpoint(location.state?.token || '', code, recoveryCode) - .then(response => { + .then((response) => { if (response.complete) { // @ts-ignore window.location = response.intended || '/'; @@ -89,7 +84,7 @@ const EnhancedForm = withFormik({ setSubmitting(false); }) - .catch(error => { + .catch((error) => { console.error(error); setSubmitting(false); clearAndAddHttpError({ error }); @@ -111,10 +106,7 @@ export default ({ history, location, ...props }: OwnProps) => { return null; } - return ; + return ( + + ); }; diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index 8157111ee..d848ff602 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -18,10 +18,10 @@ interface Values { const LoginContainer = ({ history }: RouteComponentProps) => { const ref = useRef(null); - const [ token, setToken ] = useState(''); + const [token, setToken] = useState(''); const { clearFlashes, clearAndAddHttpError } = useFlash(); - const { enabled: recaptchaEnabled, siteKey } = useStoreState(state => state.settings.data!.recaptcha); + const { enabled: recaptchaEnabled, siteKey } = useStoreState((state) => state.settings.data!.recaptcha); useEffect(() => { clearFlashes(); @@ -33,7 +33,7 @@ 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 => { + ref.current!.execute().catch((error) => { console.error(error); setSubmitting(false); @@ -44,7 +44,7 @@ const LoginContainer = ({ history }: RouteComponentProps) => { } login({ ...values, recaptchaData: token }) - .then(response => { + .then((response) => { if (response.complete) { // @ts-ignore window.location = response.intended || '/'; @@ -53,7 +53,7 @@ const LoginContainer = ({ history }: RouteComponentProps) => { history.replace('/auth/login/checkpoint', { token: response.confirmationToken }); }) - .catch(error => { + .catch((error) => { console.error(error); setToken(''); @@ -75,42 +75,30 @@ const LoginContainer = ({ history }: RouteComponentProps) => { > {({ isSubmitting, setSubmitting, submitForm }) => ( - +
- +
- {recaptchaEnabled && - { - setToken(response); - submitForm(); - }} - onExpire={() => { - setSubmitting(false); - setToken(''); - }} - /> - } + {recaptchaEnabled && ( + { + setToken(response); + submitForm(); + }} + onExpire={() => { + setSubmitting(false); + setToken(''); + }} + /> + )}
, HTMLFormElement> & { title?: string; -} +}; const Container = styled.div` ${breakpoint('sm')` @@ -30,24 +30,18 @@ const Container = styled.div` export default forwardRef(({ title, ...props }, ref) => ( - {title && -

- {title} -

- } - + {title &&

{title}

} +
- -
-
- {props.children} +
+
{props.children}

- © 2015 - {(new Date()).getFullYear()}  + © 2015 - {new Date().getFullYear()}  ) => { - const [ email, setEmail ] = useState(''); + const [email, setEmail] = useState(''); const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); @@ -36,7 +36,7 @@ export default ({ match, location }: RouteComponentProps<{ token: string }>) => // @ts-ignore window.location = '/'; }) - .catch(error => { + .catch((error) => { console.error(error); setSubmitting(false); @@ -52,22 +52,20 @@ export default ({ match, location }: RouteComponentProps<{ token: string }>) => passwordConfirmation: '', }} validationSchema={object().shape({ - password: string().required('A new password is required.') + password: string() + .required('A new password is required.') .min(8, 'Your new password should be at least 8 characters in length.'), passwordConfirmation: string() .required('Your new password does not match.') // @ts-ignore - .oneOf([ ref('password'), null ], 'Your new password does not match.'), + .oneOf([ref('password'), null], 'Your new password does not match.'), })} > {({ isSubmitting }) => ( - +

) => />
- +
-
diff --git a/resources/scripts/components/dashboard/AccountApiContainer.tsx b/resources/scripts/components/dashboard/AccountApiContainer.tsx index 938b35b85..282f19092 100644 --- a/resources/scripts/components/dashboard/AccountApiContainer.tsx +++ b/resources/scripts/components/dashboard/AccountApiContainer.tsx @@ -16,16 +16,16 @@ import { useFlashKey } from '@/plugins/useFlash'; import Code from '@/components/elements/Code'; export default () => { - const [ deleteIdentifier, setDeleteIdentifier ] = useState(''); - const [ keys, setKeys ] = useState([]); - const [ loading, setLoading ] = useState(true); + const [deleteIdentifier, setDeleteIdentifier] = useState(''); + const [keys, setKeys] = useState([]); + const [loading, setLoading] = useState(true); const { clearAndAddHttpError } = useFlashKey('account'); useEffect(() => { getApiKeys() - .then(keys => setKeys(keys)) + .then((keys) => setKeys(keys)) .then(() => setLoading(false)) - .catch(error => clearAndAddHttpError(error)); + .catch((error) => clearAndAddHttpError(error)); }, []); const doDeletion = (identifier: string) => { @@ -33,10 +33,8 @@ export default () => { clearAndAddHttpError(); deleteApiKey(identifier) - .then(() => setKeys(s => ([ - ...(s || []).filter(key => key.identifier !== identifier), - ]))) - .catch(error => clearAndAddHttpError(error)) + .then(() => setKeys((s) => [...(s || []).filter((key) => key.identifier !== identifier)])) + .catch((error) => clearAndAddHttpError(error)) .then(() => { setLoading(false); setDeleteIdentifier(''); @@ -45,13 +43,13 @@ export default () => { return ( - +
- setKeys(s => ([ ...s!, key ]))}/> + setKeys((s) => [...s!, key])} /> - + { > All requests using the {deleteIdentifier} key will be invalidated. - { - keys.length === 0 ? -

- {loading ? 'Loading...' : 'No API keys exist for this account.'} -

- : - keys.map((key, index) => ( - 0 && tw`mt-2` ]} - > - -
-

{key.description}

-

- Last used:  - {key.lastUsedAt ? format(key.lastUsedAt, 'MMM do, yyyy HH:mm') : 'Never'} -

-
-

+ {loading ? 'Loading...' : 'No API keys exist for this account.'} +

+ ) : ( + keys.map((key, index) => ( + 0 && tw`mt-2`]} + > + +
+

{key.description}

+

+ Last used:  + {key.lastUsedAt ? format(key.lastUsedAt, 'MMM do, yyyy HH:mm') : 'Never'}

- - - )) - } +
+ + +
+ )) + )}
diff --git a/resources/scripts/components/dashboard/AccountOverviewContainer.tsx b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx index 646f7690e..2587458ef 100644 --- a/resources/scripts/components/dashboard/AccountOverviewContainer.tsx +++ b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx @@ -11,19 +11,19 @@ import MessageBox from '@/components/MessageBox'; import { useLocation } from 'react-router-dom'; const Container = styled.div` - ${tw`flex flex-wrap`}; + ${tw`flex flex-wrap`}; - & > div { - ${tw`w-full`}; + & > div { + ${tw`w-full`}; - ${breakpoint('sm')` + ${breakpoint('sm')` width: calc(50% - 1rem); `} - ${breakpoint('md')` + ${breakpoint('md')` ${tw`w-auto flex-1`}; `} - } + } `; export default () => { @@ -31,28 +31,23 @@ export default () => { return ( - {state?.twoFactorRedirect && - - Your account must have two-factor authentication enabled in order to continue. - - } + {state?.twoFactorRedirect && ( + + Your account must have two-factor authentication enabled in order to continue. + + )} - + - + - - + + - + - ); }; diff --git a/resources/scripts/components/dashboard/ApiKeyModal.tsx b/resources/scripts/components/dashboard/ApiKeyModal.tsx index e2c129a51..6d01029e2 100644 --- a/resources/scripts/components/dashboard/ApiKeyModal.tsx +++ b/resources/scripts/components/dashboard/ApiKeyModal.tsx @@ -20,7 +20,9 @@ const ApiKeyModal = ({ apiKey }: Props) => { shown again.

-                {apiKey}
+                
+                    {apiKey}
+                
             
diff --git a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx index 854d0837a..4f37ffbb4 100644 --- a/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/DisableTwoFactorModal.tsx @@ -27,7 +27,7 @@ const DisableTwoFactorModal = () => { updateUserData({ useTotp: false }); dismiss(); }) - .catch(error => { + .catch((error) => { console.error(error); clearAndAddHttpError({ error, key: 'account:two-factor' }); @@ -48,13 +48,15 @@ const DisableTwoFactorModal = () => { > {({ isValid }) => ( - +
diff --git a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx index aea603995..95c505d0b 100644 --- a/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx +++ b/resources/scripts/components/dashboard/forms/SetupTwoFactorModal.tsx @@ -19,8 +19,8 @@ interface Values { } const SetupTwoFactorModal = () => { - const [ token, setToken ] = useState(null); - const [ recoveryTokens, setRecoveryTokens ] = useState([]); + const [token, setToken] = useState(null); + const [recoveryTokens, setRecoveryTokens] = useState([]); const { dismiss, setPropOverrides } = useContext(ModalContext); const updateUserData = useStoreActions((actions: Actions) => actions.user.updateUserData); @@ -29,31 +29,31 @@ const SetupTwoFactorModal = () => { useEffect(() => { getTwoFactorTokenData() .then(setToken) - .catch(error => { + .catch((error) => { console.error(error); clearAndAddHttpError({ error, key: 'account:two-factor' }); }); }, []); const submit = ({ code }: Values, { setSubmitting }: FormikHelpers) => { - setPropOverrides(state => ({ ...state, showSpinnerOverlay: true })); + setPropOverrides((state) => ({ ...state, showSpinnerOverlay: true })); enableAccountTwoFactor(code) - .then(tokens => { + .then((tokens) => { setRecoveryTokens(tokens); }) - .catch(error => { + .catch((error) => { console.error(error); clearAndAddHttpError({ error, key: 'account:two-factor' }); }) .then(() => { setSubmitting(false); - setPropOverrides(state => ({ ...state, showSpinnerOverlay: false })); + setPropOverrides((state) => ({ ...state, showSpinnerOverlay: false })); }); }; useEffect(() => { - setPropOverrides(state => ({ + setPropOverrides((state) => ({ ...state, closeOnEscape: !recoveryTokens.length, closeOnBackground: !recoveryTokens.length, @@ -64,7 +64,7 @@ const SetupTwoFactorModal = () => { updateUserData({ useTotp: true }); } }; - }, [ recoveryTokens ]); + }, [recoveryTokens]); return ( { .matches(/^(\d){6}$/, 'Authenticator code must be 6 digits.'), })} > - {recoveryTokens.length > 0 ? + {recoveryTokens.length > 0 ? ( <>

Two-factor authentication enabled

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

- These codes will not be displayed again. Please take note of them now - by storing them in a secure repository such as a password manager. + These codes will not be displayed again. Please take note of them now by + storing them in a secure repository such as a password manager.

-                        {recoveryTokens.map(token => {token})}
+                        {recoveryTokens.map((token) => (
+                            
+                                {token}
+                            
+                        ))}
                     
- : + ) : ( - +
- {!token ? + {!token ? ( - } + )}
@@ -124,20 +130,20 @@ const SetupTwoFactorModal = () => { name={'code'} type={'text'} title={'Code From Authenticator'} - description={'Enter the code from your authenticator device after scanning the QR image.'} + description={ + 'Enter the code from your authenticator device after scanning the QR image.' + } /> - {token && -
- Alternatively, enter the following token into your authenticator application: - -
- - {token.secret} - -
-
-
- } + {token && ( +
+ Alternatively, enter the following token into your authenticator application: + +
+ {token.secret} +
+
+
+ )}
@@ -145,7 +151,7 @@ const SetupTwoFactorModal = () => {
- } + )}
); }; diff --git a/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx b/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx index 547cb26ce..c13ed073a 100644 --- a/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx +++ b/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx @@ -29,17 +29,21 @@ export default () => { clearFlashes('account:email'); updateEmail({ ...values }) - .then(() => addFlash({ - type: 'success', - key: 'account:email', - message: 'Your primary email has been updated.', - })) - .catch(error => addFlash({ - type: 'error', - key: 'account:email', - title: 'Error', - message: httpErrorToHuman(error), - })) + .then(() => + addFlash({ + type: 'success', + key: 'account:email', + message: 'Your primary email has been updated.', + }) + ) + .catch((error) => + addFlash({ + type: 'error', + key: 'account:email', + title: 'Error', + message: httpErrorToHuman(error), + }) + ) .then(() => { resetForm(); setSubmitting(false); @@ -47,39 +51,28 @@ export default () => { }; return ( - - { - ({ isSubmitting, isValid }) => ( - - -
+ + {({ isSubmitting, isValid }) => ( + + + + +
-
- -
-
- -
- - - ) - } +
+
+ +
+ +
+ )}
); }; diff --git a/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx b/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx index 1c91ef4d9..7153bfda5 100644 --- a/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx +++ b/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx @@ -19,9 +19,13 @@ interface Values { const schema = Yup.object().shape({ current: Yup.string().min(1).required('You must provide your current password.'), password: Yup.string().min(8).required(), - confirmPassword: Yup.string().test('password', 'Password confirmation does not match the password you entered.', function (value) { - return value === this.parent.password; - }), + confirmPassword: Yup.string().test( + 'password', + 'Password confirmation does not match the password you entered.', + function (value) { + return value === this.parent.password; + } + ), }); export default () => { @@ -39,12 +43,14 @@ export default () => { // @ts-ignore window.location = '/auth/login'; }) - .catch(error => addFlash({ - key: 'account:password', - type: 'error', - title: 'Error', - message: httpErrorToHuman(error), - })) + .catch((error) => + addFlash({ + key: 'account:password', + type: 'error', + title: 'Error', + message: httpErrorToHuman(error), + }) + ) .then(() => setSubmitting(false)); }; @@ -55,43 +61,43 @@ export default () => { validationSchema={schema} initialValues={{ current: '', password: '', confirmPassword: '' }} > - { - ({ isSubmitting, isValid }) => ( - - -
+ {({ isSubmitting, isValid }) => ( + + + + +
-
- -
-
- -
-
- -
- - - ) - } +
+
+ +
+
+ +
+ +
+ )}
); diff --git a/resources/scripts/components/dashboard/search/SearchContainer.tsx b/resources/scripts/components/dashboard/search/SearchContainer.tsx index 0bb9978ab..4da0f3a3f 100644 --- a/resources/scripts/components/dashboard/search/SearchContainer.tsx +++ b/resources/scripts/components/dashboard/search/SearchContainer.tsx @@ -6,10 +6,10 @@ import SearchModal from '@/components/dashboard/search/SearchModal'; import Tooltip from '@/components/elements/tooltip/Tooltip'; export default () => { - const [ visible, setVisible ] = useState(false); + const [visible, setVisible] = useState(false); useEventListener('keydown', (e: KeyboardEvent) => { - if ([ 'input', 'textarea' ].indexOf(((e.target as HTMLElement).tagName || 'input').toLowerCase()) < 0) { + if (['input', 'textarea'].indexOf(((e.target as HTMLElement).tagName || 'input').toLowerCase()) < 0) { if (!visible && e.metaKey && e.key.toLowerCase() === '/') { setVisible(true); } @@ -18,16 +18,10 @@ export default () => { return ( <> - {visible && - setVisible(false)} - /> - } + {visible && setVisible(false)} />}
setVisible(true)}> - +
diff --git a/resources/scripts/components/dashboard/search/SearchModal.tsx b/resources/scripts/components/dashboard/search/SearchModal.tsx index e7099ba92..1335cff43 100644 --- a/resources/scripts/components/dashboard/search/SearchModal.tsx +++ b/resources/scripts/components/dashboard/search/SearchModal.tsx @@ -40,24 +40,26 @@ const SearchWatcher = () => { if (values.term.length >= 3) { submitForm(); } - }, [ values.term ]); + }, [values.term]); return null; }; export default ({ ...props }: Props) => { const ref = useRef(null); - const isAdmin = useStoreState(state => state.user.data!.rootAdmin); - const [ servers, setServers ] = useState([]); - const { clearAndAddHttpError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); + const isAdmin = useStoreState((state) => state.user.data!.rootAdmin); + const [servers, setServers] = useState([]); + const { clearAndAddHttpError, clearFlashes } = useStoreActions( + (actions: Actions) => actions.flashes + ); const search = debounce(({ term }: Values, { setSubmitting }: FormikHelpers) => { clearFlashes('search'); // 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 => { + .then((servers) => setServers(servers.items.filter((_, index) => index < 5))) + .catch((error) => { console.error(error); clearAndAddHttpError({ key: 'search', error }); }) @@ -69,10 +71,10 @@ export default ({ ...props }: Props) => { if (props.visible) { if (ref.current) ref.current.focus(); } - }, [ props.visible ]); + }, [props.visible]); // Formik does not support an innerRef on custom components. - const InputWithRef = (props: any) => ; + const InputWithRef = (props: any) => ; return ( { label={'Search term'} description={'Enter a server name, uuid, or allocation to begin searching.'} > - + - + - {servers.length > 0 && -
- { - servers.map(server => ( + {servers.length > 0 && ( +
+ {servers.map((server) => ( {

{server.name}

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

@@ -121,10 +124,9 @@ export default ({ ...props }: Props) => {
- )) - } -
- } + ))} +
+ )} )}
diff --git a/resources/scripts/components/dashboard/ssh/AccountSSHContainer.tsx b/resources/scripts/components/dashboard/ssh/AccountSSHContainer.tsx index 95584775d..8c0a844b3 100644 --- a/resources/scripts/components/dashboard/ssh/AccountSSHContainer.tsx +++ b/resources/scripts/components/dashboard/ssh/AccountSSHContainer.tsx @@ -22,43 +22,40 @@ export default () => { useEffect(() => { clearAndAddHttpError(error); - }, [ error ]); + }, [error]); return ( - +
- + - - { - !data || !data.length ? -

- {!data ? 'Loading...' : 'No SSH Keys exist for this account.'} -

- : - data.map((key, index) => ( - 0 && tw`mt-2` ]} - > - -
-

{key.name}

-

- SHA256:{key.fingerprint} -

-

- Added on:  - {format(key.createdAt, 'MMM do, yyyy HH:mm')} -

-
- -
- )) - } + + {!data || !data.length ? ( +

+ {!data ? 'Loading...' : 'No SSH Keys exist for this account.'} +

+ ) : ( + data.map((key, index) => ( + 0 && tw`mt-2`]} + > + +
+

{key.name}

+

SHA256:{key.fingerprint}

+

+ Added on:  + {format(key.createdAt, 'MMM do, yyyy HH:mm')} +

+
+ +
+ )) + )}
diff --git a/resources/scripts/components/dashboard/ssh/CreateSSHKeyForm.tsx b/resources/scripts/components/dashboard/ssh/CreateSSHKeyForm.tsx index 0443901b8..4b4f39cbd 100644 --- a/resources/scripts/components/dashboard/ssh/CreateSSHKeyForm.tsx +++ b/resources/scripts/components/dashboard/ssh/CreateSSHKeyForm.tsx @@ -15,7 +15,9 @@ interface Values { publicKey: string; } -const CustomTextarea = styled(Textarea)`${tw`h-32`}`; +const CustomTextarea = styled(Textarea)` + ${tw`h-32`} +`; export default () => { const { clearAndAddHttpError } = useFlashKey('account'); @@ -45,16 +47,16 @@ export default () => { > {({ isSubmitting }) => (
- + - + - +
diff --git a/resources/scripts/components/dashboard/ssh/DeleteSSHKeyButton.tsx b/resources/scripts/components/dashboard/ssh/DeleteSSHKeyButton.tsx index 78661a4b6..bd9aaf0d5 100644 --- a/resources/scripts/components/dashboard/ssh/DeleteSSHKeyButton.tsx +++ b/resources/scripts/components/dashboard/ssh/DeleteSSHKeyButton.tsx @@ -9,7 +9,7 @@ import Code from '@/components/elements/Code'; export default ({ name, fingerprint }: { name: string; fingerprint: string }) => { const { clearAndAddHttpError } = useFlashKey('account'); - const [ visible, setVisible ] = useState(false); + const [visible, setVisible] = useState(false); const { mutate } = useSSHKeys(); const onClick = () => { @@ -18,11 +18,10 @@ export default ({ name, fingerprint }: { name: string; fingerprint: string }) => Promise.all([ mutate((data) => data?.filter((value) => value.fingerprint !== fingerprint), false), deleteSSHKey(fingerprint), - ]) - .catch((error) => { - mutate(undefined, true).catch(console.error); - clearAndAddHttpError(error); - }); + ]).catch((error) => { + mutate(undefined, true).catch(console.error); + clearAndAddHttpError(error); + }); }; return ( diff --git a/resources/scripts/components/elements/AuthenticatedRoute.tsx b/resources/scripts/components/elements/AuthenticatedRoute.tsx index 9c5f2265e..2d3b6a6b9 100644 --- a/resources/scripts/components/elements/AuthenticatedRoute.tsx +++ b/resources/scripts/components/elements/AuthenticatedRoute.tsx @@ -3,14 +3,14 @@ import { Redirect, Route, RouteProps } from 'react-router'; import { useStoreState } from '@/state/hooks'; export default ({ children, ...props }: Omit) => { - const isAuthenticated = useStoreState(state => !!state.user.data?.uuid); + const isAuthenticated = useStoreState((state) => !!state.user.data?.uuid); return ( ( - isAuthenticated ? children : - )} + render={({ location }) => + isAuthenticated ? children : + } /> ); }; diff --git a/resources/scripts/components/elements/Button.tsx b/resources/scripts/components/elements/Button.tsx index 8577fad7f..e02f00ff1 100644 --- a/resources/scripts/components/elements/Button.tsx +++ b/resources/scripts/components/elements/Button.tsx @@ -12,88 +12,103 @@ interface Props { const ButtonStyle = styled.button>` ${tw`relative inline-block rounded p-2 uppercase tracking-wide text-sm transition-all duration-150 border`}; - - ${props => ((!props.isSecondary && !props.color) || props.color === 'primary') && css` - ${props => !props.isSecondary && tw`bg-primary-500 border-primary-600 border text-primary-50`}; - - &:hover:not(:disabled) { - ${tw`bg-primary-600 border-primary-700`}; - } - `}; - - ${props => props.color === 'grey' && css` - ${tw`border-neutral-600 bg-neutral-500 text-neutral-50`}; - - &:hover:not(:disabled) { - ${tw`bg-neutral-600 border-neutral-700`}; - } - `}; - - ${props => props.color === 'green' && css` - ${tw`border-green-600 bg-green-500 text-green-50`}; - - &:hover:not(:disabled) { - ${tw`bg-green-600 border-green-700`}; - } - - ${props => props.isSecondary && css` - &:active:not(:disabled) { + + ${(props) => + ((!props.isSecondary && !props.color) || props.color === 'primary') && + css` + ${(props) => !props.isSecondary && tw`bg-primary-500 border-primary-600 border text-primary-50`}; + + &:hover:not(:disabled) { + ${tw`bg-primary-600 border-primary-700`}; + } + `}; + + ${(props) => + props.color === 'grey' && + css` + ${tw`border-neutral-600 bg-neutral-500 text-neutral-50`}; + + &:hover:not(:disabled) { + ${tw`bg-neutral-600 border-neutral-700`}; + } + `}; + + ${(props) => + props.color === 'green' && + css` + ${tw`border-green-600 bg-green-500 text-green-50`}; + + &:hover:not(:disabled) { ${tw`bg-green-600 border-green-700`}; } + + ${(props) => + props.isSecondary && + css` + &:active:not(:disabled) { + ${tw`bg-green-600 border-green-700`}; + } + `}; `}; - `}; - - ${props => props.color === 'red' && css` - ${tw`border-red-600 bg-red-500 text-red-50`}; - - &:hover:not(:disabled) { - ${tw`bg-red-600 border-red-700`}; - } - - ${props => props.isSecondary && css` - &:active:not(:disabled) { + + ${(props) => + props.color === 'red' && + css` + ${tw`border-red-600 bg-red-500 text-red-50`}; + + &:hover:not(:disabled) { ${tw`bg-red-600 border-red-700`}; } + + ${(props) => + props.isSecondary && + css` + &:active:not(:disabled) { + ${tw`bg-red-600 border-red-700`}; + } + `}; `}; - `}; - - ${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`}; - - ${props => props.isSecondary && css` - ${tw`border-neutral-600 bg-transparent text-neutral-200`}; - - &:hover:not(:disabled) { - ${tw`border-neutral-500 text-neutral-100`}; - ${props => props.color === 'red' && tw`bg-red-500 border-red-600 text-red-50`}; - ${props => props.color === 'primary' && tw`bg-primary-500 border-primary-600 text-primary-50`}; - ${props => props.color === 'green' && tw`bg-green-500 border-green-600 text-green-50`}; - } - `}; - - &:disabled { opacity: 0.55; cursor: default } + + ${(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`}; + + ${(props) => + props.isSecondary && + css` + ${tw`border-neutral-600 bg-transparent text-neutral-200`}; + + &:hover:not(:disabled) { + ${tw`border-neutral-500 text-neutral-100`}; + ${(props) => props.color === 'red' && tw`bg-red-500 border-red-600 text-red-50`}; + ${(props) => props.color === 'primary' && tw`bg-primary-500 border-primary-600 text-primary-50`}; + ${(props) => props.color === 'green' && tw`bg-green-500 border-green-600 text-green-50`}; + } + `}; + + &:disabled { + opacity: 0.55; + cursor: default; + } `; type ComponentProps = Omit & Props; const Button: React.FC = ({ children, isLoading, ...props }) => ( - {isLoading && -
- -
- } - - {children} - + {isLoading && ( +
+ +
+ )} + {children}
); type LinkProps = Omit & Props; -const LinkButton: React.FC = props => ; +const LinkButton: React.FC = (props) => ; export { LinkButton, ButtonStyle }; export default Button; diff --git a/resources/scripts/components/elements/Can.tsx b/resources/scripts/components/elements/Can.tsx index dd9d4845f..824051936 100644 --- a/resources/scripts/components/elements/Can.tsx +++ b/resources/scripts/components/elements/Can.tsx @@ -14,12 +14,9 @@ const Can = ({ action, matchAny = false, renderOnError, children }: Props) => { return ( <> - { - ((matchAny && can.filter(p => p).length > 0) || (!matchAny && can.every(p => p))) ? - children - : - renderOnError - } + {(matchAny && can.filter((p) => p).length > 0) || (!matchAny && can.every((p) => p)) + ? children + : renderOnError} ); }; diff --git a/resources/scripts/components/elements/Checkbox.tsx b/resources/scripts/components/elements/Checkbox.tsx index 790536489..731fd24de 100644 --- a/resources/scripts/components/elements/Checkbox.tsx +++ b/resources/scripts/components/elements/Checkbox.tsx @@ -29,7 +29,7 @@ const Checkbox = ({ name, value, className, ...props }: Props & InputProps) => ( type={'checkbox'} checked={(field.value || []).includes(value)} onClick={() => form.setFieldTouched(field.name, true)} - onChange={e => { + onChange={(e) => { const set = new Set(field.value); set.has(value) ? set.delete(value) : set.add(value); diff --git a/resources/scripts/components/elements/CodemirrorEditor.tsx b/resources/scripts/components/elements/CodemirrorEditor.tsx index e38ec77a0..aa1611a4c 100644 --- a/resources/scripts/components/elements/CodemirrorEditor.tsx +++ b/resources/scripts/components/elements/CodemirrorEditor.tsx @@ -98,7 +98,7 @@ const EditorContainer = styled.div` } .CodeMirror-foldmarker { - color: #CBCCC6; + color: #cbccc6; text-shadow: none; margin-left: 0.25rem; margin-right: 0.25rem; @@ -144,7 +144,7 @@ const findModeByFilename = (filename: string) => { }; export default ({ style, initialContent, filename, mode, fetchContent, onContentSaved, onModeChanged }: Props) => { - const [ editor, setEditor ] = useState(); + const [editor, setEditor] = useState(); const ref = useCallback((node) => { if (!node) return; @@ -173,7 +173,7 @@ export default ({ style, initialContent, filename, mode, fetchContent, onContent // @ts-ignore autoCloseBrackets: true, matchBrackets: true, - gutters: [ 'CodeMirror-linenumbers', 'CodeMirror-foldgutter' ], + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], }); setEditor(e); @@ -185,15 +185,15 @@ export default ({ style, initialContent, filename, mode, fetchContent, onContent } onModeChanged(findModeByFilename(filename)?.mime || 'text/plain'); - }, [ filename ]); + }, [filename]); useEffect(() => { editor && editor.setOption('mode', mode); - }, [ editor, mode ]); + }, [editor, mode]); useEffect(() => { editor && editor.setValue(initialContent || ''); - }, [ editor, initialContent ]); + }, [editor, initialContent]); useEffect(() => { if (!editor) { @@ -207,11 +207,11 @@ export default ({ style, initialContent, filename, mode, fetchContent, onContent }); fetchContent(() => Promise.resolve(editor.getValue())); - }, [ editor, fetchContent, onContentSaved ]); + }, [editor, fetchContent, onContentSaved]); return ( -