diff --git a/.env.example b/.env.example index 9e3ed600..72a0a764 100644 --- a/.env.example +++ b/.env.example @@ -25,6 +25,7 @@ MAIL_USERNAME= MAIL_PASSWORD= MAIL_ENCRYPTION=tls MAIL_FROM=no-reply@example.com +MAILGUN_ENDPOINT=api.mailgun.net # You should set this to your domain to prevent it defaulting to 'localhost', causing # mail servers such as Gmail to reject your mail. # diff --git a/.eslintrc.yml b/.eslintrc.yml index d10acf15..1a68b1dc 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -44,6 +44,8 @@ rules: array-bracket-spacing: - warn - always + # Remove errors for not having newlines between operands of ternary expressions https://eslint.org/docs/rules/multiline-ternary + multiline-ternary: 0 "react-hooks/rules-of-hooks": - error "react-hooks/exhaustive-deps": 0 @@ -76,9 +78,13 @@ rules: - 1 - "line-aligned" "react/jsx-closing-tag-location": 1 - "no-use-before-define": 0 - "@typescript-eslint/no-use-before-define": 1 - "multiline-ternary": 0 + # 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 overrides: - files: - "**/*.tsx" diff --git a/CHANGELOG.md b/CHANGELOG.md index 879ec3bc..0ab55458 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## v1.4.2 +### Fixed +* Fixes logic to disallow creating a backup schedule if the server's backup limit is set to 0. +* Fixes bug preventing a database host from being updated if the linked node is set to "none". +* Fixes files and menus under the "Mass Actions Bar" being unclickable when it is visible. +* Fixes issues with the Teamspeak and Mumble eggs causing installs to fail. +* Fixes automated query to avoid pruning backups that are still running unintentionally. +* Fixes "Delete Server" confirmation modal on the admin screen to actually show up when deleting rather than immediately deleting the server. + +### Added +* Adds support for locking individual server backups to prevent deletion by users or automated backup processes. +* List of files to be deleted is now shown on the delete file confirmation modal. +* Adds support for using `IF` statements in database queries when a database user is created through the Panel. +* Adds support for using a custom mailgun API endpoint rather than only the US based endpoint. +* Adds CPU limit display next to the current CPU usage to match disk and memory usage reporting. +* Adds a "Scroll to Bottom" helper element to the server console when not scrolled to the bottom currently. +* Adds support for querying the API for servers by using the `uuidShort` field rather than only the `uuid` field. + +### Changed +* Updates codebase to use TypeScript 4. +* **[security]**: removes the external dependency for loading QRCode images. They're now generated directly on the frontend using JavaScript. + ## v1.4.1 ### Added * Adds support for only running a schedule if the server is currently in an online state. diff --git a/app/Console/Commands/Environment/EmailSettingsCommand.php b/app/Console/Commands/Environment/EmailSettingsCommand.php index 6d98b63c..57998a2e 100644 --- a/app/Console/Commands/Environment/EmailSettingsCommand.php +++ b/app/Console/Commands/Environment/EmailSettingsCommand.php @@ -37,6 +37,7 @@ class EmailSettingsCommand extends Command {--encryption=} {--host=} {--port=} + {--endpoint=} {--username=} {--password=}'; @@ -140,6 +141,11 @@ class EmailSettingsCommand extends Command trans('command/messages.environment.mail.ask_mailgun_secret'), $this->config->get('services.mailgun.secret') ); + + $this->variables['MAILGUN_ENDPOINT'] = $this->option('endpoint') ?? $this->ask( + trans('command/messages.environment.mail.ask_mailgun_endpoint'), + $this->config->get('services.mailgun.endpoint') + ); } /** diff --git a/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php index 4aef591a..71aeb4f3 100644 --- a/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php +++ b/app/Console/Commands/Maintenance/PruneOrphanedBackupsCommand.php @@ -28,7 +28,7 @@ class PruneOrphanedBackupsCommand extends Command $query = $repository->getBuilder() ->whereNull('completed_at') - ->whereDate('created_at', '<=', CarbonImmutable::now()->subMinutes($since)); + ->where('created_at', '<=', CarbonImmutable::now()->subMinutes($since)->toDateTimeString()); $count = $query->count(); if (!$count) { diff --git a/app/Http/Controllers/Api/Application/Servers/ServerController.php b/app/Http/Controllers/Api/Application/Servers/ServerController.php index b09cf613..21af4620 100644 --- a/app/Http/Controllers/Api/Application/Servers/ServerController.php +++ b/app/Http/Controllers/Api/Application/Servers/ServerController.php @@ -47,7 +47,7 @@ class ServerController extends ApplicationApiController } $servers = QueryBuilder::for(Server::query()) - ->allowedFilters(['uuid', 'name', 'image', 'external_id']) + ->allowedFilters(['uuid', 'uuidShort', 'name', 'image', 'external_id']) ->allowedSorts(['id', 'uuid', 'uuidShort', 'name', 'owner_id', 'node_id', 'status']) ->paginate($perPage); diff --git a/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php index 174cef43..f79d598e 100644 --- a/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php +++ b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php @@ -55,7 +55,7 @@ class DaemonAuthenticate } if (is_null($bearer = $request->bearerToken())) { - throw new HttpException(401, 'Access this this endpoint must include an Authorization header.', null, ['WWW-Authenticate' => 'Bearer']); + throw new HttpException(401, 'Access to this endpoint must include an Authorization header.', null, ['WWW-Authenticate' => 'Bearer']); } $parts = explode('.', $bearer); diff --git a/app/Repositories/Eloquent/DatabaseRepository.php b/app/Repositories/Eloquent/DatabaseRepository.php index 4e421df4..ab0bf709 100644 --- a/app/Repositories/Eloquent/DatabaseRepository.php +++ b/app/Repositories/Eloquent/DatabaseRepository.php @@ -107,7 +107,7 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor public function assignUserToDatabase(string $database, string $username, string $remote): bool { return $this->run(sprintf( - 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, LOCK TABLES, EXECUTE ON `%s`.* TO `%s`@`%s`', + 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, LOCK TABLES, CREATE ROUTINE, ALTER ROUTINE, EXECUTE ON `%s`.* TO `%s`@`%s`', $database, $username, $remote diff --git a/config/services.php b/config/services.php index be637e50..b15f7bf0 100644 --- a/config/services.php +++ b/config/services.php @@ -16,6 +16,7 @@ return [ 'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT') ], 'mandrill' => [ diff --git a/package.json b/package.json index 3275a945..4e50216e 100644 --- a/package.json +++ b/package.json @@ -98,21 +98,21 @@ "@types/uuid": "^3.4.5", "@types/webpack-env": "^1.15.2", "@types/yup": "^0.29.3", - "@typescript-eslint/eslint-plugin": "^4.22.1", - "@typescript-eslint/parser": "^4.22.1", + "@typescript-eslint/eslint-plugin": "^4.25.0", + "@typescript-eslint/parser": "^4.25.0", "autoprefixer": "^10.1.0", "babel-loader": "^8.0.6", "babel-plugin-styled-components": "^1.12.0", "cross-env": "^7.0.2", "css-loader": "^3.2.1", - "eslint": "^7.19.0", - "eslint-config-standard": "^16.0.2", - "eslint-plugin-import": "^2.22.1", + "eslint": "^7.27.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.23.3", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-react": "^7.22.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-react": "^7.23.2", "eslint-plugin-react-hooks": "^4.2.0", - "fork-ts-checker-webpack-plugin": "^6.1.0", + "fork-ts-checker-webpack-plugin": "^6.2.10", "postcss": "^8.2.4", "redux-devtools-extension": "^2.13.8", "source-map-loader": "^1.0.1", diff --git a/resources/lang/en/command/messages.php b/resources/lang/en/command/messages.php index e135a1ec..b02fa8c2 100644 --- a/resources/lang/en/command/messages.php +++ b/resources/lang/en/command/messages.php @@ -53,6 +53,7 @@ return [ 'ask_smtp_username' => 'SMTP Username', 'ask_smtp_password' => 'SMTP Password', 'ask_mailgun_domain' => 'Mailgun Domain', + 'ask_mailgun_endpoint' => 'Mailgun Endpoint', 'ask_mailgun_secret' => 'Mailgun Secret', 'ask_mandrill_secret' => 'Mandrill Secret', 'ask_postmark_username' => 'Postmark API Key', diff --git a/resources/scripts/components/dashboard/ServerRow.tsx b/resources/scripts/components/dashboard/ServerRow.tsx index 2b33dbab..3c387858 100644 --- a/resources/scripts/components/dashboard/ServerRow.tsx +++ b/resources/scripts/components/dashboard/ServerRow.tsx @@ -76,6 +76,7 @@ export default ({ server, className }: { server: Server; className?: string }) = const diskLimit = server.limits.disk !== 0 ? megabytesToHuman(server.limits.disk) : 'Unlimited'; const memoryLimit = server.limits.memory !== 0 ? megabytesToHuman(server.limits.memory) : 'Unlimited'; + const cpuLimit = server.limits.cpu !== 0 ? server.limits.cpu + ' %' : 'Unlimited'; return ( @@ -130,11 +131,14 @@ export default ({ server, className }: { server: Server; className?: string }) = : -