diff --git a/.env.dusk b/.env.dusk new file mode 100644 index 000000000..4c8e50527 --- /dev/null +++ b/.env.dusk @@ -0,0 +1,26 @@ +APP_ENV=local +APP_DEBUG=false +APP_KEY=NDWgIKKi9ovNK1PXZpzfNVSBdfCXGb5i +APP_JWT_KEY=test1234 +APP_TIMEZONE=America/Los_Angeles +APP_URL=http://pterodactyl.local + +CACHE_DRIVER=file +SESSION_DRIVER=file + +HASHIDS_SALT=IqRr0g82tCTeuyxGs8RV +HASHIDS_LENGTH=8 + +MAIL_DRIVER=log +MAIL_FROM=support@pterodactyl.io +QUEUE_DRIVER=array + +APP_SERVICE_AUTHOR=testing@pterodactyl.io +MAIL_FROM_NAME="Pterodactyl Panel" +RECAPTCHA_ENABLED=false + +DB_CONNECTION=testing +TESTING_DB_HOST=services.pterodactyl.local +TESTING_DB_DATABASE=panel_test +TESTING_DB_USERNAME=panel_test +TESTING_DB_PASSWORD=Test1234 diff --git a/app/Http/Controllers/Base/DashboardController.php b/app/Http/Controllers/Base/DashboardController.php deleted file mode 100644 index 8351b037a..000000000 --- a/app/Http/Controllers/Base/DashboardController.php +++ /dev/null @@ -1,54 +0,0 @@ -repository = $repository; - } - - public function servers(Request $request) - { - $servers = $this->repository->setSearchTerm($request->input('query'))->filterUserAccessServers( - $request->user(), User::FILTER_LEVEL_ALL - ); - - $data = []; - foreach ($servers->items() as $server) { - $cleaned = collect($server)->only([ - 'uuidShort', - 'uuid', - 'name', - 'cpu', - 'memory', - ]); - - $data[] = array_merge($cleaned->toArray(), [ - 'allocation' => [ - 'ip' => $server->allocation->ip, - 'port' => $server->allocation->port, - ], - 'node_name' => $server->node->name, - ]); - } - - return response()->json($data); - } -} diff --git a/app/Transformers/Api/Client/StatsTransformer.php b/app/Transformers/Api/Client/StatsTransformer.php index 01d8e3f20..d3e66eb9a 100644 --- a/app/Transformers/Api/Client/StatsTransformer.php +++ b/app/Transformers/Api/Client/StatsTransformer.php @@ -3,6 +3,8 @@ namespace Pterodactyl\Transformers\Api\Client; use Pterodactyl\Models\Server; +use GuzzleHttp\Exception\RequestException; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface; class StatsTransformer extends BaseClientTransformer @@ -36,6 +38,8 @@ class StatsTransformer extends BaseClientTransformer * * @param \Pterodactyl\Models\Server $model * @return array + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function transform(Server $model) { @@ -61,7 +65,10 @@ class StatsTransformer extends BaseClientTransformer 'disk' => [ 'current' => round(object_get($object, 'proc.disk.used', 0)), 'limit' => floatval($model->disk), + 'io' => $model->io, ], + 'installed' => $model->installed === 1, + 'suspended' => (bool) $model->suspended, ]; } diff --git a/composer.json b/composer.json index d922ebad4..e36bb6797 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,7 @@ "filp/whoops": "^2.1", "friendsofphp/php-cs-fixer": "^2.11.1", "fzaninotto/faker": "^1.6", + "laravel/dusk": "^3.0", "martinlindhe/laravel-vue-i18n-generator": "^0.1.28", "mockery/mockery": "^1.0", "nunomaduro/collision": "^2.0", @@ -68,6 +69,7 @@ }, "autoload-dev": { "psr-4": { + "Pterodactyl\\Tests\\Browser\\": "tests/Browser", "Pterodactyl\\Tests\\Integration\\": "tests/Integration", "Tests\\": "tests/" } diff --git a/composer.lock b/composer.lock index 80cf8495e..fb9a2d822 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "f84af54d009a128472ca7e19a50fccf8", + "content-hash": "069ebb3ec35c8b309b129189106ad45a", "packages": [ { "name": "appstract/laravel-blade-directives", @@ -4692,6 +4692,66 @@ ], "time": "2017-07-22T11:58:36+00:00" }, + { + "name": "facebook/webdriver", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/facebook/php-webdriver.git", + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", + "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0", + "symfony/process": "^2.8 || ^3.1 || ^4.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "php-coveralls/php-coveralls": "^2.0", + "php-mock/php-mock-phpunit": "^1.1", + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "squizlabs/php_codesniffer": "^2.6", + "symfony/var-dumper": "^3.3 || ^4.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-community": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "A PHP client for Selenium WebDriver", + "homepage": "https://github.com/facebook/php-webdriver", + "keywords": [ + "facebook", + "php", + "selenium", + "webdriver" + ], + "time": "2018-05-16T17:37:13+00:00" + }, { "name": "filp/whoops", "version": "2.1.14", @@ -5002,6 +5062,67 @@ ], "time": "2016-02-11T16:21:17+00:00" }, + { + "name": "laravel/dusk", + "version": "v3.0.8", + "source": { + "type": "git", + "url": "https://github.com/laravel/dusk.git", + "reference": "c6201427e63b869b0c1ee83d91c1d1958b71968e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/dusk/zipball/c6201427e63b869b0c1ee83d91c1d1958b71968e", + "reference": "c6201427e63b869b0c1ee83d91c1d1958b71968e", + "shasum": "" + }, + "require": { + "facebook/webdriver": "~1.0", + "illuminate/console": "~5.6", + "illuminate/support": "~5.6", + "nesbot/carbon": "~1.20", + "php": ">=7.1.0", + "symfony/console": "~4.0", + "symfony/process": "~4.0" + }, + "require-dev": { + "mockery/mockery": "~1.0", + "phpunit/phpunit": "~7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Dusk\\DuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Dusk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", + "keywords": [ + "laravel", + "testing", + "webdriver" + ], + "time": "2018-04-29T19:15:23+00:00" + }, { "name": "martinlindhe/laravel-vue-i18n-generator", "version": "0.1.28", diff --git a/gulpfile.js b/gulpfile.js index 61e13398c..cdc9c2110 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -2,6 +2,7 @@ const babel = require('gulp-babel'); const concat = require('gulp-concat'); const cssmin = require('gulp-cssmin'); const del = require('del'); +const exec = require('child_process').exec; const gulp = require('gulp'); const gulpif = require('gulp-if'); const postcss = require('gulp-postcss'); @@ -85,6 +86,32 @@ function watch() { }, scripts)); } +/** + * Generate the language files to be consumed by front end. + * + * @returns {Promise} + */ +function i18n() { + return new Promise((resolve, reject) => { + exec('php artisan vue-i18n:generate', {}, (err, stdout, stderr) => { + return err ? reject(err) : resolve({ stdout, stderr }); + }) + }) +} + +/** + * Generate the routes file to be used in Vue files. + * + * @returns {Promise} + */ +function routes() { + return new Promise((resolve, reject) => { + exec('php artisan ziggy:generate resources/assets/scripts/helpers/ziggy.js', {}, (err, stdout, stderr) => { + return err ? reject(err) : resolve({ stdout, stderr }); + }); + }) +} + /** * Cleanup unused versions of hashed assets. */ @@ -93,9 +120,12 @@ function clean() { } exports.clean = clean; +exports.i18n = i18n; +exports.routes = routes; exports.styles = styles; exports.scripts = scripts; exports.watch = watch; +gulp.task('components', gulp.parallel(i18n, routes)); gulp.task('scripts', gulp.series(clean, scripts)); -gulp.task('default', gulp.series(clean, styles, scripts)); +gulp.task('default', gulp.series(clean, i18n, routes, styles, scripts)); diff --git a/package.json b/package.json index b1f301eeb..18a00e19a 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "jquery": "^3.3.1", "jwt-decode": "^2.2.0", "lodash": "^4.17.5", + "luxon": "^1.2.1", "postcss": "^6.0.21", "postcss-import": "^11.1.0", "postcss-preset-env": "^3.4.0", @@ -42,6 +43,7 @@ "vue": "^2.5.7", "vue-axios": "^2.1.1", "vue-devtools": "^3.1.9", + "vue-feather-icons": "^4.7.1", "vue-loader": "^14.2.2", "vue-mc": "^0.2.4", "vue-router": "^3.0.1", @@ -58,6 +60,7 @@ "build:filemanager": "./node_modules/babel-cli/bin/babel.js public/themes/pterodactyl/js/frontend/files/src --source-maps --out-file public/themes/pterodactyl/js/frontend/files/filemanager.min.js", "watch": "./node_modules/gulp-cli/bin/gulp.js watch", "build": "./node_modules/gulp-cli/bin/gulp.js default", + "build:components": "./node_modules/gulp-cli/bin/gulp.js components", "build:styles": "./node_modules/gulp-cli/bin/gulp.js styles", "build:scripts": "./node_modules/gulp-cli/bin/gulp.js scripts" } diff --git a/phpunit.xml b/phpunit.xml index 0b67ad6ea..1bf73c4c6 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,6 +10,9 @@ processIsolation="false" stopOnFailure="false"> + + ./tests/Browser/Processes + ./tests/Integration diff --git a/resources/assets/scripts/bootstrap.js b/resources/assets/scripts/bootstrap.js index 562a7adf3..05dccd8e8 100644 --- a/resources/assets/scripts/bootstrap.js +++ b/resources/assets/scripts/bootstrap.js @@ -17,8 +17,8 @@ try { */ window.axios = require('axios'); - window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; +window.axios.defaults.headers.common['Accept'] = 'application/json'; window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.token || ''; if (typeof phpdebugbar !== 'undefined') { diff --git a/resources/assets/scripts/components/auth/ForgotPassword.vue b/resources/assets/scripts/components/auth/ForgotPassword.vue index e06fa40b4..5a5dc42b8 100644 --- a/resources/assets/scripts/components/auth/ForgotPassword.vue +++ b/resources/assets/scripts/components/auth/ForgotPassword.vue @@ -5,13 +5,14 @@ >
- - +

{{ $t('auth.forgot_password.label_help') }}

@@ -25,6 +26,7 @@
{{ $t('auth.go_to_login') }} @@ -68,6 +70,10 @@ email: this.$props.email, }) .then(function (response) { + if (!(response.data instanceof Object)) { + throw new Error('An error was encountered while processing this request.'); + } + self.$data.submitDisabled = false; self.$data.showSpinner = false; self.success(response.data.status); diff --git a/resources/assets/scripts/components/auth/Login.vue b/resources/assets/scripts/components/auth/Login.vue index efda62d2e..a2fadfbba 100644 --- a/resources/assets/scripts/components/auth/Login.vue +++ b/resources/assets/scripts/components/auth/Login.vue @@ -1,6 +1,6 @@