From cd7ec731dc633e23ec36144929a237d18c07d2f0 Mon Sep 17 00:00:00 2001 From: Martin Gafert Date: Thu, 2 May 2019 18:25:03 +0200 Subject: [PATCH 01/42] Change Console Chart yAxes to start at 0 and calculate maximum depending on server config (#1535) * Change Console Chart to start yAxes at 0 * Add calculation for Console Chart yAxes maximum if data is recieved * Fix problem with calculating memory maximum --- .../themes/pterodactyl/js/frontend/console.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/public/themes/pterodactyl/js/frontend/console.js b/public/themes/pterodactyl/js/frontend/console.js index 8943da42a..19a11e20c 100644 --- a/public/themes/pterodactyl/js/frontend/console.js +++ b/public/themes/pterodactyl/js/frontend/console.js @@ -255,6 +255,31 @@ $(document).ready(function () { TimeLabels.push($.format.date(new Date(), 'HH:mm:ss')); + + // memory.cmax is the maximum given by the container + // memory.amax is given by the json config + // use the maximum of both + // with no limit memory.cmax will always be higher + // but with limit memory.amax is sometimes still smaller than memory.total + MemoryChart.config.options.scales.yAxes[0].ticks.max = Math.max(proc.data.memory.cmax, proc.data.memory.amax) / (1000 * 1000); + + if (Pterodactyl.server.cpu > 0) { + // if there is a cpu limit defined use 100% as maximum + CPUChart.config.options.scales.yAxes[0].ticks.max = 100; + } else { + // if there is no cpu limit defined use linux percentage + // and find maximum in all values + var maxCpu = 1; + for(var i = 0; i < CPUData.length; i++) { + maxCpu = Math.max(maxCpu, parseFloat(CPUData[i])) + } + + maxCpu = Math.ceil(maxCpu / 100) * 100; + CPUChart.config.options.scales.yAxes[0].ticks.max = maxCpu; + } + + + CPUChart.update(); MemoryChart.update(); }); @@ -301,6 +326,13 @@ $(document).ready(function () { }, animation: { duration: 1, + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true + } + }] } } }); @@ -346,6 +378,13 @@ $(document).ready(function () { }, animation: { duration: 1, + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true + } + }] } } }); From 3f2a4b576294a5a22127e57843609686ae885784 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 9 Jun 2019 17:29:10 -0700 Subject: [PATCH 02/42] Get initial implementation for react working --- package.json | 32 +- resources/assets/index.html | 18 - resources/scripts/components/App.tsx | 12 + resources/scripts/index.tsx | 5 + .../pterodactyl/templates/wrapper.blade.php | 4 +- tsconfig.json | 12 +- webpack.config.js | 173 +++--- yarn.lock | 510 +++++++++--------- 8 files changed, 344 insertions(+), 422 deletions(-) delete mode 100644 resources/assets/index.html create mode 100644 resources/scripts/components/App.tsx create mode 100644 resources/scripts/index.tsx diff --git a/package.json b/package.json index 144807864..3ede1a38e 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,29 @@ { "name": "pterodactyl-panel", "dependencies": { + "@hot-loader/react-dom": "^16.8.6", "axios": "^0.18.0", "brace": "^0.11.1", "date-fns": "^1.29.0", "feather-icons": "^4.10.0", "jquery": "^3.3.1", "lodash": "^4.17.11", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "react-hot-loader": "^4.9.0", "socket.io-client": "^2.2.0", - "vee-validate": "^2.1.7", - "vue": "^2.6.4", - "vue-axios": "^2.1.1", - "vue-i18n": "^8.6.0", - "vue-router": "^3.0.1", - "vuex": "^3.0.1", - "vuex-router-sync": "^5.0.0", "ws-wrapper": "^2.0.0", "xterm": "^3.5.1" }, "devDependencies": { - "@babel/cli": "^7.2.3", "@babel/core": "^7.2.2", - "@babel/plugin-proposal-class-properties": "^7.3.0", - "@babel/plugin-proposal-object-rest-spread": "^7.3.1", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/preset-env": "^7.3.1", + "@babel/preset-react": "^7.0.0", "@types/feather-icons": "^4.7.0", "@types/lodash": "^4.14.119", - "@types/node": "^10.12.15", - "@types/socket.io-client": "^1.4.32", + "@types/react": "^16.8.19", + "@types/react-dom": "^16.8.4", "@types/webpack-env": "^1.13.6", "babel-loader": "^8.0.5", "clean-webpack-plugin": "^0.1.19", @@ -45,23 +40,17 @@ "precss": "^3.1.2", "purgecss-webpack-plugin": "^1.1.0", "resolve-url-loader": "^3.0.0", + "source-map-loader": "^0.2.4", "style-loader": "^0.23.1", "tailwindcss": "^0.7.4", + "terser-webpack-plugin": "^1.3.0", "ts-loader": "^5.3.3", "typescript": "^3.3.1", - "uglifyjs-webpack-plugin": "^2.1.1", - "vue-devtools": "^3.1.9", - "vue-feather-icons": "^4.7.1", - "vue-loader": "^15.6.2", - "vue-mc": "^0.2.4", - "vue-template-compiler": "^2.6.4", - "vueify-insert-css": "^1.0.0", "webpack": "^4.29.0", "webpack-assets-manifest": "^3.1.1", "webpack-cli": "^3.0.2", "webpack-dev-server": "^3.1.14", "webpack-manifest-plugin": "^2.0.3", - "webpack-shell-plugin": "^0.5.0", "webpack-stream": "^4.0.3" }, "scripts": { @@ -69,7 +58,6 @@ "build": "NODE_ENV=development ./node_modules/.bin/webpack --progress", "build:production": "NODE_ENV=production ./node_modules/.bin/webpack", "serve": "NODE_ENV=development webpack-dev-server --host 0.0.0.0 --hot", - "v:serve": "PUBLIC_PATH=http://pterodactyl.test:8080 yarn run serve", - "compile:assets": "php artisan vue-i18n:generate & php artisan ziggy:generate resources/assets/scripts/helpers/ziggy.js" + "v:serve": "PUBLIC_PATH=http://pterodactyl.test:8080 yarn run serve" } } diff --git a/resources/assets/index.html b/resources/assets/index.html deleted file mode 100644 index 2d0076944..000000000 --- a/resources/assets/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - Pterodactyl Dev - - - - - - -
- -
-

-

-
-
- - diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx new file mode 100644 index 000000000..a0b85c996 --- /dev/null +++ b/resources/scripts/components/App.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; +import { hot } from 'react-hot-loader/root'; + +class App extends React.PureComponent { + render () { + return ( +

Hello

+ ); + } +} + +export default hot(App); diff --git a/resources/scripts/index.tsx b/resources/scripts/index.tsx new file mode 100644 index 000000000..2048d538a --- /dev/null +++ b/resources/scripts/index.tsx @@ -0,0 +1,5 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import App from "@/components/App"; + +ReactDOM.render(, document.getElementById('app')); diff --git a/resources/themes/pterodactyl/templates/wrapper.blade.php b/resources/themes/pterodactyl/templates/wrapper.blade.php index 35930d63a..4424038d2 100644 --- a/resources/themes/pterodactyl/templates/wrapper.blade.php +++ b/resources/themes/pterodactyl/templates/wrapper.blade.php @@ -35,15 +35,13 @@ @section('content') @yield('above-container') -
+
@yield('container')
@yield('below-container') @show @section('scripts') {!! $asset->js('main.js') !!} - {!! $asset->js('vendor.js') !!} - {!! $asset->js('locales.js') !!} @show diff --git a/tsconfig.json b/tsconfig.json index 329e77649..5336d7ffc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,20 @@ { "compilerOptions": { - "target": "es6", + "target": "es5", "module": "esnext", + "jsx": "react", "strict": true, "noImplicitReturns": true, "moduleResolution": "node", - "lib": [ - "es2016", - "dom" - ], + "sourceMap": true, "baseUrl": ".", "paths": { "@/*": [ - "./resources/assets/scripts/*" + "./resources/scripts/*" ] } }, "include": [ - "./resources/assets/scripts/**/*" + "./resources/scripts/**/*" ] } diff --git a/webpack.config.js b/webpack.config.js index c1937ea07..b78f76126 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,22 +6,14 @@ const glob = require('glob-all'); const AssetsManifestPlugin = require('webpack-assets-manifest'); const CleanPlugin = require('clean-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const ShellPlugin = require('webpack-shell-plugin'); const PurgeCssPlugin = require('purgecss-webpack-plugin'); -const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); -const VueLoaderPlugin = require('vue-loader/lib/plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); +const TerserPlugin = require('terser-webpack-plugin'); const isProduction = process.env.NODE_ENV === 'production'; let plugins = [ new CleanPlugin(path.resolve(__dirname, 'public/assets')), - new ShellPlugin({ - onBuildStart: [ - 'php artisan vue-i18n:generate', - 'php artisan ziggy:generate resources/assets/scripts/helpers/ziggy.js', - ], - }), new MiniCssExtractPlugin({ filename: isProduction ? 'bundle.[chunkhash:8].css' : 'bundle.[hash:8].css' }), new AssetsManifestPlugin({ writeToDisk: true, @@ -29,24 +21,17 @@ let plugins = [ integrity: true, integrityHashes: ['sha384'], }), - new VueLoaderPlugin(), - new ForkTsCheckerWebpackPlugin({ - vue: true, - }), + new ForkTsCheckerWebpackPlugin(), ]; if (isProduction) { plugins = plugins.concat([ new PurgeCssPlugin({ paths: glob.sync([ - path.join(__dirname, 'resources/assets/scripts/**/*.vue'), - path.join(__dirname, 'resources/assets/scripts/**/*.ts'), + path.join(__dirname, 'resources/scripts/**/*.ts'), path.join(__dirname, 'resources/themes/pterodactyl/**/*.blade.php'), ]), - // Don't let PurgeCSS remove classes ending with -enter or -leave-active - // They're used by Vue transitions and are therefore not specifically defined - // in any of the files are are checked by PurgeCSS. - whitelistPatterns: [/^xterm/, /-enter$/, /-leave-active$/], + whitelistPatterns: [/^xterm/], extractors: [ { extractor: class { @@ -54,71 +39,22 @@ if (isProduction) { return content.match(/[A-z0-9-:\/]+/g) || []; } }, - extensions: ['html', 'ts', 'js', 'php', 'vue'], + extensions: ['html', 'ts', 'tsx', 'js', 'php'], }, ], }), ]); } -const typescriptLoaders = [ - { - loader: 'babel-loader', - options: { - cacheDirectory: !isProduction, - presets: ['@babel/preset-env'], - plugins: [ - '@babel/plugin-proposal-class-properties', - '@babel/plugin-syntax-dynamic-import', - ['@babel/plugin-proposal-object-rest-spread', { 'useBuiltIns': true }] - ], - }, - }, - { - loader: 'ts-loader', - options: { - appendTsSuffixTo: [/\.vue$/], - experimentalWatchApi: true, - transpileOnly: true, - } - } -]; - -const cssLoaders = [ - { loader: MiniCssExtractPlugin.loader }, - { - loader: 'css-loader', - options: { - sourceMap: !isProduction, - importLoaders: 1, - } - }, - { loader: 'resolve-url-loader' }, - { - loader: 'postcss-loader', - options: { - ident: 'postcss', - sourceMap: true, - plugins: [ - require('postcss-import'), - tailwind('./tailwind.js'), - require('postcss-preset-env')({ - stage: 2, - }), - require('precss'), - ].concat(isProduction ? require('cssnano') : []), - } - } -]; - module.exports = { + cache: true, target: 'web', mode: process.env.NODE_ENV, - devtool: isProduction ? false : 'eval-source-map', + devtool: isProduction ? false : 'cheap-eval-source-map', performance: { hints: false, }, - entry: ['./resources/assets/styles/main.css', './resources/assets/scripts/app.ts'], + entry: ['./resources/assets/styles/main.css', './resources/scripts/index.tsx'], output: { path: path.resolve(__dirname, 'public/assets'), filename: isProduction ? 'bundle.[chunkhash:8].js' : 'bundle.[hash:8].js', @@ -129,34 +65,57 @@ module.exports = { module: { rules: [ { - test: /\.js$/, - loader: 'babel-loader', - exclude: file => (/node_modules/.test(file) && !/\.vue\.js/.test(file)), - options: { - cacheDirectory: !isProduction, - presets: ['@babel/preset-env'], - plugins: [ - '@babel/plugin-proposal-class-properties', - '@babel/plugin-syntax-dynamic-import', - ['@babel/plugin-proposal-object-rest-spread', { 'useBuiltIns': true }] - ], - }, - }, - { - test: /\.vue$/, - use: 'vue-loader', - }, - { - test: /\.ts$/, + test: /\.tsx?$/, exclude: /node_modules/, - use: typescriptLoaders, + use: [ + { + loader: 'babel-loader', + options: { + cacheDirectory: !isProduction, + presets: ['@babel/preset-env', '@babel/preset-react'], + plugins: ['react-hot-loader/babel', '@babel/plugin-syntax-dynamic-import'], + }, + }, + { + loader: 'ts-loader', + options: { + experimentalWatchApi: true, + transpileOnly: true, + }, + }, + ], }, { test: /\.css$/, include: [ path.resolve(__dirname, 'resources'), ], - use: cssLoaders, + use: [ + { loader: MiniCssExtractPlugin.loader }, + { + loader: 'css-loader', + options: { + sourceMap: !isProduction, + importLoaders: 1, + }, + }, + { loader: 'resolve-url-loader' }, + { + loader: 'postcss-loader', + options: { + ident: 'postcss', + sourceMap: true, + plugins: [ + require('postcss-import'), + tailwind('./tailwind.js'), + require('postcss-preset-env')({ + stage: 2, + }), + require('precss'), + ].concat(isProduction ? require('cssnano') : []), + }, + }, + ], }, { test: /\.(png|jpg|gif|svg)$/, @@ -168,41 +127,29 @@ module.exports = { ], }, resolve: { - extensions: ['.ts', '.js', '.vue', '.json'], + extensions: ['.ts', '.tsx', '.js', '.json'], alias: { - 'vue$': 'vue/dist/vue.esm.js', - '@': path.join(__dirname, 'resources/assets/scripts'), + '@': path.join(__dirname, 'resources/scripts'), + 'react-dom': '@hot-loader/react-dom', }, symlinks: false, }, plugins: plugins, optimization: { minimize: true, - minimizer: !isProduction ? [] : [ - new UglifyJsPlugin({ + minimizer: [ + new TerserPlugin({ cache: true, parallel: true, - uglifyOptions: { + terserOptions: { + safari10: true, + mangle: true, output: { comments: false, }, }, }), ], - splitChunks: { - cacheGroups: { - locales: { - test: /locales/, - name: 'locales', - chunks: 'initial', - }, - vendors: { - test: /[\\/]node_modules[\\/]/, - name: 'vendor', - chunks: 'initial', - }, - } - } }, devServer: { contentBase: path.join(__dirname, 'public'), diff --git a/yarn.lock b/yarn.lock index d6635c180..a5a8188b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,22 +2,6 @@ # yarn lockfile v1 -"@babel/cli@^7.2.3": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.2.3.tgz#1b262e42a3e959d28ab3d205ba2718e1923cfee6" - dependencies: - commander "^2.8.1" - convert-source-map "^1.1.0" - fs-readdir-recursive "^1.1.0" - glob "^7.0.0" - lodash "^4.17.10" - mkdirp "^0.5.1" - output-file-sync "^2.0.0" - slash "^2.0.0" - source-map "^0.5.0" - optionalDependencies: - chokidar "^2.0.3" - "@babel/code-frame@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9" @@ -82,6 +66,13 @@ "@babel/helper-explode-assignable-expression" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-builder-react-jsx@^7.3.0": + version "7.3.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz#a1ac95a5d2b3e88ae5e54846bf462eeb81b318a4" + dependencies: + "@babel/types" "^7.3.0" + esutils "^2.0.0" + "@babel/helper-call-delegate@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz#6a957f105f37755e8645343d3038a22e1449cc4a" @@ -90,16 +81,6 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-create-class-features-plugin@^7.3.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.0.tgz#2b01a81b3adc2b1287f9ee193688ef8dc71e718f" - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-member-expression-to-functions" "^7.0.0" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.2.3" - "@babel/helper-define-map@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz#3b74caec329b3c80c116290887c0dd9ae468c20c" @@ -207,15 +188,6 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-replace-supers@^7.2.3": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz#19970020cf22677d62b3a689561dbd9644d8c5e5" - dependencies: - "@babel/helper-member-expression-to-functions" "^7.0.0" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.2.3" - "@babel/types" "^7.0.0" - "@babel/helper-simple-access@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" @@ -288,13 +260,6 @@ "@babel/helper-remap-async-to-generator" "^7.1.0" "@babel/plugin-syntax-async-generators" "^7.2.0" -"@babel/plugin-proposal-class-properties@^7.3.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.0.tgz#272636bc0fa19a0bc46e601ec78136a173ea36cd" - dependencies: - "@babel/helper-create-class-features-plugin" "^7.3.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-json-strings@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" @@ -342,6 +307,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-jsx@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" @@ -502,6 +473,34 @@ "@babel/helper-get-function-arity" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-react-display-name@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz#ebfaed87834ce8dc4279609a4f0c324c156e3eb0" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-react-jsx-self@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz#461e21ad9478f1031dd5e276108d027f1b5240ba" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@babel/plugin-transform-react-jsx-source@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz#20c8c60f0140f5dd3cd63418d452801cf3f7180f" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@babel/plugin-transform-react-jsx@^7.0.0": + version "7.3.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz#f2cab99026631c767e2745a5368b331cfe8f5290" + dependencies: + "@babel/helper-builder-react-jsx" "^7.3.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + "@babel/plugin-transform-regenerator@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz#5b41686b4ed40bef874d7ed6a84bdd849c13e0c1" @@ -596,6 +595,16 @@ js-levenshtein "^1.1.3" semver "^5.3.0" +"@babel/preset-react@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.0.0.tgz#e86b4b3d99433c7b3e9e91747e2653958bc6b3c0" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" + "@babel/template@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" @@ -650,7 +659,7 @@ globals "^11.1.0" lodash "^4.17.10" -"@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3": +"@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2": version "7.2.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8" dependencies: @@ -711,6 +720,15 @@ version "1.0.0" resolved "https://registry.yarnpkg.com/@csstools/sass-import-resolve/-/sass-import-resolve-1.0.0.tgz#32c3cdb2f7af3cd8f0dca357b592e7271f3831b5" +"@hot-loader/react-dom@^16.8.6": + version "16.8.6" + resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.8.6.tgz#7923ba27db1563a7cc48d4e0b2879a140df461ea" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.6" + "@types/feather-icons@^4.7.0": version "4.7.0" resolved "https://registry.yarnpkg.com/@types/feather-icons/-/feather-icons-4.7.0.tgz#ec66bc046bcd1513835f87541ecef54b50c57ec9" @@ -719,32 +737,27 @@ version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" -"@types/node@^10.12.15": - version "10.12.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.15.tgz#20e85651b62fd86656e57c9c9bc771ab1570bc59" +"@types/prop-types@*": + version "15.7.1" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" -"@types/socket.io-client@^1.4.32": - version "1.4.32" - resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.32.tgz#988a65a0386c274b1c22a55377fab6a30789ac14" +"@types/react-dom@^16.8.4": + version "16.8.4" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.4.tgz#7fb7ba368857c7aa0f4e4511c4710ca2c5a12a88" + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^16.8.19": + version "16.8.19" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.19.tgz#629154ef05e2e1985cdde94477deefd823ad9be3" + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" "@types/webpack-env@^1.13.6": version "1.13.6" resolved "http://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976" -"@vue/component-compiler-utils@^2.5.1": - version "2.5.2" - resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.5.2.tgz#a8d57e773354ab10e4742c7d6a8dd86184d4d7be" - dependencies: - consolidate "^0.15.1" - hash-sum "^1.0.2" - lru-cache "^4.1.2" - merge-source-map "^1.1.0" - postcss "^7.0.14" - postcss-selector-parser "^5.0.0" - prettier "1.16.3" - source-map "~0.6.1" - vue-template-es2015-compiler "^1.8.2" - "@webassemblyjs/ast@1.5.9": version "1.5.9" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.9.tgz#b2770182678691ab4949d593105c15d4074fedb6" @@ -1238,6 +1251,12 @@ async@^2.1.2: dependencies: lodash "^4.17.10" +async@^2.5.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + dependencies: + lodash "^4.17.11" + atob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" @@ -1253,13 +1272,6 @@ autoprefixer@^9.4.5: postcss "^7.0.14" postcss-value-parser "^3.3.1" -axios@^0.16: - version "0.16.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d" - dependencies: - follow-redirects "^1.2.3" - is-buffer "^1.1.5" - axios@^0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" @@ -1292,10 +1304,6 @@ babel-extract-comments@^1.0.0: dependencies: babylon "^6.18.0" -babel-helper-vue-jsx-merge-props@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6" - babel-loader@^8.0.5: version "8.0.5" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.5.tgz#225322d7509c2157655840bba52e46b6c2f2fe33" @@ -1389,14 +1397,14 @@ blob@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" -bluebird@^3.1.1, bluebird@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" - bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" +bluebird@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" @@ -1590,7 +1598,7 @@ cacache@^10.0.4: unique-filename "^1.1.0" y18n "^4.0.0" -cacache@^11.0.2, cacache@^11.2.0: +cacache@^11.0.2, cacache@^11.3.2: version "11.3.2" resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" dependencies: @@ -1708,7 +1716,7 @@ chardet@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" -chokidar@^2.0.0, chokidar@^2.0.3: +chokidar@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" dependencies: @@ -1946,10 +1954,14 @@ colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" -commander@2.15.x, commander@^2.8.1, commander@~2.15.0: +commander@2.15.x, commander@~2.15.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" +commander@^2.19.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -2029,12 +2041,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -consolidate@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" - dependencies: - bluebird "^3.1.1" - constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -2348,6 +2354,10 @@ csso@^3.5.0: dependencies: css-tree "1.0.0-alpha.29" +csstype@^2.2.0: + version "2.6.5" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.5.tgz#1cd1dff742ebf4d7c991470ae71e12bb6751e034" + cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -2366,10 +2376,6 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -de-indent@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" - debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2529,6 +2535,10 @@ dom-serializer@0: domelementtype "~1.1.1" entities "~1.1.1" +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -2804,7 +2814,7 @@ estraverse@^4.1.0, estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" -esutils@^2.0.2: +esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -2973,6 +2983,10 @@ fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" @@ -3072,7 +3086,7 @@ follow-redirects@^1.0.0: dependencies: debug "=3.1.0" -follow-redirects@^1.2.3, follow-redirects@^1.3.0: +follow-redirects@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.0.tgz#234f49cf770b7f35b40e790f636ceba0c3a0ab77" dependencies: @@ -3142,10 +3156,6 @@ fs-minipass@^1.2.5: dependencies: minipass "^2.2.1" -fs-readdir-recursive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -3226,7 +3236,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.1.3: +glob@^7.0.3, glob@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" dependencies: @@ -3252,6 +3262,13 @@ global-modules-path@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.1.0.tgz#923ec524e8726bb0c1a4ed4b8e21e1ff80c88bbb" +global@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + dependencies: + min-document "^2.19.0" + process "~0.5.1" + globals@^11.1.0: version "11.5.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642" @@ -3356,10 +3373,6 @@ hash-base@^3.0.0: inherits "^2.0.1" safe-buffer "^5.0.1" -hash-sum@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" - hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.3" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" @@ -3367,7 +3380,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" -he@1.1.x, he@^1.1.0: +he@1.1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -3383,6 +3396,12 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hoist-non-react-statics@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.6.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.0.tgz#23235b29ab230c576aab0d4f13fc046b0b038222" @@ -3792,10 +3811,6 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" -is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -3880,7 +3895,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4030,7 +4045,7 @@ loader-utils@^1.0.2, loader-utils@^1.1.0: emojis-list "^2.0.0" json5 "^0.5.0" -loader-utils@^1.2.1: +loader-utils@^1.2.1, loader-utils@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" dependencies: @@ -4175,7 +4190,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -"lodash@>=3.5 <5", lodash@^4.17, lodash@^4.17.10, lodash@^4.17.3, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0: +"lodash@>=3.5 <5", lodash@^4.17.10, lodash@^4.17.3, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" @@ -4201,6 +4216,12 @@ loose-envify@^1.0.0: dependencies: js-tokens "^3.0.0" +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lower-case@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" @@ -4212,13 +4233,6 @@ lru-cache@^4.0.1, lru-cache@^4.1.1: pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@^4.1.2: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -4291,12 +4305,6 @@ merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - dependencies: - source-map "^0.6.1" - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -4348,6 +4356,12 @@ mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + dependencies: + dom-walk "^0.1.0" + mini-css-extract-plugin@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" @@ -4438,10 +4452,6 @@ mkdirp@0.5.x, mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~ dependencies: minimist "0.0.8" -moment@^2.18.1: - version "2.22.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -4791,14 +4801,6 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -output-file-sync@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-2.0.1.tgz#f53118282f5f553c2799541792b723a4c71430c0" - dependencies: - graceful-fs "^4.1.11" - is-plain-obj "^1.1.0" - mkdirp "^0.5.1" - p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -5627,10 +5629,6 @@ precss@^3.1.2: postcss-preset-env "^3.2.2" postcss-property-lookup "^2.0.0" -prettier@1.16.3: - version "1.16.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d" - pretty-error@^2.0.2: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" @@ -5654,10 +5652,22 @@ process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" +process@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" +prop-types@^15.6.1, prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + proxy-addr@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" @@ -5789,6 +5799,46 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-dom@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.6" + +react-hot-loader@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.9.0.tgz#498e5f76aae28bfd420efdabddf5af156745b4dd" + dependencies: + fast-levenshtein "^2.0.6" + global "^4.3.0" + hoist-non-react-statics "^3.3.0" + loader-utils "^1.1.0" + lodash "^4.17.11" + prop-types "^15.6.1" + react-lifecycles-compat "^3.0.4" + shallowequal "^1.0.2" + source-map "^0.7.3" + +react-is@^16.7.0, react-is@^16.8.1: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + +react@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.6" + read-cache@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" @@ -6141,6 +6191,13 @@ sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" +scheduler@^0.13.6: + version "0.13.6" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^0.4.4, schema-utils@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" @@ -6196,6 +6253,10 @@ serialize-javascript@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" +serialize-javascript@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" + serve-index@^1.7.2: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -6258,6 +6319,10 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +shallowequal@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -6278,10 +6343,6 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6358,6 +6419,13 @@ source-list-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" +source-map-loader@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.4.tgz#c18b0dc6e23bf66f6792437557c569a11e072271" + dependencies: + async "^2.5.0" + loader-utils "^1.1.0" + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" @@ -6368,6 +6436,13 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@~0.5.10: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@~0.5.9: version "0.5.10" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" @@ -6387,6 +6462,10 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + spdx-correct@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" @@ -6694,6 +6773,21 @@ terser-webpack-plugin@^1.1.0: webpack-sources "^1.1.0" worker-farm "^1.5.2" +terser-webpack-plugin@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz#69aa22426299f4b5b3775cbed8cb2c5d419aa1d4" + dependencies: + cacache "^11.3.2" + find-cache-dir "^2.0.0" + is-wsl "^1.1.0" + loader-utils "^1.2.3" + schema-utils "^1.0.0" + serialize-javascript "^1.7.0" + source-map "^0.6.1" + terser "^4.0.0" + webpack-sources "^1.3.0" + worker-farm "^1.7.0" + terser@^3.8.1: version "3.16.0" resolved "https://registry.yarnpkg.com/terser/-/terser-3.16.0.tgz#04028e6e5da461d91691cedd75fa53a17f2f20d9" @@ -6702,6 +6796,14 @@ terser@^3.8.1: source-map "~0.6.1" source-map-support "~0.5.9" +terser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.0.0.tgz#ef356f6f359a963e2cc675517f21c1c382877374" + dependencies: + commander "^2.19.0" + source-map "~0.6.1" + source-map-support "~0.5.10" + through2@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" @@ -6835,13 +6937,6 @@ uglify-js@^2.8.29: optionalDependencies: uglify-to-browserify "~1.0.0" -uglify-js@^3.0.0: - version "3.4.9" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" - dependencies: - commander "~2.17.1" - source-map "~0.6.1" - uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" @@ -6867,19 +6962,6 @@ uglifyjs-webpack-plugin@^1.2.4: webpack-sources "^1.1.0" worker-farm "^1.5.2" -uglifyjs-webpack-plugin@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.1.1.tgz#6937d7513a37280d4792f1fb536bef35e08e420a" - dependencies: - cacache "^11.2.0" - find-cache-dir "^2.0.0" - schema-utils "^1.0.0" - serialize-javascript "^1.4.0" - source-map "^0.6.1" - uglify-js "^3.0.0" - webpack-sources "^1.1.0" - worker-farm "^1.5.2" - unicode-canonical-property-names-ecmascript@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.3.tgz#f6119f417467593c0086357c85546b6ad5abc583" @@ -7060,18 +7142,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validator@^8.1.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-8.2.0.tgz#3c1237290e37092355344fef78c231249dab77b9" - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" -vee-validate@^2.1.7: - version "2.1.7" - resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.1.7.tgz#33984454d8a633cdc4825a2cf5857747d91bae67" - vendors@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" @@ -7093,90 +7167,6 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -vue-axios@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/vue-axios/-/vue-axios-2.1.1.tgz#6167f2db93065d9bcfc90e65d063ccf34f8b1d63" - -vue-devtools@^3.1.9: - version "3.1.9" - resolved "https://registry.yarnpkg.com/vue-devtools/-/vue-devtools-3.1.9.tgz#283b458c6853f569a987da0092e7c52e8243a436" - -vue-feather-icons@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/vue-feather-icons/-/vue-feather-icons-4.7.1.tgz#d8c55fbee7c9ad59689ebbaf07ad1e2f1e5c37da" - dependencies: - babel-helper-vue-jsx-merge-props "^2.0.2" - -vue-hot-reload-api@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2" - -vue-i18n@^8.6.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.6.0.tgz#63848b183cf6f100436bcdd84382416e43b07188" - -vue-loader@^15.6.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.6.2.tgz#892741d96260936ff69e892f72ec361ba4d100d2" - dependencies: - "@vue/component-compiler-utils" "^2.5.1" - hash-sum "^1.0.2" - loader-utils "^1.1.0" - vue-hot-reload-api "^2.3.0" - vue-style-loader "^4.1.0" - -vue-mc@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/vue-mc/-/vue-mc-0.2.4.tgz#93569cb6e08e2d1c52968a74cce8a6b2c9bda66a" - dependencies: - axios "^0.16" - lodash "^4.17" - moment "^2.18.1" - validator "^8.1.0" - vue "^2.2" - -vue-router@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9" - -vue-style-loader@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8" - dependencies: - hash-sum "^1.0.2" - loader-utils "^1.0.2" - -vue-template-compiler@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.4.tgz#0feacfe35e3386033bf4fe31ab4ff1dc1a0c5dec" - dependencies: - de-indent "^1.0.2" - he "^1.1.0" - -vue-template-es2015-compiler@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.8.2.tgz#dd73e80ba58bb65dd7a8aa2aeef6089cf6116f2a" - -vue@^2.2: - version "2.5.16" - resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085" - -vue@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.4.tgz#8a5a44e5740d8b8423a420c8655c97663421fb4d" - -vueify-insert-css@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/vueify-insert-css/-/vueify-insert-css-1.0.0.tgz#57e5d791907e8c9d87ae6de099a2174bd0a7f990" - -vuex-router-sync@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vuex-router-sync/-/vuex-router-sync-5.0.0.tgz#1a225c17a1dd9e2f74af0a1b2c62072e9492b305" - -vuex@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2" - watchpack@^1.4.0, watchpack@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" @@ -7278,10 +7268,6 @@ webpack-manifest-plugin@^2.0.3: lodash ">=3.5 <5" tapable "^1.0.0" -webpack-shell-plugin@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/webpack-shell-plugin/-/webpack-shell-plugin-0.5.0.tgz#29b8a1d80ddeae0ddb10e729667f728653c2c742" - webpack-sources@^1.0.0, webpack-sources@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" @@ -7436,6 +7422,12 @@ worker-farm@^1.5.2: dependencies: errno "~0.1.7" +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + dependencies: + errno "~0.1.7" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" From 0ab37682740c2b1f954df7a2e4e3ea724e3dc8bc Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 9 Jun 2019 17:38:33 -0700 Subject: [PATCH 03/42] Install eslint to enforce a style --- package.json | 13 +- resources/scripts/.eslintrc.yml | 36 ++ yarn.lock | 996 ++++++++++++++++++++------------ 3 files changed, 680 insertions(+), 365 deletions(-) create mode 100644 resources/scripts/.eslintrc.yml diff --git a/package.json b/package.json index 3ede1a38e..c5c9a3570 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "react": "^16.8.6", "react-dom": "^16.8.6", "react-hot-loader": "^4.9.0", + "react-router-dom": "^5.0.1", + "redux": "^4.0.1", "socket.io-client": "^2.2.0", "ws-wrapper": "^2.0.0", "xterm": "^3.5.1" @@ -25,10 +27,18 @@ "@types/react": "^16.8.19", "@types/react-dom": "^16.8.4", "@types/webpack-env": "^1.13.6", + "@typescript-eslint/eslint-plugin": "^1.10.1", + "@typescript-eslint/parser": "^1.10.1", "babel-loader": "^8.0.5", "clean-webpack-plugin": "^0.1.19", "css-loader": "^2.1.0", "cssnano": "^4.0.3", + "eslint": "^5.16.0", + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.17.3", + "eslint-plugin-node": "^9.1.0", + "eslint-plugin-promise": "^4.1.1", + "eslint-plugin-standard": "^4.0.0", "fork-ts-checker-webpack-plugin": "^0.5.2", "glob-all": "^3.1.0", "html-webpack-plugin": "^3.2.0", @@ -50,8 +60,7 @@ "webpack-assets-manifest": "^3.1.1", "webpack-cli": "^3.0.2", "webpack-dev-server": "^3.1.14", - "webpack-manifest-plugin": "^2.0.3", - "webpack-stream": "^4.0.3" + "webpack-manifest-plugin": "^2.0.3" }, "scripts": { "watch": "NODE_ENV=development ./node_modules/.bin/webpack --watch --progress", diff --git a/resources/scripts/.eslintrc.yml b/resources/scripts/.eslintrc.yml new file mode 100644 index 000000000..a5cfc529e --- /dev/null +++ b/resources/scripts/.eslintrc.yml @@ -0,0 +1,36 @@ +parser: "@typescript-eslint/parser" +parserOptions: + ecmaVersion: 6 + project: "./tsconfig.json" + tsconfigRootDir: "./" +env: + browser: true + es6: true +plugins: + - "@typescript-eslint" +extends: + - "standard" + - "plugin:@typescript-eslint/recommended" +rules: + semi: + - error + - always + comma-dangle: + - error + - always-multiline + "@typescript-eslint/explicit-function-return-type": 0 + "@typescript-eslint/explicit-member-accessibility": 0 + "@typescript-eslint/no-unused-vars": 0 + "@typescript-eslint/no-explicit-any": 0 + "@typescript-eslint/no-non-null-assertion": 0 +overrides: + - files: + - "**/*.tsx" + rules: + operator-linebreak: + - error + - before + - overrides: + "&&": "after" + "?": "ignore" + ":": "ignore" diff --git a/yarn.lock b/yarn.lock index a5a8188b0..2827bb211 100644 --- a/yarn.lock +++ b/yarn.lock @@ -605,6 +605,12 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" +"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + dependencies: + regenerator-runtime "^0.13.2" + "@babel/template@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" @@ -729,6 +735,10 @@ prop-types "^15.6.2" scheduler "^0.13.6" +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + "@types/feather-icons@^4.7.0": version "4.7.0" resolved "https://registry.yarnpkg.com/@types/feather-icons/-/feather-icons-4.7.0.tgz#ec66bc046bcd1513835f87541ecef54b50c57ec9" @@ -758,6 +768,39 @@ version "1.13.6" resolved "http://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976" +"@typescript-eslint/eslint-plugin@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.10.1.tgz#0a9e41f375d082363e63169049cd03ef0b6dd85e" + dependencies: + "@typescript-eslint/experimental-utils" "1.10.1" + eslint-utils "^1.3.1" + functional-red-black-tree "^1.0.1" + regexpp "^2.0.1" + tsutils "^3.7.0" + +"@typescript-eslint/experimental-utils@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.10.1.tgz#459eb096f38cf913b226bad08e92d67961d0d8d0" + dependencies: + "@typescript-eslint/typescript-estree" "1.10.1" + eslint-scope "^4.0.0" + +"@typescript-eslint/parser@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.10.1.tgz#ee119a3e399a0dfa834ce7840eb92a244ccf570c" + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "1.10.1" + "@typescript-eslint/typescript-estree" "1.10.1" + eslint-visitor-keys "^1.0.0" + +"@typescript-eslint/typescript-estree@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.10.1.tgz#e4a2c45498ed53ecbdc3d8019407b63a9c16fbda" + dependencies: + lodash.unescape "4.0.1" + semver "5.5.0" + "@webassemblyjs/ast@1.5.9": version "1.5.9" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.9.tgz#b2770182678691ab4949d593105c15d4074fedb6" @@ -1030,12 +1073,6 @@ accepts@~1.3.4, accepts@~1.3.5: mime-types "~2.1.18" negotiator "0.6.1" -acorn-dynamic-import@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" - dependencies: - acorn "^4.0.3" - acorn-dynamic-import@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" @@ -1046,9 +1083,9 @@ acorn-dynamic-import@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" -acorn@^4.0.3: - version "4.0.13" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" +acorn-jsx@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" acorn@^5.0.0: version "5.6.1" @@ -1058,6 +1095,10 @@ acorn@^6.0.5: version "6.0.6" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.6.tgz#cd75181670d5b99bdb1b1c993941d3a239ab1f56" +acorn@^6.0.7: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" + adjust-sourcemap-loader@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-1.2.0.tgz#e33fde95e50db9f2a802e3647e311d2fc5000c69" @@ -1091,24 +1132,19 @@ ajv@^6.1.0: json-schema-traverse "^0.3.0" uri-js "^4.2.1" -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" +ajv@^6.9.1: + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - dependencies: - ansi-wrap "^0.1.0" - ansi-colors@^3.0.0: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -1117,11 +1153,9 @@ ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - dependencies: - ansi-wrap "0.1.0" +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" ansi-html@0.0.7: version "0.0.7" @@ -1135,20 +1169,20 @@ ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: color-convert "^1.9.0" -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1197,6 +1231,13 @@ array-flatten@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -1233,6 +1274,10 @@ assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + async-each@^1.0.0, async-each@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -1245,12 +1290,6 @@ async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.1.2: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - dependencies: - lodash "^4.17.10" - async@^2.5.0: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" @@ -1564,10 +1603,6 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" @@ -1635,6 +1670,10 @@ callsite@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + camel-case@3.0.x: version "3.0.0" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" @@ -1646,7 +1685,7 @@ camelcase-css@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" -camelcase@^1.0.2, camelcase@^1.2.1: +camelcase@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -1679,13 +1718,6 @@ caniuse-lite@^1.0.30000929, caniuse-lite@^1.0.30000932: version "1.0.30000933" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000933.tgz#5871ff54b3177675ae1c2a275b2aae7abf2b9222" -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1704,7 +1736,7 @@ chalk@^2.0, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^2.4.2: +chalk@^2.1.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" dependencies: @@ -1716,6 +1748,10 @@ chardet@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + chokidar@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" @@ -1840,22 +1876,6 @@ cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -1864,26 +1884,6 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - -clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - -cloneable-readable@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.2.tgz#d591dee4a8f8bc15da43ce97dceeba13d43e2a65" - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - coa@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.1.tgz#f3f8b0b15073e35d70263fb1042cb2c023db38af" @@ -1928,10 +1928,6 @@ color-string@^1.4.0, color-string@^1.5.2: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - color@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/color/-/color-1.0.3.tgz#e48e832d85f14ef694fb468811c2d5cfe729b55d" @@ -2045,6 +2041,10 @@ constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -2376,7 +2376,7 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2394,13 +2394,19 @@ debug@^3.2.5: dependencies: ms "^2.1.1" +debug@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + dependencies: + ms "^2.1.1" + debug@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" dependencies: ms "^2.1.1" -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.2.0: +decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2422,6 +2428,10 @@ deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + default-gateway@^2.6.0: version "2.7.2" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f" @@ -2522,6 +2532,19 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + dependencies: + esutils "^2.0.2" + dom-converter@~0.1: version "0.1.4" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b" @@ -2613,6 +2636,10 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -2653,15 +2680,6 @@ engine.io-parser@~2.1.1: blob "0.0.5" has-binary2 "~1.0.2" -enhanced-resolve@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - object-assign "^4.0.1" - tapable "^0.2.7" - enhanced-resolve@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz#e34a6eaa790f62fccd71d93959f56b2b432db10a" @@ -2688,7 +2706,13 @@ errno@^0.1.3, errno@~0.1.7: dependencies: prr "~1.0.1" -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +error-ex@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" dependencies: @@ -2704,6 +2728,17 @@ es-abstract@^1.5.1, es-abstract@^1.6.1: is-callable "^1.1.3" is-regex "^1.0.4" +es-abstract@^1.7.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" + es-to-primitive@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" @@ -2712,7 +2747,15 @@ es-to-primitive@^1.1.1: is-date-object "^1.0.1" is-symbol "^1.0.1" -es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: version "0.10.45" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.45.tgz#0bfdf7b473da5919d5adf3bd25ceb754fccc3653" dependencies: @@ -2720,7 +2763,7 @@ es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: es6-symbol "~3.1.1" next-tick "1" -es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: +es6-iterator@^2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" dependencies: @@ -2728,43 +2771,13 @@ es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0 es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: +es6-symbol@^3.1.1, es6-symbol@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" dependencies: d "1" es5-ext "~0.10.14" -es6-weak-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -2773,14 +2786,65 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" +eslint-config-standard@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" + +eslint-import-resolver-node@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" + debug "^2.6.9" + resolve "^1.5.0" + +eslint-module-utils@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a" + dependencies: + debug "^2.6.8" + pkg-dir "^2.0.0" + +eslint-plugin-es@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz#475f65bb20c993fc10e8c8fe77d1d60068072da6" + dependencies: + eslint-utils "^1.3.0" + regexpp "^2.0.1" + +eslint-plugin-import@^2.17.3: + version "2.17.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz#00548b4434c18faebaba04b24ae6198f280de189" + dependencies: + array-includes "^3.0.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.0" + has "^1.0.3" + lodash "^4.17.11" + minimatch "^3.0.4" + read-pkg-up "^2.0.0" + resolve "^1.11.0" + +eslint-plugin-node@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-9.1.0.tgz#f2fd88509a31ec69db6e9606d76dabc5adc1b91a" + dependencies: + eslint-plugin-es "^1.4.0" + eslint-utils "^1.3.1" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.1.1.tgz#1e08cb68b5b2cd8839f8d5864c796f56d82746db" + +eslint-plugin-standard@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c" eslint-scope@^3.7.1, eslint-scope@~3.7.1: version "3.7.1" @@ -2796,21 +2860,87 @@ eslint-scope@^4.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.0, eslint-utils@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" + eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" +eslint@^5.16.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + dependencies: + estraverse "^4.0.0" + esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" dependencies: estraverse "^4.1.0" -estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" @@ -2822,13 +2952,6 @@ etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - "eventemitter3@>=2 <4", eventemitter3@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" @@ -2954,6 +3077,14 @@ external-editor@^3.0.0: iconv-lite "^0.4.22" tmp "^0.0.33" +external-editor@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -2967,14 +3098,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -fancy-log@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - time-stamp "^1.0.0" - fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" @@ -2983,7 +3106,7 @@ fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -3020,6 +3143,12 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + dependencies: + flat-cache "^2.0.1" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -3069,6 +3198,18 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" + flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" @@ -3187,6 +3328,10 @@ function-bind@^1.1.0, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + gather-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gather-stream/-/gather-stream-1.0.0.tgz#b33994af457a8115700d410f317733cbe7a0904b" @@ -3273,6 +3418,10 @@ globals@^11.1.0: version "11.5.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642" +globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -3291,6 +3440,10 @@ graceful-fs@^4.1.15: version "4.1.15" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + handle-thing@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" @@ -3315,14 +3468,14 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -3354,7 +3507,7 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.0: +has@^1.0.0, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" dependencies: @@ -3388,6 +3541,17 @@ hex-color-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" +history@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^2.2.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^0.4.0" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -3396,15 +3560,15 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" dependencies: react-is "^16.7.0" hosted-git-info@^2.1.4: - version "2.6.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.0.tgz#23235b29ab230c576aab0d4f13fc046b0b038222" + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" hpack.js@^2.1.6: version "2.1.6" @@ -3508,6 +3672,12 @@ iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -3532,12 +3702,27 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + +ignore@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.2.tgz#e28e584d43ad7e92f96995019cc43b9e1ac49558" + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" dependencies: import-from "^2.1.0" +import-fresh@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-from@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" @@ -3607,6 +3792,24 @@ inquirer@^6.0.0: strip-ansi "^4.0.0" through "^2.3.6" +inquirer@^6.2.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7" + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.11" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + internal-ip@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-3.0.1.tgz#df5c99876e1d2eb2ea2d74f520e3f669a00ece27" @@ -3614,7 +3817,7 @@ internal-ip@^3.0.1: default-gateway "^2.6.0" ipaddr.js "^1.5.2" -interpret@^1.0.0, interpret@^1.1.0: +interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" @@ -3682,16 +3885,14 @@ is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + is-color-stop@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" @@ -3845,6 +4046,12 @@ is-symbol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + dependencies: + has-symbols "^1.0.0" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3899,6 +4106,13 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" +js-yaml@^3.13.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@^3.9.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" @@ -3921,10 +4135,6 @@ jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" -json-loader@^0.5.4: - version "0.5.7" - resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" - json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -3933,11 +4143,19 @@ json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" -json5@^0.5.0, json5@^0.5.1: +json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -3995,10 +4213,6 @@ klaw@^1.0.0: optionalDependencies: graceful-fs "^4.1.9" -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -4015,6 +4229,13 @@ leb@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3" +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + load-json-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" @@ -4114,10 +4335,6 @@ lodash.assign@^4.0.1: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" -lodash.clone@^4.3.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" - lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4165,10 +4382,6 @@ lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" -lodash.some@^4.2.2: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" - lodash.template@^4.2.4: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" @@ -4186,6 +4399,10 @@ lodash.toarray@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" +lodash.unescape@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -4206,17 +4423,13 @@ long@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - loose-envify@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: js-tokens "^3.0.0" -loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: @@ -4294,7 +4507,7 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^2.0.0" -memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: +memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" dependencies: @@ -4362,6 +4575,14 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" +mini-create-react-context@^0.3.0: + version "0.3.2" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189" + dependencies: + "@babel/runtime" "^7.4.0" + gud "^1.0.0" + tiny-warning "^1.0.2" + mini-css-extract-plugin@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" @@ -4507,6 +4728,10 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + needle@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" @@ -4610,11 +4835,11 @@ nopt@^4.0.1: osenv "^0.1.4" normalize-package-data@^2.3.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" dependencies: hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" + resolve "^1.10.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" @@ -4692,6 +4917,10 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" @@ -4760,6 +4989,17 @@ opn@^5.1.0: dependencies: is-wsl "^1.1.0" +optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -4867,6 +5107,12 @@ param-case@2.1.x: dependencies: no-case "^2.2.0" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + dependencies: + callsites "^3.0.0" + parse-asn1@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" @@ -4926,7 +5172,7 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -4938,10 +5184,20 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -5003,15 +5259,6 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -plugin-error@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" - dependencies: - ansi-colors "^1.0.1" - arr-diff "^4.0.0" - arr-union "^3.1.0" - extend-shallow "^3.0.2" - portfinder@^1.0.9: version "1.0.20" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" @@ -5629,6 +5876,10 @@ precss@^3.1.2: postcss-preset-env "^3.2.2" postcss-property-lookup "^2.0.0" +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + pretty-error@^2.0.2: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" @@ -5644,7 +5895,7 @@ private@^0.1.6: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: +process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" @@ -5656,6 +5907,10 @@ process@~0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -5822,7 +6077,7 @@ react-hot-loader@^4.9.0: shallowequal "^1.0.2" source-map "^0.7.3" -react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" @@ -5830,6 +6085,33 @@ react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" +react-router-dom@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.0.1" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f" + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.3.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + react@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" @@ -5866,7 +6148,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: @@ -5919,6 +6201,13 @@ reduce-css-calc@^2.0.0: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" +redux@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + regenerate-unicode-properties@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-6.0.0.tgz#0fc26f9d5142289df4e177dec58f303d2d097c16" @@ -5939,6 +6228,10 @@ regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + regenerator-transform@^0.13.3: version "0.13.3" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb" @@ -5964,6 +6257,10 @@ regexp-tree@^0.1.0: colors "^1.1.2" yargs "^12.0.5" +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -6046,14 +6343,10 @@ repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -6080,6 +6373,14 @@ resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + +resolve-pathname@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" + resolve-url-loader@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.0.0.tgz#c47eeca1fc8d62b08dc2eef12b3af5af3e775c74" @@ -6106,6 +6407,12 @@ resolve@^1.1.7, resolve@^1.3.2: dependencies: path-parse "^1.0.5" +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.5.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" + dependencies: + path-parse "^1.0.6" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -6136,11 +6443,11 @@ rgba-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" dependencies: - align-text "^0.1.1" + glob "^7.1.3" rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: version "2.6.2" @@ -6173,6 +6480,12 @@ rxjs@^6.1.0: dependencies: tslib "^1.9.0" +rxjs@^6.4.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -6223,7 +6536,11 @@ selfsigned@^1.9.1: dependencies: node-forge "0.7.5" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.1: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + +semver@5.5.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -6231,6 +6548,10 @@ semver@^5.0.1, semver@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" +semver@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" + send@0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" @@ -6343,6 +6664,14 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6454,7 +6783,7 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" -source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1: +source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -6467,15 +6796,15 @@ source-map@^0.7.3: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" spdx-correct@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" spdx-expression-parse@^3.0.0: version "3.0.0" @@ -6485,8 +6814,8 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + version "3.0.4" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1" spdy-transport@^3.0.0: version "3.0.0" @@ -6593,6 +6922,14 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + string.prototype.repeat@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" @@ -6625,6 +6962,12 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + dependencies: + ansi-regex "^4.1.0" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -6640,7 +6983,7 @@ strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" -strip-json-comments@~2.0.1: +strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -6669,12 +7012,6 @@ supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - dependencies: - has-flag "^2.0.0" - supports-color@^5.1.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -6712,6 +7049,19 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" +symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + +table@^5.2.3: + version "5.4.0" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.0.tgz#d772a3216e68829920a41a32c18eda286c95d780" + dependencies: + ajv "^6.9.1" + lodash "^4.17.11" + slice-ansi "^2.1.0" + string-width "^3.0.0" + tailwindcss@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-0.7.4.tgz#fb7926821d42eacdc12e6621a49d21f37a3ff9e9" @@ -6732,10 +7082,6 @@ tailwindcss@^0.7.4: pretty-hrtime "^1.0.3" strip-comments "^1.0.2" -tapable@^0.2.7: - version "0.2.8" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" - tapable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" @@ -6804,6 +7150,10 @@ terser@^4.0.0: source-map "~0.6.1" source-map-support "~0.5.10" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + through2@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" @@ -6811,7 +7161,7 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" -through@^2.3.6, through@^2.3.8: +through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -6819,10 +7169,6 @@ thunky@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826" -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - timers-browserify@^2.0.4: version "2.0.10" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" @@ -6833,6 +7179,14 @@ timsort@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" +tiny-invariant@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463" + +tiny-warning@^1.0.0, tiny-warning@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -6891,14 +7245,30 @@ ts-loader@^5.3.3: micromatch "^3.1.4" semver "^5.0.1" +tslib@^1.8.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + tslib@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e" +tsutils@^3.7.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.14.0.tgz#bf8d5a7bae5369331fa0f2b0a5a10bd7f7396c77" + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + type-is@~1.6.16: version "1.6.16" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" @@ -6928,27 +7298,6 @@ uglify-js@3.3.x: commander "~2.15.0" source-map "~0.6.1" -uglify-js@^2.8.29: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -uglifyjs-webpack-plugin@^0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" - dependencies: - source-map "^0.5.6" - uglify-js "^2.8.29" - webpack-sources "^1.0.1" - uglifyjs-webpack-plugin@^1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz#2ef8387c8f1a903ec5e44fa36f9f3cbdcea67641" @@ -7062,7 +7411,7 @@ upper-case@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" -uri-js@^4.2.1: +uri-js@^4.2.1, uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" dependencies: @@ -7136,12 +7485,16 @@ v8-compile-cache@^2.0.0: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a" validate-npm-package-license@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +value-equal@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -7150,24 +7503,13 @@ vendors@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" -vinyl@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - vm-browserify@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" dependencies: indexof "0.0.1" -watchpack@^1.4.0, watchpack@^1.5.0: +watchpack@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" dependencies: @@ -7282,47 +7624,6 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-stream@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/webpack-stream/-/webpack-stream-4.0.3.tgz#96399fd7911b94c264bfc59e356738a89b5ca136" - dependencies: - fancy-log "^1.3.2" - lodash.clone "^4.3.2" - lodash.some "^4.2.2" - memory-fs "^0.4.1" - plugin-error "^1.0.1" - supports-color "^5.3.0" - through "^2.3.8" - vinyl "^2.1.0" - webpack "^3.4.1" - -webpack@^3.4.1: - version "3.12.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.12.0.tgz#3f9e34360370602fcf639e97939db486f4ec0d74" - dependencies: - acorn "^5.0.0" - acorn-dynamic-import "^2.0.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - async "^2.1.2" - enhanced-resolve "^3.4.0" - escope "^3.6.0" - interpret "^1.0.0" - json-loader "^0.5.4" - json5 "^0.5.1" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - mkdirp "~0.5.0" - node-libs-browser "^2.0.0" - source-map "^0.5.3" - supports-color "^4.2.1" - tapable "^0.2.7" - uglifyjs-webpack-plugin "^0.4.6" - watchpack "^1.4.0" - webpack-sources "^1.0.1" - yargs "^8.0.2" - webpack@^4.29.0: version "4.29.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.0.tgz#f2cfef83f7ae404ba889ff5d43efd285ca26e750" @@ -7408,13 +7709,9 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" worker-farm@^1.5.2: version "1.6.0" @@ -7443,6 +7740,12 @@ write-file-stdout@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/write-file-stdout/-/write-file-stdout-0.0.2.tgz#c252d7c7c5b1b402897630e3453c7bfe690d9ca1" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + dependencies: + mkdirp "^0.5.1" + ws-wrapper@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ws-wrapper/-/ws-wrapper-2.0.0.tgz#598f67a9fdf13d532abaf593d8ce8889f94b5a2a" @@ -7500,12 +7803,6 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" - dependencies: - camelcase "^4.1.0" - yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" @@ -7580,39 +7877,12 @@ yargs@^12.0.5: y18n "^3.2.1 || ^4.0.0" yargs-parser "^11.1.1" -yargs@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - yargs@~1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b" dependencies: minimist "^0.1.0" -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" From d9f30294ded67ef57b1ec2cf519f1bbac3e8fe18 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 9 Jun 2019 19:26:20 -0700 Subject: [PATCH 04/42] Migrate the existing login form to use React --- package.json | 5 + .../assets/styles/components/animations.css | 71 ++--------- resources/scripts/api/auth/login.ts | 25 ++++ resources/scripts/api/http.ts | 42 +++++++ resources/scripts/components/App.tsx | 9 +- resources/scripts/components/MessageBox.tsx | 14 +++ .../components/auth/LoginContainer.tsx | 111 ++++++++++++++++++ .../components/forms/OpenInputField.tsx | 30 +++++ .../scripts/routers/AuthenticationRouter.tsx | 26 ++++ .../pterodactyl/templates/auth/core.blade.php | 5 +- .../pterodactyl/templates/base/core.blade.php | 2 +- .../pterodactyl/templates/wrapper.blade.php | 4 +- routes/base.php | 4 +- tsconfig.json | 1 + yarn.lock | 45 ++++++- 15 files changed, 322 insertions(+), 72 deletions(-) create mode 100644 resources/scripts/api/auth/login.ts create mode 100644 resources/scripts/api/http.ts create mode 100644 resources/scripts/components/MessageBox.tsx create mode 100644 resources/scripts/components/auth/LoginContainer.tsx create mode 100644 resources/scripts/components/forms/OpenInputField.tsx create mode 100644 resources/scripts/routers/AuthenticationRouter.tsx diff --git a/package.json b/package.json index c5c9a3570..d182b39b0 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "@hot-loader/react-dom": "^16.8.6", "axios": "^0.18.0", "brace": "^0.11.1", + "classnames": "^2.2.6", "date-fns": "^1.29.0", "feather-icons": "^4.10.0", "jquery": "^3.3.1", @@ -12,6 +13,7 @@ "react-dom": "^16.8.6", "react-hot-loader": "^4.9.0", "react-router-dom": "^5.0.1", + "react-transition-group": "^4.1.0", "redux": "^4.0.1", "socket.io-client": "^2.2.0", "ws-wrapper": "^2.0.0", @@ -22,10 +24,13 @@ "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/preset-env": "^7.3.1", "@babel/preset-react": "^7.0.0", + "@types/classnames": "^2.2.8", "@types/feather-icons": "^4.7.0", "@types/lodash": "^4.14.119", "@types/react": "^16.8.19", "@types/react-dom": "^16.8.4", + "@types/react-router-dom": "^4.3.3", + "@types/react-transition-group": "^2.9.2", "@types/webpack-env": "^1.13.6", "@typescript-eslint/eslint-plugin": "^1.10.1", "@typescript-eslint/parser": "^1.10.1", diff --git a/resources/assets/styles/components/animations.css b/resources/assets/styles/components/animations.css index 25b3985a4..d76279725 100644 --- a/resources/assets/styles/components/animations.css +++ b/resources/assets/styles/components/animations.css @@ -1,68 +1,19 @@ -.animate { - &.fadein { - animation: fadein 500ms; - } -} - -.animated-fade-in { - animation: fadein 500ms; +/*! purgecss start ignore */ +.fade-enter { + @apply .opacity-0; } .fade-enter-active { - animation: fadein 500ms; + @apply .opacity-100; + transition: opacity 150ms; } -.fade-leave-active { - animation: fadein 500ms reverse; +.fade-exit { + @apply .opacity-100; } -@keyframes fadein { - from { opacity: 0; } - to { opacity: 1; } -} - -@keyframes onlineblink { - 0% { - @apply .bg-green-500; - } - 100% { - @apply .bg-green-600; - } -} - -@keyframes offlineblink { - 0% { - @apply .bg-red-500; - } - 100% { - @apply .bg-red-600; - } -} - -/* - * transition="modal" - */ -.modal-enter, .modal-leave-active { - opacity: 0; -} - -.modal-enter .modal-container, -.modal-leave-active .modal-container { - animation: opacity 250ms linear; -} - -/** - * name="slide-fade" mode="out-in" - */ -.slide-fade-enter-active { - transition: all 250ms ease; -} - -.slide-fade-leave-active { - transition: all 250ms cubic-bezier(1.0, 0.5, 0.8, 1.0); -} - -.slide-fade-enter, .slide-fade-leave-to { - transform: translateX(10px); - opacity: 0; +.fade-exit-active { + @apply .opacity-0; + transition: opacity 150ms; } +/*! purgecss end ignore */ diff --git a/resources/scripts/api/auth/login.ts b/resources/scripts/api/auth/login.ts new file mode 100644 index 000000000..742c0b17c --- /dev/null +++ b/resources/scripts/api/auth/login.ts @@ -0,0 +1,25 @@ +import http from '@/api/http'; + +interface LoginResponse { + complete: boolean; + intended?: string; + token?: string; +} + +export default (user: string, password: string): Promise => { + return new Promise((resolve, reject) => { + http.post('/auth/login', { user, password }) + .then(response => { + if (!(response.data instanceof Object)) { + return reject(new Error('An error occurred while processing the login request.')); + } + + return resolve({ + complete: response.data.complete, + intended: response.data.intended || undefined, + token: response.data.token || undefined, + }); + }) + .catch(reject); + }); +}; diff --git a/resources/scripts/api/http.ts b/resources/scripts/api/http.ts new file mode 100644 index 000000000..1c8359d97 --- /dev/null +++ b/resources/scripts/api/http.ts @@ -0,0 +1,42 @@ +import axios, { AxiosInstance } from 'axios'; + +// This token is set in the bootstrap.js file at the beginning of the request +// and is carried through from there. +// const token: string = ''; + +const http: AxiosInstance = axios.create({ + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, +}); + +// If we have a phpdebugbar instance registered at this point in time go +// ahead and route the response data through to it so things show up. +// @ts-ignore +if (typeof window.phpdebugbar !== 'undefined') { + http.interceptors.response.use(response => { + // @ts-ignore + window.phpdebugbar.ajaxHandler.handle(response.request); + + return response; + }); +} + +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 { + if (error.response && error.response.data) { + const { data } = error.response; + if (data.errors && data.errors[0] && data.errors[0].detail) { + return data.errors[0].detail; + } + } + + return error.message; +} diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index a0b85c996..69f482e7d 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -1,10 +1,17 @@ import * as React from 'react'; import { hot } from 'react-hot-loader/root'; +import { BrowserRouter as Router, Route } from 'react-router-dom'; +import AuthenticationRouter from '@/routers/AuthenticationRouter'; class App extends React.PureComponent { render () { return ( -

Hello

+ +
+ + +
+
); } } diff --git a/resources/scripts/components/MessageBox.tsx b/resources/scripts/components/MessageBox.tsx new file mode 100644 index 000000000..8ebf11553 --- /dev/null +++ b/resources/scripts/components/MessageBox.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; + +interface Props { + title?: string; + message: string; + type?: 'success' | 'info' | 'warning' | 'error'; +} + +export default ({ title, message, type }: Props) => ( +
+ {title && {title}} + {message} +
+); diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx new file mode 100644 index 000000000..db0b947fa --- /dev/null +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -0,0 +1,111 @@ +import * as React from 'react'; +import OpenInputField from '@/components/forms/OpenInputField'; +import { Link } from 'react-router-dom'; +import login from '@/api/auth/login'; +import { httpErrorToHuman } from '@/api/http'; +import MessageBox from '@/components/MessageBox'; + +type State = Readonly<{ + errorMessage?: string; + isLoading: boolean; + username?: string; + password?: string; +}>; + +export default class LoginContainer extends React.PureComponent<{}, State> { + username = React.createRef(); + + state: State = { + isLoading: false, + }; + + submit = (e: React.FormEvent) => { + e.preventDefault(); + + const { username, password } = this.state; + + this.setState({ isLoading: true }, () => { + login(username!, password!) + .then(response => { + + }) + .catch(error => this.setState({ + isLoading: false, + errorMessage: httpErrorToHuman(error), + }, () => console.error(error))); + }); + }; + + canSubmit () { + if (!this.state.username || !this.state.password) { + return false; + } + + return this.state.username.length > 0 && this.state.password.length > 0; + } + + // @ts-ignore + handleFieldUpdate = (e: React.ChangeEvent) => this.setState({ + [e.target.id]: e.target.value, + }); + + render () { + return ( + + {this.state.errorMessage && +
+ +
+ } +
+
+ +
+
+ +
+
+ +
+
+ + Forgot password? + +
+
+
+ ); + } +} diff --git a/resources/scripts/components/forms/OpenInputField.tsx b/resources/scripts/components/forms/OpenInputField.tsx new file mode 100644 index 000000000..0f7e2603b --- /dev/null +++ b/resources/scripts/components/forms/OpenInputField.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import classNames from 'classnames'; + +type Props = React.InputHTMLAttributes & { + label: string; +}; + +export default ({ className, onChange, label, ...props }: Props) => { + const [ value, setValue ] = React.useState(''); + + const classes = classNames('input open-label', { + 'has-content': value && value.length > 0, + }); + + return ( +
+ { + setValue(e.target.value); + if (onChange) { + onChange(e); + } + }} + {...props} + /> + +
+ ); +}; diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx new file mode 100644 index 000000000..45de3165c --- /dev/null +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { BrowserRouter, Route, Switch } from 'react-router-dom'; +import LoginContainer from '@/components/auth/LoginContainer'; +import { CSSTransition, TransitionGroup } from 'react-transition-group'; + +export default class AuthenticationRouter extends React.PureComponent { + render () { + return ( + + ( + + + + + + + + + + )} + /> + + ); + } +} diff --git a/resources/themes/pterodactyl/templates/auth/core.blade.php b/resources/themes/pterodactyl/templates/auth/core.blade.php index 988509c3d..da27fcc95 100644 --- a/resources/themes/pterodactyl/templates/auth/core.blade.php +++ b/resources/themes/pterodactyl/templates/auth/core.blade.php @@ -4,10 +4,7 @@ @section('container')
- - +

{!! trans('strings.copyright', ['year' => date('Y')]) !!}

diff --git a/resources/themes/pterodactyl/templates/base/core.blade.php b/resources/themes/pterodactyl/templates/base/core.blade.php index bf3b37c1a..6fdb68687 100644 --- a/resources/themes/pterodactyl/templates/base/core.blade.php +++ b/resources/themes/pterodactyl/templates/base/core.blade.php @@ -1,7 +1,7 @@ @extends('templates/wrapper') @section('container') - +
@endsection @section('below-container') diff --git a/resources/themes/pterodactyl/templates/wrapper.blade.php b/resources/themes/pterodactyl/templates/wrapper.blade.php index 4424038d2..cbfa5bfc0 100644 --- a/resources/themes/pterodactyl/templates/wrapper.blade.php +++ b/resources/themes/pterodactyl/templates/wrapper.blade.php @@ -35,9 +35,7 @@ @section('content') @yield('above-container') -
- @yield('container') -
+ @yield('container') @yield('below-container') @show @section('scripts') diff --git a/routes/base.php b/routes/base.php index dbdad9712..e90fa7ef8 100644 --- a/routes/base.php +++ b/routes/base.php @@ -32,5 +32,5 @@ Route::group(['prefix' => 'account/two_factor'], function () { Route::post('/totp/disable', 'SecurityController@delete')->name('account.two_factor.disable'); }); -Route::get('/{vue}', 'IndexController@index') - ->where('vue', '^(?!(\/)?(api|admin|daemon)).+'); +Route::get('/{react}', 'IndexController@index') + ->where('react', '^(?!(\/)?(api|auth|admin|daemon)).+'); diff --git a/tsconfig.json b/tsconfig.json index 5336d7ffc..454ab76ff 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "moduleResolution": "node", "sourceMap": true, "baseUrl": ".", + "lib": ["es2015", "dom"], "paths": { "@/*": [ "./resources/scripts/*" diff --git a/yarn.lock b/yarn.lock index 2827bb211..3718ced67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -735,6 +735,10 @@ prop-types "^15.6.2" scheduler "^0.13.6" +"@types/classnames@^2.2.8": + version "2.2.8" + resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.8.tgz#17139e1e1104203572caa4368f6796f6225b70b4" + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -743,6 +747,10 @@ version "4.7.0" resolved "https://registry.yarnpkg.com/@types/feather-icons/-/feather-icons-4.7.0.tgz#ec66bc046bcd1513835f87541ecef54b50c57ec9" +"@types/history@*": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220" + "@types/lodash@^4.14.119": version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" @@ -757,6 +765,27 @@ dependencies: "@types/react" "*" +"@types/react-router-dom@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.3.tgz#7837e3e9fefbc84a8f6c8a51dca004f4e83e94e3" + dependencies: + "@types/history" "*" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.0.1.tgz#9f4548c75755c55b0cffdd743080e5afa87da6dd" + dependencies: + "@types/history" "*" + "@types/react" "*" + +"@types/react-transition-group@^2.9.2": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.9.2.tgz#c48cf2a11977c8b4ff539a1c91d259eaa627028d" + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^16.8.19": version "16.8.19" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.19.tgz#629154ef05e2e1985cdde94477deefd823ad9be3" @@ -1841,7 +1870,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.5: +classnames@^2.2.5, classnames@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" @@ -2551,6 +2580,12 @@ dom-converter@~0.1: dependencies: utila "~0.3" +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + dependencies: + "@babel/runtime" "^7.1.2" + dom-serializer@0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" @@ -6112,6 +6147,14 @@ react-router@5.0.1: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-transition-group@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.1.0.tgz#7b50c0a93a6c127336187252c3c1a70eff3304ce" + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" From b93b40ba316d87b345c91284759ed7e8a029dfce Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 11 Jun 2019 22:02:18 -0700 Subject: [PATCH 05/42] Begin working on password reset page --- .../assets/styles/components/animations.css | 9 +++ .../api/auth/requestPasswordResetEmail.ts | 9 +++ resources/scripts/api/http.ts | 1 + .../auth/ForgotPasswordContainer.tsx | 75 +++++++++++++++++++ .../components/auth/LoginContainer.tsx | 2 +- .../components/forms/OpenInputField.tsx | 8 +- .../scripts/routers/AuthenticationRouter.tsx | 24 ++++-- .../pterodactyl/templates/auth/core.blade.php | 3 - 8 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 resources/scripts/api/auth/requestPasswordResetEmail.ts create mode 100644 resources/scripts/components/auth/ForgotPasswordContainer.tsx diff --git a/resources/assets/styles/components/animations.css b/resources/assets/styles/components/animations.css index d76279725..97c1ced7c 100644 --- a/resources/assets/styles/components/animations.css +++ b/resources/assets/styles/components/animations.css @@ -16,4 +16,13 @@ @apply .opacity-0; transition: opacity 150ms; } + +/** @todo fix this, hides footer stuff */ +div.route-transition-group { + @apply .relative; + + & section { + @apply .absolute .w-full .pin-t .pin-l; + } +} /*! purgecss end ignore */ diff --git a/resources/scripts/api/auth/requestPasswordResetEmail.ts b/resources/scripts/api/auth/requestPasswordResetEmail.ts new file mode 100644 index 000000000..f456560bb --- /dev/null +++ b/resources/scripts/api/auth/requestPasswordResetEmail.ts @@ -0,0 +1,9 @@ +import http from '@/api/http'; + +export default (email: string): Promise => { + return new Promise((resolve, reject) => { + http.post('/auth/password', { email }) + .then(() => resolve()) + .catch(reject); + }); +}; diff --git a/resources/scripts/api/http.ts b/resources/scripts/api/http.ts index 1c8359d97..676a735c7 100644 --- a/resources/scripts/api/http.ts +++ b/resources/scripts/api/http.ts @@ -9,6 +9,7 @@ const http: AxiosInstance = axios.create({ 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json', 'Content-Type': 'application/json', + 'X-CSRF-Token': (window as any).X_CSRF_TOKEN as string || '', }, }); diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx new file mode 100644 index 000000000..f41de737b --- /dev/null +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import OpenInputField from '@/components/forms/OpenInputField'; +import { Link } from 'react-router-dom'; +import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail'; + +type Props = Readonly<{ + +}>; + +type State = Readonly<{ + email: string; + isSubmitting: boolean; +}>; + +export default class ForgotPasswordContainer extends React.PureComponent { + state: State = { + email: '', + isSubmitting: false, + }; + + handleFieldUpdate = (e: React.ChangeEvent) => this.setState({ + email: e.target.value, + }); + + handleSubmission = (e: React.FormEvent) => this.setState({ isSubmitting: true }, () => { + e.preventDefault(); + + requestPasswordResetEmail(this.state.email) + .then(() => { + + }) + .catch(console.error) + .then(() => this.setState({ isSubmitting: false })); + }); + + render () { + return ( + +
+
+ +
+
+ +
+
+ + Return to Login + +
+
+
+ ); + } +} diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index db0b947fa..3ccae9923 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -98,7 +98,7 @@ export default class LoginContainer extends React.PureComponent<{}, State> {
Forgot password? diff --git a/resources/scripts/components/forms/OpenInputField.tsx b/resources/scripts/components/forms/OpenInputField.tsx index 0f7e2603b..92161ef21 100644 --- a/resources/scripts/components/forms/OpenInputField.tsx +++ b/resources/scripts/components/forms/OpenInputField.tsx @@ -3,9 +3,10 @@ import classNames from 'classnames'; type Props = React.InputHTMLAttributes & { label: string; + description?: string; }; -export default ({ className, onChange, label, ...props }: Props) => { +export default ({ className, description, onChange, label, ...props }: Props) => { const [ value, setValue ] = React.useState(''); const classes = classNames('input open-label', { @@ -25,6 +26,11 @@ export default ({ className, onChange, label, ...props }: Props) => { {...props} /> + {description && +

+ {description} +

+ }
); }; diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx index 45de3165c..c7d6b3ee6 100644 --- a/resources/scripts/routers/AuthenticationRouter.tsx +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import LoginContainer from '@/components/auth/LoginContainer'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer'; export default class AuthenticationRouter extends React.PureComponent { render () { @@ -9,13 +10,24 @@ export default class AuthenticationRouter extends React.PureComponent { ( - + - - - - - +
+ + + + + +

+ © 2015 - 2019  + + Pterodactyl Software + +

+
)} diff --git a/resources/themes/pterodactyl/templates/auth/core.blade.php b/resources/themes/pterodactyl/templates/auth/core.blade.php index da27fcc95..6c106020b 100644 --- a/resources/themes/pterodactyl/templates/auth/core.blade.php +++ b/resources/themes/pterodactyl/templates/auth/core.blade.php @@ -5,8 +5,5 @@ @section('container')
-

- {!! trans('strings.copyright', ['year' => date('Y')]) !!} -

@endsection From 435626f4b7b2d25239b74a9ca7f5556e76b71bb0 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 11 Jun 2019 23:12:03 -0700 Subject: [PATCH 06/42] Add support for flash messages utilizing redux --- package.json | 4 ++ .../assets/styles/components/spinners.css | 33 ++++++++++++ resources/scripts/components/App.tsx | 25 ++++++--- .../scripts/components/FlashMessageRender.tsx | 38 ++++++++++++++ resources/scripts/components/MessageBox.tsx | 12 +++-- .../auth/ForgotPasswordContainer.tsx | 51 ++++++++++++++----- .../components/auth/LoginContainer.tsx | 8 ++- resources/scripts/index.tsx | 2 +- resources/scripts/redux/actions/flash.ts | 17 +++++++ resources/scripts/redux/configure.ts | 14 +++++ resources/scripts/redux/reducers.ts | 7 +++ resources/scripts/redux/reducers/flash.ts | 21 ++++++++ resources/scripts/redux/types.d.ts | 17 +++++++ .../scripts/routers/AuthenticationRouter.tsx | 6 ++- yarn.lock | 47 +++++++++++++++-- 15 files changed, 268 insertions(+), 34 deletions(-) create mode 100644 resources/scripts/components/FlashMessageRender.tsx create mode 100644 resources/scripts/redux/actions/flash.ts create mode 100644 resources/scripts/redux/configure.ts create mode 100644 resources/scripts/redux/reducers.ts create mode 100644 resources/scripts/redux/reducers/flash.ts create mode 100644 resources/scripts/redux/types.d.ts diff --git a/package.json b/package.json index d182b39b0..f1e43863f 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "pterodactyl-panel", "dependencies": { "@hot-loader/react-dom": "^16.8.6", + "@types/react-redux": "^7.0.9", "axios": "^0.18.0", "brace": "^0.11.1", "classnames": "^2.2.6", @@ -12,9 +13,11 @@ "react": "^16.8.6", "react-dom": "^16.8.6", "react-hot-loader": "^4.9.0", + "react-redux": "^7.1.0", "react-router-dom": "^5.0.1", "react-transition-group": "^4.1.0", "redux": "^4.0.1", + "redux-persist": "^5.10.0", "socket.io-client": "^2.2.0", "ws-wrapper": "^2.0.0", "xterm": "^3.5.1" @@ -31,6 +34,7 @@ "@types/react-dom": "^16.8.4", "@types/react-router-dom": "^4.3.3", "@types/react-transition-group": "^2.9.2", + "@types/redux-persist": "^4.3.1", "@types/webpack-env": "^1.13.6", "@typescript-eslint/eslint-plugin": "^1.10.1", "@typescript-eslint/parser": "^1.10.1", diff --git a/resources/assets/styles/components/spinners.css b/resources/assets/styles/components/spinners.css index f5591f280..2141e76a6 100644 --- a/resources/assets/styles/components/spinners.css +++ b/resources/assets/styles/components/spinners.css @@ -56,3 +56,36 @@ transform: rotate(360deg); } } + +.spinner-circle { + @apply .w-8 .h-8; + border: 3px solid hsla(211, 12%, 43%, 0.2); + border-top-color: hsl(211, 12%, 43%); + border-radius: 50%; + animation: spin 1s cubic-bezier(0.55, 0.25, 0.25, 0.70) infinite; + + &.spinner-sm { + @apply .w-4 .h-4 .border-2; + } + + &.spinner-lg { + @apply .w-16 .h-16; + border-width: 6px; + } + + &.spinner-blue { + border: 3px solid hsla(212, 92%, 43%, 0.2); + border-top-color: hsl(212, 92%, 43%); + } + + &.spinner-white { + border: 3px solid rgba(255, 255, 255, 0.2); + border-top-color: rgb(255, 255, 255); + } +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 69f482e7d..9405bf524 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -2,16 +2,29 @@ import * as React from 'react'; import { hot } from 'react-hot-loader/root'; import { BrowserRouter as Router, Route } from 'react-router-dom'; import AuthenticationRouter from '@/routers/AuthenticationRouter'; +import { Provider } from 'react-redux'; +import { persistor, store } from '@/redux/configure'; +import { PersistGate } from 'redux-persist/integration/react'; class App extends React.PureComponent { render () { return ( - -
- - -
-
+ + + +
+ + +
+
+
+
+ ); + } + + renderLoading () { + return ( +
); } } diff --git a/resources/scripts/components/FlashMessageRender.tsx b/resources/scripts/components/FlashMessageRender.tsx new file mode 100644 index 000000000..356e536c2 --- /dev/null +++ b/resources/scripts/components/FlashMessageRender.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { FlashMessage, ReduxState } from '@/redux/types'; +import { connect } from 'react-redux'; +import MessageBox from '@/components/MessageBox'; + +type Props = Readonly<{ + flashes: FlashMessage[]; +}>; + +class FlashMessageRender extends React.PureComponent { + render () { + if (this.props.flashes.length === 0) { + return null; + } + + return ( + + { + this.props.flashes.map(flash => ( + + {flash.message} + + )) + } + + ) + } +} + +const mapStateToProps = (state: ReduxState) => ({ + flashes: state.flashes, +}); + +export default connect(mapStateToProps)(FlashMessageRender); diff --git a/resources/scripts/components/MessageBox.tsx b/resources/scripts/components/MessageBox.tsx index 8ebf11553..a962afb88 100644 --- a/resources/scripts/components/MessageBox.tsx +++ b/resources/scripts/components/MessageBox.tsx @@ -1,14 +1,18 @@ import * as React from 'react'; +export type FlashMessageType = 'success' | 'info' | 'warning' | 'error'; + interface Props { title?: string; - message: string; - type?: 'success' | 'info' | 'warning' | 'error'; + children: string; + type?: FlashMessageType; } -export default ({ title, message, type }: Props) => ( +export default ({ title, children, type }: Props) => (
{title && {title}} - {message} + + {children} +
); diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index f41de737b..dfea4f38b 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -2,9 +2,14 @@ import * as React from 'react'; import OpenInputField from '@/components/forms/OpenInputField'; import { Link } from 'react-router-dom'; import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail'; +import { connect } from 'react-redux'; +import { ReduxState } from '@/redux/types'; +import { pushFlashMessage, clearAllFlashMessages } from '@/redux/actions/flash'; +import { httpErrorToHuman } from '@/api/http'; type Props = Readonly<{ - + pushFlashMessage: typeof pushFlashMessage; + clearAllFlashMessages: typeof clearAllFlashMessages; }>; type State = Readonly<{ @@ -12,7 +17,7 @@ type State = Readonly<{ isSubmitting: boolean; }>; -export default class ForgotPasswordContainer extends React.PureComponent { +class ForgotPasswordContainer extends React.PureComponent { state: State = { email: '', isSubmitting: false, @@ -22,16 +27,27 @@ export default class ForgotPasswordContainer extends React.PureComponent) => this.setState({ isSubmitting: true }, () => { + handleSubmission = (e: React.FormEvent) => { e.preventDefault(); - requestPasswordResetEmail(this.state.email) - .then(() => { - - }) - .catch(console.error) - .then(() => this.setState({ isSubmitting: false })); - }); + this.setState({ isSubmitting: true }, () => { + this.props.clearAllFlashMessages(); + requestPasswordResetEmail(this.state.email) + .then(() => { + // @todo actually handle this. + }) + .catch(error => { + console.error(error); + this.props.pushFlashMessage({ + id: 'auth:forgot-password', + type: 'error', + title: 'Error', + message: httpErrorToHuman(error), + }); + }) + .then(() => this.setState({ isSubmitting: false })); + }); + }; render () { return ( @@ -50,11 +66,11 @@ export default class ForgotPasswordContainer extends React.PureComponent
+
+
+ + Return to Login + +
+ +
+ ); + } +} + +const mapDispatchToProps = { + pushFlashMessage, + clearAllFlashMessages, +}; + +export default connect(null, mapDispatchToProps)(ResetPasswordContainer); diff --git a/resources/scripts/components/forms/OpenInputField.tsx b/resources/scripts/components/forms/OpenInputField.tsx index 3e25a9c56..ab0fa0485 100644 --- a/resources/scripts/components/forms/OpenInputField.tsx +++ b/resources/scripts/components/forms/OpenInputField.tsx @@ -4,13 +4,18 @@ import classNames from 'classnames'; type Props = React.InputHTMLAttributes & { label: string; description?: string; + value?: string; }; -export default React.forwardRef(({ className, description, onChange, label, ...props }, ref) => { - const [ value, setValue ] = React.useState(''); +export default React.forwardRef(({ className, description, onChange, label, value, ...props }, ref) => { + const [ stateValue, setStateValue ] = React.useState(value); + + if (value !== stateValue) { + setStateValue(value); + } const classes = classNames('input open-label', { - 'has-content': value && value.length > 0, + 'has-content': stateValue && stateValue.length > 0, }); return ( @@ -19,16 +24,17 @@ export default React.forwardRef(({ className, descripti ref={ref} className={classes} onChange={e => { - setValue(e.target.value); + setStateValue(e.target.value); if (onChange) { onChange(e); } }} + value={typeof value !== 'undefined' ? (stateValue || '') : undefined} {...props} /> {description && -

+

{description}

} diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx index 6f59c3511..285725727 100644 --- a/resources/scripts/routers/AuthenticationRouter.tsx +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -4,6 +4,7 @@ import LoginContainer from '@/components/auth/LoginContainer'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer'; import FlashMessageRender from '@/components/FlashMessageRender'; +import ResetPasswordContainer from '@/components/auth/ResetPasswordContainer'; export default class AuthenticationRouter extends React.PureComponent { render () { @@ -14,12 +15,11 @@ export default class AuthenticationRouter extends React.PureComponent {
-
- -
+ - + +

diff --git a/yarn.lock b/yarn.lock index d5955f2b4..56197c658 100644 --- a/yarn.lock +++ b/yarn.lock @@ -766,6 +766,12 @@ version "15.7.1" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" +"@types/query-string@^6.3.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-6.3.0.tgz#b6fa172a01405abcaedac681118e78429d62ea39" + dependencies: + query-string "*" + "@types/react-dom@^16.8.4": version "16.8.4" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.4.tgz#7fb7ba368857c7aa0f4e4511c4710ca2c5a12a88" @@ -6064,6 +6070,14 @@ qs@6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" +query-string@*, query-string@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.7.0.tgz#7e92bf8525140cf8c5ebf500f26716b0de5b7023" + dependencies: + decode-uri-component "^0.2.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -6918,6 +6932,10 @@ spdy@^4.0.0: select-hose "^2.0.0" spdy-transport "^3.0.0" +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -6987,6 +7005,10 @@ stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" From 2a626a3e1f94afcd997a2f8276c49ff436d573f6 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 16 Jun 2019 18:07:57 -0700 Subject: [PATCH 10/42] Kinda working checkpoint magic --- resources/assets/styles/components/forms.css | 1 + .../auth/LoginCheckpointContainer.tsx | 105 ++++++++++++++++++ .../components/auth/LoginContainer.tsx | 12 +- .../scripts/routers/AuthenticationRouter.tsx | 4 +- 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 resources/scripts/components/auth/LoginCheckpointContainer.tsx diff --git a/resources/assets/styles/components/forms.css b/resources/assets/styles/components/forms.css index ffa96c39d..b083a8f79 100644 --- a/resources/assets/styles/components/forms.css +++ b/resources/assets/styles/components/forms.css @@ -50,6 +50,7 @@ input[type=number] { */ .input:not(.open-label) { @apply .appearance-none .p-3 .rounded .border .border-neutral-200 .text-neutral-800 .w-full; + min-width: 0; transition: border 150ms linear; &:focus { diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx new file mode 100644 index 000000000..b2fe6ad52 --- /dev/null +++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx @@ -0,0 +1,105 @@ +import * as React from 'react'; +import { RouteComponentProps } from 'react-router'; +import { connect } from 'react-redux'; +import { pushFlashMessage, clearAllFlashMessages } from '@/redux/actions/flash'; +import NetworkErrorMessage from '@/components/NetworkErrorMessage'; + +type State = Readonly<{ + isLoading: boolean; + errorMessage?: string; + code: string; +}>; + +class LoginCheckpointContainer extends React.PureComponent { + state: State = { + code: '', + isLoading: false, + }; + + moveToNextInput (e: React.KeyboardEvent, isBackspace: boolean = false) { + const form = e.currentTarget.form; + + if (form) { + const index = Array.prototype.indexOf.call(form, e.currentTarget); + const element = form.elements[index + (isBackspace ? -1 : 1)]; + + // @ts-ignore + element && element.focus(); + } + } + + handleNumberInput = (e: React.KeyboardEvent) => { + const number = Number(e.key); + if (isNaN(number)) { + return; + } + + this.setState(s => ({ code: s.code + number.toString() })); + this.moveToNextInput(e); + }; + + handleBackspace = (e: React.KeyboardEvent) => { + const isBackspace = e.key === 'Delete' || e.key === 'Backspace'; + + if (!isBackspace || e.currentTarget.value.length > 0) { + e.currentTarget.value = ''; + return; + } + + this.setState(s => ({ code: s.code.substring(0, s.code.length - 2) })); + e.currentTarget.value = ''; + this.moveToNextInput(e, true); + }; + + render () { + return ( + +

+ Device Checkpoint +

+ +
null}> +

+ This account is protected with two-factor authentication. Please provide an authentication + code from your device in order to continue. +

+
+ { + [1, 2, 3, 4, 5, 6].map((_, index) => ( + + )) + } +
+
+ +
+
+ + ); + } +} + +const mapDispatchToProps = { + pushFlashMessage, + clearAllFlashMessages, +}; + +export default connect(null, mapDispatchToProps)(LoginCheckpointContainer); diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index 787f60f50..6a388a451 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import OpenInputField from '@/components/forms/OpenInputField'; -import { Link } from 'react-router-dom'; +import { Link, RouteComponentProps } from 'react-router-dom'; import login from '@/api/auth/login'; import { httpErrorToHuman } from '@/api/http'; import NetworkErrorMessage from '@/components/NetworkErrorMessage'; @@ -12,7 +12,7 @@ type State = Readonly<{ password?: string; }>; -export default class LoginContainer extends React.PureComponent<{}, State> { +export default class LoginContainer extends React.PureComponent { username = React.createRef(); state: State = { @@ -27,7 +27,15 @@ export default class LoginContainer extends React.PureComponent<{}, State> { this.setState({ isLoading: true }, () => { login(username!, password!) .then(response => { + if (response.complete) { + // @ts-ignore + window.location = response.intended || '/'; + return; + } + this.props.history.replace('/login/checkpoint', { + token: response.token, + }); }) .catch(error => this.setState({ isLoading: false, diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx index 285725727..673278c7e 100644 --- a/resources/scripts/routers/AuthenticationRouter.tsx +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -5,6 +5,7 @@ import { CSSTransition, TransitionGroup } from 'react-transition-group'; import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer'; import FlashMessageRender from '@/components/FlashMessageRender'; import ResetPasswordContainer from '@/components/auth/ResetPasswordContainer'; +import LoginCheckpointContainer from '@/components/auth/LoginCheckpointContainer'; export default class AuthenticationRouter extends React.PureComponent { render () { @@ -17,7 +18,8 @@ export default class AuthenticationRouter extends React.PureComponent {
- + + From 06ff76e2e93dabfcabd3dae5431d8fe942f6c510 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 21 Jun 2019 21:04:42 -0700 Subject: [PATCH 11/42] Fix security vulnerability in file manager allowing XSS attack --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- .../pterodactyl/js/frontend/files/filemanager.min.js | 4 ++-- .../js/frontend/files/filemanager.min.js.map | 2 +- .../pterodactyl/js/frontend/files/src/actions.js | 12 ++++++++---- .../pterodactyl/js/frontend/files/src/contextmenu.js | 2 +- .../themes/pterodactyl/server/files/index.blade.php | 2 +- 7 files changed, 25 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 417434183..b765b87c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ 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. +## v0.7.14 (Derelict Dermodactylus) +### Fixed +* **[SECURITY]** Fixes an XSS vulnerability when performing certain actions in the file manager. + +### Changed +* Support for setting a node to listen on ports lower than 1024. +* Regenerated database passwords now respect the same settings that were used when initially created. +* Cleaned up 2FA QR code generation to use a more up-to-date library and API. +* Console charts now properly start at 0 and scale based on server configuration. No more crazy spikes that +are due to a change of one unit. + ## v0.7.13 (Derelict Dermodactylus) ### Fixed * Fixes a bug with the location update API endpoint throwing an error due to an unexected response value. diff --git a/README.md b/README.md index 5831a8709..8ff13e8c8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Logo Image](https://cdn.pterodactyl.io/logos/Banner%20Logo%20Black@2x.png)](https://pterodactyl.io) +[![Logo Image](https://cdn.pterodactyl.io/logos/new/pterodactyl_logo.png)](https://pterodactyl.io) [![Build status](https://img.shields.io/travis/pterodactyl/panel/develop.svg?style=flat-square)](https://travis-ci.org/pterodactyl/panel) [![StyleCI](https://styleci.io/repos/47508644/shield?branch=develop)](https://styleci.io/repos/47508644) diff --git a/public/themes/pterodactyl/js/frontend/files/filemanager.min.js b/public/themes/pterodactyl/js/frontend/files/filemanager.min.js index eeb7370b3..67f9a8900 100644 --- a/public/themes/pterodactyl/js/frontend/files/filemanager.min.js +++ b/public/themes/pterodactyl/js/frontend/files/filemanager.min.js @@ -1,5 +1,5 @@ -'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i\n \n ';nameBlock.html(attachEditor);var inputField=nameBlock.find('input');var inputLoader=nameBlock.find('.input-loader');inputField.focus();inputField.on('blur keydown',function(e){if(e.type==='keydown'&&e.which===27||e.type==='blur'||e.type==='keydown'&&e.which===13&¤tName===inputField.val()){if(!_.isEmpty(currentLink)){nameBlock.html(currentLink)}else{nameBlock.html(currentName)}inputField.remove();ContextMenu.unbind().run();return}if(e.type==='keydown'&&e.which!==13)return;inputLoader.show();var currentPath=decodeURIComponent(nameBlock.data('path'));$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/rename',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+currentPath+inputField.val()})}).done(function(data){nameBlock.attr('data-name',inputField.val());if(!_.isEmpty(currentLink)){var newLink=currentLink.attr('href');if(nameBlock.parent().data('type')!=='folder'){newLink=newLink.substr(0,newLink.lastIndexOf('/'))+'/'+inputField.val()}currentLink.attr('href',newLink);nameBlock.html(currentLink.html(inputField.val()))}else{nameBlock.html(inputField.val())}inputField.remove()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}nameBlock.addClass('has-error').delay(2000).queue(function(){nameBlock.removeClass('has-error').dequeue()});inputField.popover({animation:true,placement:'top',content:error,title:'Save Error'}).popover('show')}).always(function(){inputLoader.remove();ContextMenu.unbind().run()})})}},{key:'copy',value:function copy(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var currentPath=decodeURIComponent(nameBlock.data('path'));swal({type:'input',title:'Copy File',text:'Please enter the new path for the copied file below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:''+currentPath+currentName},function(val){if(val===false){return false}$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/copy',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+val})}).done(function(data){swal({type:'success',title:'',text:'File successfully copied.'});Files.list()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'download',value:function download(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var fileName=decodeURIComponent(nameBlock.attr('data-name'));var filePath=decodeURIComponent(nameBlock.data('path'));window.location='/server/'+Pterodactyl.server.uuidShort+'/files/download/'+filePath+fileName}},{key:'delete',value:function _delete(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var delPath=decodeURIComponent(nameBlock.data('path'));var delName=decodeURIComponent(nameBlock.data('name'));swal({type:'warning',title:'',text:'Are you sure you want to delete '+delName+'?',html:true,showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true},function(){$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/delete',timeout:10000,data:JSON.stringify({items:[''+delPath+delName]})}).done(function(data){nameBlock.parent().addClass('warning').delay(200).fadeOut();swal({type:'success',title:'File Deleted'})}).fail(function(jqXHR){console.error(jqXHR);swal({type:'error',title:'Whoops!',html:true,text:'An error occurred while attempting to delete this file. Please try again.'})})})}},{key:'toggleMassActions',value:function toggleMassActions(){if($('#file_listing input[type="checkbox"]:checked').length){$('#mass_actions').removeClass('disabled')}else{$('#mass_actions').addClass('disabled')}}},{key:'toggleHighlight',value:function toggleHighlight(event){var parent=$(event.currentTarget);var item=$(event.currentTarget).find('input');if($(item).is(':checked')){$(item).prop('checked',false);parent.removeClass('warning').delay(200)}else{$(item).prop('checked',true);parent.addClass('warning').delay(200)}}},{key:'highlightAll',value:function highlightAll(event){var parent=void 0;var item=$(event.currentTarget).find('input');if($(item).is(':checked')){$('#file_listing input[type=checkbox]').prop('checked',false);$('#file_listing input[data-action="addSelection"]').each(function(){parent=$(this).closest('tr');parent.removeClass('warning').delay(200)})}else{$('#file_listing input[type=checkbox]').prop('checked',true);$('#file_listing input[data-action="addSelection"]').each(function(){parent=$(this).closest('tr');parent.addClass('warning').delay(200)})}}},{key:'deleteSelected',value:function deleteSelected(){var selectedItems=[];var selectedItemsElements=[];var parent=void 0;var nameBlock=void 0;var delLocation=void 0;$('#file_listing input[data-action="addSelection"]:checked').each(function(){parent=$(this).closest('tr');nameBlock=$(parent).find('td[data-identifier="name"]');delLocation=decodeURIComponent(nameBlock.data('path'))+decodeURIComponent(nameBlock.data('name'));selectedItems.push(delLocation);selectedItemsElements.push(parent)});if(selectedItems.length!=0){var formattedItems='';var i=0;$.each(selectedItems,function(key,value){formattedItems+=''+value+', ';i++;return i<5});formattedItems=formattedItems.slice(0,-2);if(selectedItems.length>5){formattedItems+=', and '+(selectedItems.length-5)+' other(s)'}swal({type:'warning',title:'',text:'Are you sure you want to delete the following files: '+formattedItems+'?',html:true,showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true},function(){$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/delete',timeout:10000,data:JSON.stringify({items:selectedItems})}).done(function(data){$('#file_listing input:checked').each(function(){$(this).prop('checked',false)});$.each(selectedItemsElements,function(){$(this).addClass('warning').delay(200).fadeOut()});swal({type:'success',title:'Files Deleted'})}).fail(function(jqXHR){console.error(jqXHR);swal({type:'error',title:'Whoops!',html:true,text:'An error occurred while attempting to delete these files. Please try again.'})})})}else{swal({type:'warning',title:'',text:'Please select files/folders to delete.'})}}},{key:'decompress',value:function decompress(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));swal({title:' Decompressing...',text:'This might take a few seconds to complete.',html:true,allowOutsideClick:false,allowEscapeKey:false,showConfirmButton:false});$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/decompress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName})}).done(function(data){swal.close();Files.list(compPath)}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:error})})}},{key:'compress',value:function compress(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/compress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName,to:compPath.toString()})}).done(function(data){Files.list(compPath,function(err){if(err)return;var fileListing=$('#file_listing').find('[data-name="'+data.saved_as+'"]').parent();fileListing.addClass('success pulsate').delay(3000).queue(function(){fileListing.removeClass('success pulsate').dequeue()})})}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:error})})}}]);return ActionsClass}(); -'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i New File
  • New Folder
  • '}if(Pterodactyl.permissions.downloadFiles||Pterodactyl.permissions.deleteFiles){buildMenu+='
  • '}if(Pterodactyl.permissions.downloadFiles){buildMenu+=''}if(Pterodactyl.permissions.deleteFiles){buildMenu+='
  • Delete
  • '}buildMenu+='';return buildMenu}},{key:'rightClick',value:function rightClick(){var _this=this;$('[data-action="toggleMenu"]').on('mousedown',function(event){event.preventDefault();if($(document).find('#fileOptionMenu').is(':visible')){$('body').trigger('click');return}_this.showMenu(event)});$('#file_listing > tbody td').on('contextmenu',function(event){_this.showMenu(event)})}},{key:'showMenu',value:function showMenu(event){var _this2=this;var parent=$(event.target).closest('tr');var menu=$(this.makeMenu(parent));if(parent.data('type')==='disabled')return;event.preventDefault();$(menu).appendTo('body');$(menu).data('invokedOn',$(event.target)).show().css({position:'absolute',left:event.pageX-150,top:event.pageY});this.activeLine=parent;this.activeLine.addClass('active');var Actions=new ActionsClass(parent,menu);if(Pterodactyl.permissions.moveFiles){$(menu).find('li[data-action="move"]').unbind().on('click',function(e){e.preventDefault();Actions.move()});$(menu).find('li[data-action="rename"]').unbind().on('click',function(e){e.preventDefault();Actions.rename()})}if(Pterodactyl.permissions.copyFiles){$(menu).find('li[data-action="copy"]').unbind().on('click',function(e){e.preventDefault();Actions.copy()})}if(Pterodactyl.permissions.compressFiles){if(parent.data('type')==='folder'){$(menu).find('li[data-action="compress"]').removeClass('hidden')}$(menu).find('li[data-action="compress"]').unbind().on('click',function(e){e.preventDefault();Actions.compress()})}if(Pterodactyl.permissions.decompressFiles){if(_.without(['application/zip','application/gzip','application/x-gzip'],parent.data('mime')).length<3){$(menu).find('li[data-action="decompress"]').removeClass('hidden')}$(menu).find('li[data-action="decompress"]').unbind().on('click',function(e){e.preventDefault();Actions.decompress()})}if(Pterodactyl.permissions.createFiles){$(menu).find('li[data-action="folder"]').unbind().on('click',function(e){e.preventDefault();Actions.folder()})}if(Pterodactyl.permissions.downloadFiles){if(parent.data('type')==='file'){$(menu).find('li[data-action="download"]').removeClass('hidden')}$(menu).find('li[data-action="download"]').unbind().on('click',function(e){e.preventDefault();Actions.download()})}if(Pterodactyl.permissions.deleteFiles){$(menu).find('li[data-action="delete"]').unbind().on('click',function(e){e.preventDefault();Actions.delete()})}$(window).unbind().on('click',function(event){if($(event.target).is('.disable-menu-hide')){event.preventDefault();return}$(menu).unbind().remove();if(!_.isNull(_this2.activeLine))_this2.activeLine.removeClass('active')})}},{key:'directoryClick',value:function directoryClick(){$('a[data-action="directory-view"]').on('click',function(event){event.preventDefault();var path=$(this).parent().data('path')||'';var name=$(this).parent().data('name')||'';window.location.hash=encodeURIComponent(path+name);Files.list()})}}]);return ContextMenuClass}();window.ContextMenu=new ContextMenuClass; +'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i').text(value).html()}},{key:'folder',value:function folder(path){var inputValue=void 0;if(path){inputValue=path}else{var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.data('name'));var currentPath=decodeURIComponent(nameBlock.data('path'));if($(this.element).data('type')==='file'){inputValue=currentPath}else{inputValue=''+currentPath+currentName+'/'}}swal({type:'input',title:'Create Folder',text:'Please enter the path and folder name below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:inputValue},function(val){if(val===false){return false}$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/folder',timeout:10000,data:JSON.stringify({path:val})}).done(function(data){swal.close();Files.list()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'move',value:function move(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var currentPath=decodeURIComponent(nameBlock.data('path'));swal({type:'input',title:'Move File',text:'Please enter the new path for the file below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:''+currentPath+currentName},function(val){if(val===false){return false}$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/move',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+val})}).done(function(data){nameBlock.parent().addClass('warning').delay(200).fadeOut();swal.close()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'rename',value:function rename(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentLink=nameBlock.find('a');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var attachEditor='\n \n \n ';nameBlock.html(attachEditor);var inputField=nameBlock.find('input');var inputLoader=nameBlock.find('.input-loader');inputField.focus();inputField.on('blur keydown',function(e){if(e.type==='keydown'&&e.which===27||e.type==='blur'||e.type==='keydown'&&e.which===13&¤tName===inputField.val()){if(!_.isEmpty(currentLink)){nameBlock.html(currentLink)}else{nameBlock.html(currentName)}inputField.remove();ContextMenu.unbind().run();return}if(e.type==='keydown'&&e.which!==13)return;inputLoader.show();var currentPath=decodeURIComponent(nameBlock.data('path'));$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/rename',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+currentPath+inputField.val()})}).done(function(data){nameBlock.attr('data-name',inputField.val());if(!_.isEmpty(currentLink)){var newLink=currentLink.attr('href');if(nameBlock.parent().data('type')!=='folder'){newLink=newLink.substr(0,newLink.lastIndexOf('/'))+'/'+inputField.val()}currentLink.attr('href',newLink);nameBlock.html(currentLink.html(inputField.val()))}else{nameBlock.html(inputField.val())}inputField.remove()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}nameBlock.addClass('has-error').delay(2000).queue(function(){nameBlock.removeClass('has-error').dequeue()});inputField.popover({animation:true,placement:'top',content:error,title:'Save Error'}).popover('show')}).always(function(){inputLoader.remove();ContextMenu.unbind().run()})})}},{key:'copy',value:function copy(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var currentName=decodeURIComponent(nameBlock.attr('data-name'));var currentPath=decodeURIComponent(nameBlock.data('path'));swal({type:'input',title:'Copy File',text:'Please enter the new path for the copied file below.',showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true,inputValue:''+currentPath+currentName},function(val){if(val===false){return false}$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/copy',timeout:10000,data:JSON.stringify({from:''+currentPath+currentName,to:''+val})}).done(function(data){swal({type:'success',title:'',text:'File successfully copied.'});Files.list()}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'',text:error})})})}},{key:'download',value:function download(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var fileName=decodeURIComponent(nameBlock.attr('data-name'));var filePath=decodeURIComponent(nameBlock.data('path'));window.location='/server/'+Pterodactyl.server.uuidShort+'/files/download/'+filePath+fileName}},{key:'delete',value:function _delete(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var delPath=decodeURIComponent(nameBlock.data('path'));var delName=decodeURIComponent(nameBlock.data('name'));swal({type:'warning',title:'',text:'Are you sure you want to delete '+this.sanitizedString(delName)+'?',html:true,showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true},function(){$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/delete',timeout:10000,data:JSON.stringify({items:[''+delPath+delName]})}).done(function(data){nameBlock.parent().addClass('warning').delay(200).fadeOut();swal({type:'success',title:'File Deleted'})}).fail(function(jqXHR){console.error(jqXHR);swal({type:'error',title:'Whoops!',html:true,text:'An error occurred while attempting to delete this file. Please try again.'})})})}},{key:'toggleMassActions',value:function toggleMassActions(){if($('#file_listing input[type="checkbox"]:checked').length){$('#mass_actions').removeClass('disabled')}else{$('#mass_actions').addClass('disabled')}}},{key:'toggleHighlight',value:function toggleHighlight(event){var parent=$(event.currentTarget);var item=$(event.currentTarget).find('input');if($(item).is(':checked')){$(item).prop('checked',false);parent.removeClass('warning').delay(200)}else{$(item).prop('checked',true);parent.addClass('warning').delay(200)}}},{key:'highlightAll',value:function highlightAll(event){var parent=void 0;var item=$(event.currentTarget).find('input');if($(item).is(':checked')){$('#file_listing input[type=checkbox]').prop('checked',false);$('#file_listing input[data-action="addSelection"]').each(function(){parent=$(this).closest('tr');parent.removeClass('warning').delay(200)})}else{$('#file_listing input[type=checkbox]').prop('checked',true);$('#file_listing input[data-action="addSelection"]').each(function(){parent=$(this).closest('tr');parent.addClass('warning').delay(200)})}}},{key:'deleteSelected',value:function deleteSelected(){var selectedItems=[];var selectedItemsElements=[];var parent=void 0;var nameBlock=void 0;var delLocation=void 0;$('#file_listing input[data-action="addSelection"]:checked').each(function(){parent=$(this).closest('tr');nameBlock=$(parent).find('td[data-identifier="name"]');delLocation=decodeURIComponent(nameBlock.data('path'))+decodeURIComponent(nameBlock.data('name'));selectedItems.push(delLocation);selectedItemsElements.push(parent)});if(selectedItems.length!=0){var formattedItems='';var i=0;$.each(selectedItems,function(key,value){formattedItems+=''+this.sanitizedString(value)+', ';i++;return i<5});formattedItems=formattedItems.slice(0,-2);if(selectedItems.length>5){formattedItems+=', and '+(selectedItems.length-5)+' other(s)'}swal({type:'warning',title:'',text:'Are you sure you want to delete the following files: '+this.sanitizedString(formattedItems)+'?',html:true,showCancelButton:true,showConfirmButton:true,closeOnConfirm:false,showLoaderOnConfirm:true},function(){$.ajax({type:'POST',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/delete',timeout:10000,data:JSON.stringify({items:selectedItems})}).done(function(data){$('#file_listing input:checked').each(function(){$(this).prop('checked',false)});$.each(selectedItemsElements,function(){$(this).addClass('warning').delay(200).fadeOut()});swal({type:'success',title:'Files Deleted'})}).fail(function(jqXHR){console.error(jqXHR);swal({type:'error',title:'Whoops!',html:true,text:'An error occurred while attempting to delete these files. Please try again.'})})})}else{swal({type:'warning',title:'',text:'Please select files/folders to delete.'})}}},{key:'decompress',value:function decompress(){var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));swal({title:' Decompressing...',text:'This might take a few seconds to complete.',html:true,allowOutsideClick:false,allowEscapeKey:false,showConfirmButton:false});$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/decompress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName})}).done(function(data){swal.close();Files.list(compPath)}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:error})})}},{key:'compress',value:function compress(){var _this=this;var nameBlock=$(this.element).find('td[data-identifier="name"]');var compPath=decodeURIComponent(nameBlock.data('path'));var compName=decodeURIComponent(nameBlock.data('name'));$.ajax({type:'POST',url:Pterodactyl.node.scheme+'://'+Pterodactyl.node.fqdn+':'+Pterodactyl.node.daemonListen+'/v1/server/file/compress',headers:{'X-Access-Token':Pterodactyl.server.daemonSecret,'X-Access-Server':Pterodactyl.server.uuid},contentType:'application/json; charset=utf-8',data:JSON.stringify({files:''+compPath+compName,to:compPath.toString()})}).done(function(data){Files.list(compPath,function(err){if(err)return;var fileListing=$('#file_listing').find('[data-name="'+data.saved_as+'"]').parent();fileListing.addClass('success pulsate').delay(3000).queue(function(){fileListing.removeClass('success pulsate').dequeue()})})}).fail(function(jqXHR){console.error(jqXHR);var error='An error occurred while trying to process this request.';if(typeof jqXHR.responseJSON!=='undefined'&&typeof jqXHR.responseJSON.error!=='undefined'){error=jqXHR.responseJSON.error}swal({type:'error',title:'Whoops!',html:true,text:_this.sanitizedString(error)})})}}]);return ActionsClass}(); +'use strict';var _createClass=function(){function defineProperties(target,props){for(var i=0;i').text(newFilePath).html()+'" class="text-muted"> New File
  • New Folder
  • '}if(Pterodactyl.permissions.downloadFiles||Pterodactyl.permissions.deleteFiles){buildMenu+='
  • '}if(Pterodactyl.permissions.downloadFiles){buildMenu+=''}if(Pterodactyl.permissions.deleteFiles){buildMenu+='
  • Delete
  • '}buildMenu+='';return buildMenu}},{key:'rightClick',value:function rightClick(){var _this=this;$('[data-action="toggleMenu"]').on('mousedown',function(event){event.preventDefault();if($(document).find('#fileOptionMenu').is(':visible')){$('body').trigger('click');return}_this.showMenu(event)});$('#file_listing > tbody td').on('contextmenu',function(event){_this.showMenu(event)})}},{key:'showMenu',value:function showMenu(event){var _this2=this;var parent=$(event.target).closest('tr');var menu=$(this.makeMenu(parent));if(parent.data('type')==='disabled')return;event.preventDefault();$(menu).appendTo('body');$(menu).data('invokedOn',$(event.target)).show().css({position:'absolute',left:event.pageX-150,top:event.pageY});this.activeLine=parent;this.activeLine.addClass('active');var Actions=new ActionsClass(parent,menu);if(Pterodactyl.permissions.moveFiles){$(menu).find('li[data-action="move"]').unbind().on('click',function(e){e.preventDefault();Actions.move()});$(menu).find('li[data-action="rename"]').unbind().on('click',function(e){e.preventDefault();Actions.rename()})}if(Pterodactyl.permissions.copyFiles){$(menu).find('li[data-action="copy"]').unbind().on('click',function(e){e.preventDefault();Actions.copy()})}if(Pterodactyl.permissions.compressFiles){if(parent.data('type')==='folder'){$(menu).find('li[data-action="compress"]').removeClass('hidden')}$(menu).find('li[data-action="compress"]').unbind().on('click',function(e){e.preventDefault();Actions.compress()})}if(Pterodactyl.permissions.decompressFiles){if(_.without(['application/zip','application/gzip','application/x-gzip'],parent.data('mime')).length<3){$(menu).find('li[data-action="decompress"]').removeClass('hidden')}$(menu).find('li[data-action="decompress"]').unbind().on('click',function(e){e.preventDefault();Actions.decompress()})}if(Pterodactyl.permissions.createFiles){$(menu).find('li[data-action="folder"]').unbind().on('click',function(e){e.preventDefault();Actions.folder()})}if(Pterodactyl.permissions.downloadFiles){if(parent.data('type')==='file'){$(menu).find('li[data-action="download"]').removeClass('hidden')}$(menu).find('li[data-action="download"]').unbind().on('click',function(e){e.preventDefault();Actions.download()})}if(Pterodactyl.permissions.deleteFiles){$(menu).find('li[data-action="delete"]').unbind().on('click',function(e){e.preventDefault();Actions.delete()})}$(window).unbind().on('click',function(event){if($(event.target).is('.disable-menu-hide')){event.preventDefault();return}$(menu).unbind().remove();if(!_.isNull(_this2.activeLine))_this2.activeLine.removeClass('active')})}},{key:'directoryClick',value:function directoryClick(){$('a[data-action="directory-view"]').on('click',function(event){event.preventDefault();var path=$(this).parent().data('path')||'';var name=$(this).parent().data('name')||'';window.location.hash=encodeURIComponent(path+name);Files.list()})}}]);return ContextMenuClass}();window.ContextMenu=new ContextMenuClass; 'use strict';var _typeof=typeof Symbol==='function'&&typeof Symbol.iterator==='symbol'?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==='function'&&obj.constructor===Symbol&&obj!==Symbol.prototype?'symbol':typeof obj};var _createClass=function(){function defineProperties(target,props){for(var i=0;i\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass ActionsClass {\n constructor(element, menu) {\n this.element = element;\n this.menu = menu;\n }\n\n destroy() {\n this.element = undefined;\n }\n\n folder(path) {\n let inputValue\n if (path) {\n inputValue = path\n } else {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.data('name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n if ($(this.element).data('type') === 'file') {\n inputValue = currentPath;\n } else {\n inputValue = `${currentPath}${currentName}/`;\n }\n }\n\n swal({\n type: 'input',\n title: 'Create Folder',\n text: 'Please enter the path and folder name below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: inputValue\n }, (val) => {\n if (val === false) {\n return false;\n }\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/folder`,\n timeout: 10000,\n data: JSON.stringify({\n path: val,\n }),\n }).done(data => {\n swal.close();\n Files.list();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n }\n\n move() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n swal({\n type: 'input',\n title: 'Move File',\n text: 'Please enter the new path for the file below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: `${currentPath}${currentName}`,\n }, (val) => {\n if (val === false) {\n return false;\n }\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/move`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${val}`,\n }),\n }).done(data => {\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\n swal.close();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n\n }\n\n rename() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentLink = nameBlock.find('a');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const attachEditor = `\n \n \n `;\n\n nameBlock.html(attachEditor);\n const inputField = nameBlock.find('input');\n const inputLoader = nameBlock.find('.input-loader');\n\n inputField.focus();\n inputField.on('blur keydown', e => {\n // Save Field\n if (\n (e.type === 'keydown' && e.which === 27)\n || e.type === 'blur'\n || (e.type === 'keydown' && e.which === 13 && currentName === inputField.val())\n ) {\n if (!_.isEmpty(currentLink)) {\n nameBlock.html(currentLink);\n } else {\n nameBlock.html(currentName);\n }\n inputField.remove();\n ContextMenu.unbind().run();\n return;\n }\n\n if (e.type === 'keydown' && e.which !== 13) return;\n\n inputLoader.show();\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/rename`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${currentPath}${inputField.val()}`,\n }),\n }).done(data => {\n nameBlock.attr('data-name', inputField.val());\n if (!_.isEmpty(currentLink)) {\n let newLink = currentLink.attr('href');\n if (nameBlock.parent().data('type') !== 'folder') {\n newLink = newLink.substr(0, newLink.lastIndexOf('/')) + '/' + inputField.val();\n }\n currentLink.attr('href', newLink);\n nameBlock.html(\n currentLink.html(inputField.val())\n );\n } else {\n nameBlock.html(inputField.val());\n }\n inputField.remove();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n nameBlock.addClass('has-error').delay(2000).queue(() => {\n nameBlock.removeClass('has-error').dequeue();\n });\n inputField.popover({\n animation: true,\n placement: 'top',\n content: error,\n title: 'Save Error'\n }).popover('show');\n }).always(() => {\n inputLoader.remove();\n ContextMenu.unbind().run();\n });\n });\n }\n\n copy() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n swal({\n type: 'input',\n title: 'Copy File',\n text: 'Please enter the new path for the copied file below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: `${currentPath}${currentName}`,\n }, (val) => {\n if (val === false) {\n return false;\n }\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/copy`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${val}`,\n }),\n }).done(data => {\n swal({\n type: 'success',\n title: '',\n text: 'File successfully copied.'\n });\n Files.list();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n }\n\n download() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const fileName = decodeURIComponent(nameBlock.attr('data-name'));\n const filePath = decodeURIComponent(nameBlock.data('path'));\n\n window.location = `/server/${Pterodactyl.server.uuidShort}/files/download/${filePath}${fileName}`;\n }\n\n delete() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const delPath = decodeURIComponent(nameBlock.data('path'));\n const delName = decodeURIComponent(nameBlock.data('name'));\n\n swal({\n type: 'warning',\n title: '',\n text: 'Are you sure you want to delete ' + delName + '?',\n html: true,\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true\n }, () => {\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`,\n timeout: 10000,\n data: JSON.stringify({\n items: [`${delPath}${delName}`]\n }),\n }).done(data => {\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\n swal({\n type: 'success',\n title: 'File Deleted'\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: 'An error occurred while attempting to delete this file. Please try again.',\n });\n });\n });\n }\n\n toggleMassActions() {\n if ($('#file_listing input[type=\"checkbox\"]:checked').length) {\n $('#mass_actions').removeClass('disabled');\n } else {\n $('#mass_actions').addClass('disabled');\n }\n }\n\n toggleHighlight(event) {\n const parent = $(event.currentTarget);\n const item = $(event.currentTarget).find('input');\n\n if($(item).is(':checked')) {\n $(item).prop('checked', false);\n parent.removeClass('warning').delay(200);\n } else {\n $(item).prop('checked', true);\n parent.addClass('warning').delay(200);\n }\n }\n\n highlightAll(event) {\n let parent;\n const item = $(event.currentTarget).find('input');\n\n if($(item).is(':checked')) {\n $('#file_listing input[type=checkbox]').prop('checked', false);\n $('#file_listing input[data-action=\"addSelection\"]').each(function() {\n parent = $(this).closest('tr');\n parent.removeClass('warning').delay(200);\n });\n } else {\n $('#file_listing input[type=checkbox]').prop('checked', true);\n $('#file_listing input[data-action=\"addSelection\"]').each(function() {\n parent = $(this).closest('tr');\n parent.addClass('warning').delay(200);\n });\n }\n }\n\n deleteSelected() {\n let selectedItems = [];\n let selectedItemsElements = [];\n let parent;\n let nameBlock;\n let delLocation;\n\n $('#file_listing input[data-action=\"addSelection\"]:checked').each(function() {\n parent = $(this).closest('tr');\n nameBlock = $(parent).find('td[data-identifier=\"name\"]');\n delLocation = decodeURIComponent(nameBlock.data('path')) + decodeURIComponent(nameBlock.data('name'));\n\n selectedItems.push(delLocation);\n selectedItemsElements.push(parent);\n });\n\n if (selectedItems.length != 0)\n {\n let formattedItems = \"\";\n let i = 0;\n $.each(selectedItems, function(key, value) {\n formattedItems += (\"\" + value + \", \");\n i++;\n return i < 5;\n });\n\n formattedItems = formattedItems.slice(0, -2);\n if (selectedItems.length > 5) {\n formattedItems += ', and ' + (selectedItems.length - 5) + ' other(s)';\n }\n\n swal({\n type: 'warning',\n title: '',\n text: 'Are you sure you want to delete the following files: ' + formattedItems + '?',\n html: true,\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true\n }, () => {\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`,\n timeout: 10000,\n data: JSON.stringify({\n items: selectedItems\n }),\n }).done(data => {\n $('#file_listing input:checked').each(function() {\n $(this).prop('checked', false);\n });\n\n $.each(selectedItemsElements, function() {\n $(this).addClass('warning').delay(200).fadeOut();\n })\n\n swal({\n type: 'success',\n title: 'Files Deleted'\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: 'An error occurred while attempting to delete these files. Please try again.',\n });\n });\n });\n } else {\n swal({\n type: 'warning',\n title: '',\n text: 'Please select files/folders to delete.',\n });\n }\n }\n\n decompress() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const compPath = decodeURIComponent(nameBlock.data('path'));\n const compName = decodeURIComponent(nameBlock.data('name'));\n\n swal({\n title: ' Decompressing...',\n text: 'This might take a few seconds to complete.',\n html: true,\n allowOutsideClick: false,\n allowEscapeKey: false,\n showConfirmButton: false,\n });\n\n $.ajax({\n type: 'POST',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/decompress`,\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n data: JSON.stringify({\n files: `${compPath}${compName}`\n })\n }).done(data => {\n swal.close();\n Files.list(compPath);\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: error\n });\n });\n }\n\n compress() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const compPath = decodeURIComponent(nameBlock.data('path'));\n const compName = decodeURIComponent(nameBlock.data('name'));\n\n $.ajax({\n type: 'POST',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/compress`,\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n data: JSON.stringify({\n files: `${compPath}${compName}`,\n to: compPath.toString()\n })\n }).done(data => {\n Files.list(compPath, err => {\n if (err) return;\n const fileListing = $('#file_listing').find(`[data-name=\"${data.saved_as}\"]`).parent();\n fileListing.addClass('success pulsate').delay(3000).queue(() => {\n fileListing.removeClass('success pulsate').dequeue();\n });\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: error\n });\n });\n }\n}\n","\"use strict\";\n\n// Copyright (c) 2015 - 2017 Dane Everitt \n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass ContextMenuClass {\n constructor() {\n this.activeLine = null;\n }\n\n run() {\n this.directoryClick();\n this.rightClick();\n }\n\n makeMenu(parent) {\n $(document).find('#fileOptionMenu').remove();\n if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\n\n let newFilePath = $('#file_listing').data('current-dir');\n if (parent.data('type') === 'folder') {\n const nameBlock = parent.find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n newFilePath = `${currentPath}${currentName}`;\n }\n\n let buildMenu = '
      ';\n\n if (Pterodactyl.permissions.moveFiles) {\n buildMenu += '
    • Rename
    • \\\n
    • Move
    • ';\n }\n\n if (Pterodactyl.permissions.copyFiles) {\n buildMenu += '
    • Copy
    • ';\n }\n\n if (Pterodactyl.permissions.compressFiles) {\n buildMenu += '
    • Compress
    • ';\n }\n\n if (Pterodactyl.permissions.decompressFiles) {\n buildMenu += '
    • Decompress
    • ';\n }\n\n if (Pterodactyl.permissions.createFiles) {\n buildMenu += '
    • \\\n
    • New File
    • \\\n
    • New Folder
    • ';\n }\n\n if (Pterodactyl.permissions.downloadFiles || Pterodactyl.permissions.deleteFiles) {\n buildMenu += '
    • ';\n }\n\n if (Pterodactyl.permissions.downloadFiles) {\n buildMenu += '
    • Download
    • ';\n }\n\n if (Pterodactyl.permissions.deleteFiles) {\n buildMenu += '
    • Delete
    • ';\n }\n\n buildMenu += '
    ';\n return buildMenu;\n }\n\n rightClick() {\n $('[data-action=\"toggleMenu\"]').on('mousedown', event => {\n event.preventDefault();\n if ($(document).find('#fileOptionMenu').is(':visible')) {\n $('body').trigger('click');\n return;\n }\n this.showMenu(event);\n });\n $('#file_listing > tbody td').on('contextmenu', event => {\n this.showMenu(event);\n });\n }\n\n showMenu(event) {\n const parent = $(event.target).closest('tr');\n const menu = $(this.makeMenu(parent));\n\n if (parent.data('type') === 'disabled') return;\n event.preventDefault();\n\n $(menu).appendTo('body');\n $(menu).data('invokedOn', $(event.target)).show().css({\n position: 'absolute',\n left: event.pageX - 150,\n top: event.pageY,\n });\n\n this.activeLine = parent;\n this.activeLine.addClass('active');\n\n // Handle Events\n const Actions = new ActionsClass(parent, menu);\n if (Pterodactyl.permissions.moveFiles) {\n $(menu).find('li[data-action=\"move\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.move();\n });\n $(menu).find('li[data-action=\"rename\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.rename();\n });\n }\n\n if (Pterodactyl.permissions.copyFiles) {\n $(menu).find('li[data-action=\"copy\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.copy();\n });\n }\n\n if (Pterodactyl.permissions.compressFiles) {\n if (parent.data('type') === 'folder') {\n $(menu).find('li[data-action=\"compress\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"compress\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.compress();\n });\n }\n\n if (Pterodactyl.permissions.decompressFiles) {\n if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) {\n $(menu).find('li[data-action=\"decompress\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"decompress\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.decompress();\n });\n }\n\n if (Pterodactyl.permissions.createFiles) {\n $(menu).find('li[data-action=\"folder\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.folder();\n });\n }\n\n if (Pterodactyl.permissions.downloadFiles) {\n if (parent.data('type') === 'file') {\n $(menu).find('li[data-action=\"download\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"download\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.download();\n });\n }\n\n if (Pterodactyl.permissions.deleteFiles) {\n $(menu).find('li[data-action=\"delete\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.delete();\n });\n }\n\n $(window).unbind().on('click', event => {\n if($(event.target).is('.disable-menu-hide')) {\n event.preventDefault();\n return;\n }\n $(menu).unbind().remove();\n if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\n });\n }\n\n directoryClick() {\n $('a[data-action=\"directory-view\"]').on('click', function (event) {\n event.preventDefault();\n\n const path = $(this).parent().data('path') || '';\n const name = $(this).parent().data('name') || '';\n\n window.location.hash = encodeURIComponent(path + name);\n Files.list();\n });\n }\n}\n\nwindow.ContextMenu = new ContextMenuClass;\n","\"use strict\";\n\n// Copyright (c) 2015 - 2017 Dane Everitt \n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass FileManager {\n constructor() {\n this.list(this.decodeHash());\n }\n\n list(path, next) {\n if (_.isUndefined(path)) {\n path = this.decodeHash();\n }\n\n this.loader(true);\n $.ajax({\n type: 'POST',\n url: Pterodactyl.meta.directoryList,\n headers: {\n 'X-CSRF-Token': Pterodactyl.meta.csrftoken,\n },\n data: {\n directory: path,\n },\n }).done(data => {\n this.loader(false);\n $('#load_files').slideUp(10).html(data).slideDown(10, () => {\n ContextMenu.run();\n this.reloadFilesButton();\n this.addFolderButton();\n this.selectItem();\n this.selectAll();\n this.selectiveDeletion();\n this.selectRow();\n if (_.isFunction(next)) {\n return next();\n }\n });\n $('#internal_alert').slideUp();\n\n if (typeof Siofu === 'object') {\n Siofu.listenOnInput(document.getElementById(\"files_touch_target\"));\n }\n }).fail(jqXHR => {\n this.loader(false);\n if (_.isFunction(next)) {\n return next(new Error('Failed to load file listing.'));\n }\n\n if ((path !== '' && path !== '/') && jqXHR.status === 404) {\n return this.list('', next);\n }\n\n swal({\n type: 'error',\n title: 'File Error',\n text: jqXHR.responseJSON.errors[0].detail || 'An error occurred while attempting to process this request. Please try again.',\n });\n console.error(jqXHR);\n });\n }\n\n loader(show) {\n if (show){\n $('.file-overlay').fadeIn(100);\n } else {\n $('.file-overlay').fadeOut(100);\n }\n }\n\n reloadFilesButton() {\n $('i[data-action=\"reload-files\"]').unbind().on('click', () => {\n $('i[data-action=\"reload-files\"]').addClass('fa-spin');\n this.list();\n });\n }\n\n selectItem() {\n $('[data-action=\"addSelection\"]').on('click', event => {\n event.preventDefault();\n });\n }\n\n selectAll() {\n $('[data-action=\"selectAll\"]').on('click', event => {\n event.preventDefault();\n });\n }\n\n selectiveDeletion() {\n $('[data-action=\"selective-deletion\"]').on('mousedown', event => {\n new ActionsClass().deleteSelected();\n });\n }\n\n addFolderButton() {\n $('[data-action=\"add-folder\"]').unbind().on('click', () => {\n new ActionsClass().folder($('#file_listing').data('current-dir') || '/');\n })\n }\n\n selectRow() {\n $('#file_listing tr').on('mousedown', event => {\n if (event.which === 1) {\n if ($(event.target).is('th') || $(event.target).is('input[data-action=\"selectAll\"]')) {\n new ActionsClass().highlightAll(event);\n } else if ($(event.target).is('td') || $(event.target).is('input[data-action=\"addSelection\"]')) {\n new ActionsClass().toggleHighlight(event);\n }\n\n new ActionsClass().toggleMassActions();\n }\n });\n }\n\n decodeHash() {\n return decodeURIComponent(window.location.hash.substring(1));\n }\n\n}\n\nwindow.Files = new FileManager;\n"]} \ No newline at end of file +{"version":3,"sources":["src/actions.js","src/contextmenu.js","src/index.js"],"names":[],"mappings":"AAAA,a,8oBAqBM,a,YACF,sBAAY,OAAZ,CAAqB,IAArB,CAA2B,oCACvB,KAAK,OAAL,CAAe,OAAf,CACA,KAAK,IAAL,CAAY,IACf,C,kEAES,CACN,KAAK,OAAL,CAAe,SAClB,C,wDAEe,K,CAAO,CACnB,MAAO,GAAE,OAAF,EAAW,IAAX,CAAgB,KAAhB,EAAuB,IAAvB,EACV,C,sCAEM,I,CAAM,CACT,GAAI,kBAAJ,CACA,GAAI,IAAJ,CAAU,CACN,WAAa,IAChB,CAFD,IAEO,CACH,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAApB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAApB,CAEA,GAAI,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,MAArB,IAAiC,MAArC,CAA6C,CACzC,WAAa,WAChB,CAFD,IAEO,CACH,cAAgB,WAAhB,CAA8B,WAA9B,IACH,CACJ,CAED,KAAK,CACD,KAAM,OADL,CAED,MAAO,eAFN,CAGD,KAAM,8CAHL,CAID,iBAAkB,IAJjB,CAKD,kBAAmB,IALlB,CAMD,eAAgB,KANf,CAOD,oBAAqB,IAPpB,CAQD,WAAY,UARX,CAAL,CASG,SAAC,GAAD,CAAS,CACR,GAAI,MAAQ,KAAZ,CAAmB,CACf,MAAO,MACV,CAED,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAFN,CAMH,YAAa,iCANV,CAOH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,yBAPG,CAQH,QAAS,KARN,CASH,KAAM,KAAK,SAAL,CAAe,CACjB,KAAM,GADW,CAAf,CATH,CAAP,EAYG,IAZH,CAYQ,cAAQ,CACZ,KAAK,KAAL,GACA,MAAM,IAAN,EACH,CAfD,EAeG,IAfH,CAeQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,GAAI,OAAQ,yDAAZ,CACA,GAAI,MAAO,OAAM,YAAb,GAA8B,WAA9B,EAA6C,MAAO,OAAM,YAAN,CAAmB,KAA1B,GAAoC,WAArF,CAAkG,CAC9F,MAAQ,MAAM,YAAN,CAAmB,KAC9B,CACD,KAAK,CACD,KAAM,OADL,CAED,MAAO,EAFN,CAGD,KAAM,KAHL,CAAL,CAKH,CA1BD,CA2BH,CAzCD,CA0CH,C,mCAEM,CACH,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,WAAf,CAAnB,CAApB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAApB,CAEA,KAAK,CACD,KAAM,OADL,CAED,MAAO,WAFN,CAGD,KAAM,+CAHL,CAID,iBAAkB,IAJjB,CAKD,kBAAmB,IALlB,CAMD,eAAgB,KANf,CAOD,oBAAqB,IAPpB,CAQD,cAAe,WAAf,CAA6B,WAR5B,CAAL,CASG,SAAC,GAAD,CAAS,CACR,GAAI,MAAQ,KAAZ,CAAmB,CACf,MAAO,MACV,CAED,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAFN,CAMH,YAAa,iCANV,CAOH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,uBAPG,CAQH,QAAS,KARN,CASH,KAAM,KAAK,SAAL,CAAe,CACjB,QAAS,WAAT,CAAuB,WADN,CAEjB,MAAO,GAFU,CAAf,CATH,CAAP,EAaG,IAbH,CAaQ,cAAQ,CACZ,UAAU,MAAV,GAAmB,QAAnB,CAA4B,SAA5B,EAAuC,KAAvC,CAA6C,GAA7C,EAAkD,OAAlD,GACA,KAAK,KAAL,EACH,CAhBD,EAgBG,IAhBH,CAgBQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,GAAI,OAAQ,yDAAZ,CACA,GAAI,MAAO,OAAM,YAAb,GAA8B,WAA9B,EAA6C,MAAO,OAAM,YAAN,CAAmB,KAA1B,GAAoC,WAArF,CAAkG,CAC9F,MAAQ,MAAM,YAAN,CAAmB,KAC9B,CACD,KAAK,CACD,KAAM,OADL,CAED,MAAO,EAFN,CAGD,KAAM,KAHL,CAAL,CAKH,CA3BD,CA4BH,CA1CD,CA4CH,C,uCAEQ,CACL,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,aAAc,UAAU,IAAV,CAAe,GAAf,CAApB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,WAAf,CAAnB,CAApB,CACA,GAAM,uFACwD,WADxD,4GAAN,CAKA,UAAU,IAAV,CAAe,YAAf,EACA,GAAM,YAAa,UAAU,IAAV,CAAe,OAAf,CAAnB,CACA,GAAM,aAAc,UAAU,IAAV,CAAe,eAAf,CAApB,CAEA,WAAW,KAAX,GACA,WAAW,EAAX,CAAc,cAAd,CAA8B,WAAK,CAE/B,GACK,EAAE,IAAF,GAAW,SAAX,EAAwB,EAAE,KAAF,GAAY,EAArC,EACG,EAAE,IAAF,GAAW,MADd,EAEI,EAAE,IAAF,GAAW,SAAX,EAAwB,EAAE,KAAF,GAAY,EAApC,EAA0C,cAAgB,WAAW,GAAX,EAHlE,CAIE,CACE,GAAI,CAAC,EAAE,OAAF,CAAU,WAAV,CAAL,CAA6B,CACzB,UAAU,IAAV,CAAe,WAAf,CACH,CAFD,IAEO,CACH,UAAU,IAAV,CAAe,WAAf,CACH,CACD,WAAW,MAAX,GACA,YAAY,MAAZ,GAAqB,GAArB,GACA,MACH,CAED,GAAI,EAAE,IAAF,GAAW,SAAX,EAAwB,EAAE,KAAF,GAAY,EAAxC,CAA4C,OAE5C,YAAY,IAAZ,GACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAApB,CAEA,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAFN,CAMH,YAAa,iCANV,CAOH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,yBAPG,CAQH,QAAS,KARN,CASH,KAAM,KAAK,SAAL,CAAe,CACjB,QAAS,WAAT,CAAuB,WADN,CAEjB,MAAO,WAAP,CAAqB,WAAW,GAAX,EAFJ,CAAf,CATH,CAAP,EAaG,IAbH,CAaQ,cAAQ,CACZ,UAAU,IAAV,CAAe,WAAf,CAA4B,WAAW,GAAX,EAA5B,EACA,GAAI,CAAC,EAAE,OAAF,CAAU,WAAV,CAAL,CAA6B,CACzB,GAAI,SAAU,YAAY,IAAZ,CAAiB,MAAjB,CAAd,CACA,GAAI,UAAU,MAAV,GAAmB,IAAnB,CAAwB,MAAxB,IAAoC,QAAxC,CAAkD,CAC9C,QAAU,QAAQ,MAAR,CAAe,CAAf,CAAkB,QAAQ,WAAR,CAAoB,GAApB,CAAlB,EAA8C,GAA9C,CAAoD,WAAW,GAAX,EACjE,CACD,YAAY,IAAZ,CAAiB,MAAjB,CAAyB,OAAzB,EACA,UAAU,IAAV,CACI,YAAY,IAAZ,CAAiB,WAAW,GAAX,EAAjB,CADJ,CAGH,CATD,IASO,CACH,UAAU,IAAV,CAAe,WAAW,GAAX,EAAf,CACH,CACD,WAAW,MAAX,EACH,CA5BD,EA4BG,IA5BH,CA4BQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,GAAI,OAAQ,yDAAZ,CACA,GAAI,MAAO,OAAM,YAAb,GAA8B,WAA9B,EAA6C,MAAO,OAAM,YAAN,CAAmB,KAA1B,GAAoC,WAArF,CAAkG,CAC9F,MAAQ,MAAM,YAAN,CAAmB,KAC9B,CACD,UAAU,QAAV,CAAmB,WAAnB,EAAgC,KAAhC,CAAsC,IAAtC,EAA4C,KAA5C,CAAkD,UAAM,CACpD,UAAU,WAAV,CAAsB,WAAtB,EAAmC,OAAnC,EACH,CAFD,EAGA,WAAW,OAAX,CAAmB,CACf,UAAW,IADI,CAEf,UAAW,KAFI,CAGf,QAAS,KAHM,CAIf,MAAO,YAJQ,CAAnB,EAKG,OALH,CAKW,MALX,CAMH,CA3CD,EA2CG,MA3CH,CA2CU,UAAM,CACZ,YAAY,MAAZ,GACA,YAAY,MAAZ,GAAqB,GAArB,EACH,CA9CD,CA+CH,CArED,CAsEH,C,mCAEM,CACH,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,WAAf,CAAnB,CAApB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAApB,CAEA,KAAK,CACD,KAAM,OADL,CAED,MAAO,WAFN,CAGD,KAAM,sDAHL,CAID,iBAAkB,IAJjB,CAKD,kBAAmB,IALlB,CAMD,eAAgB,KANf,CAOD,oBAAqB,IAPpB,CAQD,cAAe,WAAf,CAA6B,WAR5B,CAAL,CASG,SAAC,GAAD,CAAS,CACR,GAAI,MAAQ,KAAZ,CAAmB,CACf,MAAO,MACV,CAED,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAFN,CAMH,YAAa,iCANV,CAOH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,uBAPG,CAQH,QAAS,KARN,CASH,KAAM,KAAK,SAAL,CAAe,CACjB,QAAS,WAAT,CAAuB,WADN,CAEjB,MAAO,GAFU,CAAf,CATH,CAAP,EAaG,IAbH,CAaQ,cAAQ,CACZ,KAAK,CACD,KAAM,SADL,CAED,MAAO,EAFN,CAGD,KAAM,2BAHL,CAAL,EAKA,MAAM,IAAN,EACH,CApBD,EAoBG,IApBH,CAoBQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,GAAI,OAAQ,yDAAZ,CACA,GAAI,MAAO,OAAM,YAAb,GAA8B,WAA9B,EAA6C,MAAO,OAAM,YAAN,CAAmB,KAA1B,GAAoC,WAArF,CAAkG,CAC9F,MAAQ,MAAM,YAAN,CAAmB,KAC9B,CACD,KAAK,CACD,KAAM,OADL,CAED,MAAO,EAFN,CAGD,KAAM,KAHL,CAAL,CAKH,CA/BD,CAgCH,CA9CD,CA+CH,C,2CAEU,CACP,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,UAAW,mBAAmB,UAAU,IAAV,CAAe,WAAf,CAAnB,CAAjB,CACA,GAAM,UAAW,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAAjB,CAEA,OAAO,QAAP,YAA6B,YAAY,MAAZ,CAAmB,SAAhD,oBAA4E,QAA5E,CAAuF,QAC1F,C,wCAEQ,CACL,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,SAAU,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAAhB,CACA,GAAM,SAAU,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAAhB,CAEA,KAAK,CACD,KAAM,SADL,CAED,MAAO,EAFN,CAGD,KAAM,yCAA2C,KAAK,eAAL,CAAqB,OAArB,CAA3C,CAA2E,UAHhF,CAID,KAAM,IAJL,CAKD,iBAAkB,IALjB,CAMD,kBAAmB,IANlB,CAOD,eAAgB,KAPf,CAQD,oBAAqB,IARpB,CAAL,CASG,UAAM,CACL,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAFN,CAMH,YAAa,iCANV,CAOH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,yBAPG,CAQH,QAAS,KARN,CASH,KAAM,KAAK,SAAL,CAAe,CACjB,MAAO,IAAI,OAAJ,CAAc,OAAd,CADU,CAAf,CATH,CAAP,EAYG,IAZH,CAYQ,cAAQ,CACZ,UAAU,MAAV,GAAmB,QAAnB,CAA4B,SAA5B,EAAuC,KAAvC,CAA6C,GAA7C,EAAkD,OAAlD,GACA,KAAK,CACD,KAAM,SADL,CAED,MAAO,cAFN,CAAL,CAIH,CAlBD,EAkBG,IAlBH,CAkBQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,KAAK,CACD,KAAM,OADL,CAED,MAAO,SAFN,CAGD,KAAM,IAHL,CAID,KAAM,2EAJL,CAAL,CAMH,CA1BD,CA2BH,CArCD,CAsCH,C,6DAEmB,CAChB,GAAI,EAAE,8CAAF,EAAkD,MAAtD,CAA8D,CAC1D,EAAE,eAAF,EAAmB,WAAnB,CAA+B,UAA/B,CACH,CAFD,IAEO,CACH,EAAE,eAAF,EAAmB,QAAnB,CAA4B,UAA5B,CACH,CACJ,C,wDAEe,K,CAAO,CACnB,GAAM,QAAS,EAAE,MAAM,aAAR,CAAf,CACA,GAAM,MAAO,EAAE,MAAM,aAAR,EAAuB,IAAvB,CAA4B,OAA5B,CAAb,CAEA,GAAG,EAAE,IAAF,EAAQ,EAAR,CAAW,UAAX,CAAH,CAA2B,CACvB,EAAE,IAAF,EAAQ,IAAR,CAAa,SAAb,CAAwB,KAAxB,EACA,OAAO,WAAP,CAAmB,SAAnB,EAA8B,KAA9B,CAAoC,GAApC,CACH,CAHD,IAGO,CACH,EAAE,IAAF,EAAQ,IAAR,CAAa,SAAb,CAAwB,IAAxB,EACA,OAAO,QAAP,CAAgB,SAAhB,EAA2B,KAA3B,CAAiC,GAAjC,CACH,CACJ,C,kDAEY,K,CAAO,CAChB,GAAI,cAAJ,CACA,GAAM,MAAO,EAAE,MAAM,aAAR,EAAuB,IAAvB,CAA4B,OAA5B,CAAb,CAEA,GAAG,EAAE,IAAF,EAAQ,EAAR,CAAW,UAAX,CAAH,CAA2B,CACzB,EAAE,oCAAF,EAAwC,IAAxC,CAA6C,SAA7C,CAAwD,KAAxD,EACA,EAAE,iDAAF,EAAqD,IAArD,CAA0D,UAAW,CACjE,OAAS,EAAE,IAAF,EAAQ,OAAR,CAAgB,IAAhB,CAAT,CACA,OAAO,WAAP,CAAmB,SAAnB,EAA8B,KAA9B,CAAoC,GAApC,CACH,CAHD,CAID,CAND,IAMO,CACL,EAAE,oCAAF,EAAwC,IAAxC,CAA6C,SAA7C,CAAwD,IAAxD,EACA,EAAE,iDAAF,EAAqD,IAArD,CAA0D,UAAW,CACjE,OAAS,EAAE,IAAF,EAAQ,OAAR,CAAgB,IAAhB,CAAT,CACA,OAAO,QAAP,CAAgB,SAAhB,EAA2B,KAA3B,CAAiC,GAAjC,CACH,CAHD,CAID,CACJ,C,uDAEgB,CACb,GAAI,eAAgB,EAApB,CACA,GAAI,uBAAwB,EAA5B,CACA,GAAI,cAAJ,CACA,GAAI,iBAAJ,CACA,GAAI,mBAAJ,CAEA,EAAE,yDAAF,EAA6D,IAA7D,CAAkE,UAAW,CACzE,OAAS,EAAE,IAAF,EAAQ,OAAR,CAAgB,IAAhB,CAAT,CACA,UAAY,EAAE,MAAF,EAAU,IAAV,CAAe,4BAAf,CAAZ,CACA,YAAc,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,EAA6C,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAA3D,CAEA,cAAc,IAAd,CAAmB,WAAnB,EACA,sBAAsB,IAAtB,CAA2B,MAA3B,CACH,CAPD,EASA,GAAI,cAAc,MAAd,EAAwB,CAA5B,CACA,CACI,GAAI,gBAAiB,EAArB,CACA,GAAI,GAAI,CAAR,CACA,EAAE,IAAF,CAAO,aAAP,CAAsB,SAAS,GAAT,CAAc,KAAd,CAAqB,CACvC,gBAAmB,SAAW,KAAK,eAAL,CAAqB,KAArB,CAAX,CAAyC,WAA5D,CACA,IACA,MAAO,GAAI,CACd,CAJD,EAMA,eAAiB,eAAe,KAAf,CAAqB,CAArB,CAAwB,CAAC,CAAzB,CAAjB,CACA,GAAI,cAAc,MAAd,CAAuB,CAA3B,CAA8B,CAC1B,gBAAkB,UAAY,cAAc,MAAd,CAAuB,CAAnC,EAAwC,WAC7D,CAED,KAAK,CACD,KAAM,SADL,CAED,MAAO,EAFN,CAGD,KAAM,wDAA0D,KAAK,eAAL,CAAqB,cAArB,CAA1D,CAAiG,GAHtG,CAID,KAAM,IAJL,CAKD,iBAAkB,IALjB,CAMD,kBAAmB,IANlB,CAOD,eAAgB,KAPf,CAQD,oBAAqB,IARpB,CAAL,CASG,UAAM,CACL,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAFN,CAMH,YAAa,iCANV,CAOH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,yBAPG,CAQH,QAAS,KARN,CASH,KAAM,KAAK,SAAL,CAAe,CACjB,MAAO,aADU,CAAf,CATH,CAAP,EAYG,IAZH,CAYQ,cAAQ,CACZ,EAAE,6BAAF,EAAiC,IAAjC,CAAsC,UAAW,CAC7C,EAAE,IAAF,EAAQ,IAAR,CAAa,SAAb,CAAwB,KAAxB,CACH,CAFD,EAIA,EAAE,IAAF,CAAO,qBAAP,CAA8B,UAAW,CACrC,EAAE,IAAF,EAAQ,QAAR,CAAiB,SAAjB,EAA4B,KAA5B,CAAkC,GAAlC,EAAuC,OAAvC,EACH,CAFD,EAIA,KAAK,CACD,KAAM,SADL,CAED,MAAO,eAFN,CAAL,CAIH,CAzBD,EAyBG,IAzBH,CAyBQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,KAAK,CACD,KAAM,OADL,CAED,MAAO,SAFN,CAGD,KAAM,IAHL,CAID,KAAM,6EAJL,CAAL,CAMH,CAjCD,CAkCH,CA5CD,CA6CH,CA5DD,IA4DO,CACH,KAAK,CACH,KAAM,SADH,CAEH,MAAO,EAFJ,CAGH,KAAM,wCAHH,CAAL,CAKH,CACJ,C,+CAEY,CACT,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,UAAW,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAAjB,CACA,GAAM,UAAW,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAAjB,CAEA,KAAK,CACD,MAAO,wDADN,CAED,KAAM,4CAFL,CAGD,KAAM,IAHL,CAID,kBAAmB,KAJlB,CAKD,eAAgB,KALf,CAMD,kBAAmB,KANlB,CAAL,EASA,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,6BAFG,CAGH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAHN,CAOH,YAAa,iCAPV,CAQH,KAAM,KAAK,SAAL,CAAe,CACjB,SAAU,QAAV,CAAqB,QADJ,CAAf,CARH,CAAP,EAWG,IAXH,CAWQ,cAAQ,CACZ,KAAK,KAAL,GACA,MAAM,IAAN,CAAW,QAAX,CACH,CAdD,EAcG,IAdH,CAcQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,GAAI,OAAQ,yDAAZ,CACA,GAAI,MAAO,OAAM,YAAb,GAA8B,WAA9B,EAA6C,MAAO,OAAM,YAAN,CAAmB,KAA1B,GAAoC,WAArF,CAAkG,CAC9F,MAAQ,MAAM,YAAN,CAAmB,KAC9B,CACD,KAAK,CACD,KAAM,OADL,CAED,MAAO,SAFN,CAGD,KAAM,IAHL,CAID,KAAM,KAJL,CAAL,CAMH,CA1BD,CA2BH,C,2CAEU,gBACP,GAAM,WAAY,EAAE,KAAK,OAAP,EAAgB,IAAhB,CAAqB,4BAArB,CAAlB,CACA,GAAM,UAAW,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAAjB,CACA,GAAM,UAAW,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAAjB,CAEA,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,IAAQ,YAAY,IAAZ,CAAiB,MAAzB,OAAqC,YAAY,IAAZ,CAAiB,IAAtD,KAA8D,YAAY,IAAZ,CAAiB,YAA/E,2BAFG,CAGH,QAAS,CACL,iBAAkB,YAAY,MAAZ,CAAmB,YADhC,CAEL,kBAAmB,YAAY,MAAZ,CAAmB,IAFjC,CAHN,CAOH,YAAa,iCAPV,CAQH,KAAM,KAAK,SAAL,CAAe,CACjB,SAAU,QAAV,CAAqB,QADJ,CAEjB,GAAI,SAAS,QAAT,EAFa,CAAf,CARH,CAAP,EAYG,IAZH,CAYQ,cAAQ,CACZ,MAAM,IAAN,CAAW,QAAX,CAAqB,aAAO,CACxB,GAAI,GAAJ,CAAS,OACT,GAAM,aAAc,EAAE,eAAF,EAAmB,IAAnB,gBAAuC,KAAK,QAA5C,OAA0D,MAA1D,EAApB,CACA,YAAY,QAAZ,CAAqB,iBAArB,EAAwC,KAAxC,CAA8C,IAA9C,EAAoD,KAApD,CAA0D,UAAM,CAC5D,YAAY,WAAZ,CAAwB,iBAAxB,EAA2C,OAA3C,EACH,CAFD,CAGH,CAND,CAOH,CApBD,EAoBG,IApBH,CAoBQ,eAAS,CACb,QAAQ,KAAR,CAAc,KAAd,EACA,GAAI,OAAQ,yDAAZ,CACA,GAAI,MAAO,OAAM,YAAb,GAA8B,WAA9B,EAA6C,MAAO,OAAM,YAAN,CAAmB,KAA1B,GAAoC,WAArF,CAAkG,CAC9F,MAAQ,MAAM,YAAN,CAAmB,KAC9B,CACD,KAAK,CACD,KAAM,OADL,CAED,MAAO,SAFN,CAGD,KAAM,IAHL,CAID,KAAM,MAAK,eAAL,CAAqB,KAArB,CAJL,CAAL,CAMH,CAhCD,CAiCH,C;;ACjiBL,a,8oBAqBM,iB,YACF,2BAAc,wCACV,KAAK,UAAL,CAAkB,IACrB,C,8DAEK,CACF,KAAK,cAAL,GACA,KAAK,UAAL,EACH,C,0CAEQ,M,CAAQ,CACb,EAAE,QAAF,EAAY,IAAZ,CAAiB,iBAAjB,EAAoC,MAApC,GACA,GAAI,CAAC,EAAE,MAAF,CAAS,KAAK,UAAd,CAAL,CAAgC,KAAK,UAAL,CAAgB,WAAhB,CAA4B,QAA5B,EAEhC,GAAI,aAAc,EAAE,eAAF,EAAmB,IAAnB,CAAwB,aAAxB,CAAlB,CACA,GAAI,OAAO,IAAP,CAAY,MAAZ,IAAwB,QAA5B,CAAsC,CAClC,GAAM,WAAY,OAAO,IAAP,CAAY,4BAAZ,CAAlB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,WAAf,CAAnB,CAApB,CACA,GAAM,aAAc,mBAAmB,UAAU,IAAV,CAAe,MAAf,CAAnB,CAApB,CACA,eAAiB,WAAjB,CAA+B,WAClC,CAED,GAAI,WAAY,kFAAhB,CAEA,GAAI,YAAY,WAAZ,CAAwB,SAA5B,CAAuC,CACnC,WAAa,iPAEhB,CAED,GAAI,YAAY,WAAZ,CAAwB,SAA5B,CAAuC,CACnC,WAAa,kGAChB,CAED,GAAI,YAAY,WAAZ,CAAwB,aAA5B,CAA2C,CACvC,WAAa,kIAChB,CAED,GAAI,YAAY,WAAZ,CAAwB,eAA5B,CAA6C,CACzC,WAAa,8HAChB,CAED,GAAI,YAAY,WAAZ,CAAwB,WAA5B,CAAyC,CACrC,WAAa,+FAC4C,YAAY,MAAZ,CAAmB,SAD/D,CAC0E,kBAD1E,CAC+F,EAAE,OAAF,EAAW,IAAX,CAAgB,WAAhB,EAA6B,IAA7B,EAD/F,CACqI,6MAErJ,CAED,GAAI,YAAY,WAAZ,CAAwB,aAAxB,EAAyC,YAAY,WAAZ,CAAwB,WAArE,CAAkF,CAC9E,WAAa,2BAChB,CAED,GAAI,YAAY,WAAZ,CAAwB,aAA5B,CAA2C,CACvC,WAAa,4HAChB,CAED,GAAI,YAAY,WAAZ,CAAwB,WAA5B,CAAyC,CACrC,WAAa,0HAChB,CAED,WAAa,OAAb,CACA,MAAO,UACV,C,+CAEY,gBACT,EAAE,4BAAF,EAAgC,EAAhC,CAAmC,WAAnC,CAAgD,eAAS,CACrD,MAAM,cAAN,GACA,GAAI,EAAE,QAAF,EAAY,IAAZ,CAAiB,iBAAjB,EAAoC,EAApC,CAAuC,UAAvC,CAAJ,CAAwD,CACpD,EAAE,MAAF,EAAU,OAAV,CAAkB,OAAlB,EACA,MACH,CACD,MAAK,QAAL,CAAc,KAAd,CACH,CAPD,EAQA,EAAE,0BAAF,EAA8B,EAA9B,CAAiC,aAAjC,CAAgD,eAAS,CACrD,MAAK,QAAL,CAAc,KAAd,CACH,CAFD,CAGH,C,0CAEQ,K,CAAO,iBACZ,GAAM,QAAS,EAAE,MAAM,MAAR,EAAgB,OAAhB,CAAwB,IAAxB,CAAf,CACA,GAAM,MAAO,EAAE,KAAK,QAAL,CAAc,MAAd,CAAF,CAAb,CAEA,GAAI,OAAO,IAAP,CAAY,MAAZ,IAAwB,UAA5B,CAAwC,OACxC,MAAM,cAAN,GAEA,EAAE,IAAF,EAAQ,QAAR,CAAiB,MAAjB,EACA,EAAE,IAAF,EAAQ,IAAR,CAAa,WAAb,CAA0B,EAAE,MAAM,MAAR,CAA1B,EAA2C,IAA3C,GAAkD,GAAlD,CAAsD,CAClD,SAAU,UADwC,CAElD,KAAM,MAAM,KAAN,CAAc,GAF8B,CAGlD,IAAK,MAAM,KAHuC,CAAtD,EAMA,KAAK,UAAL,CAAkB,MAAlB,CACA,KAAK,UAAL,CAAgB,QAAhB,CAAyB,QAAzB,EAGA,GAAM,SAAU,GAAI,aAAJ,CAAiB,MAAjB,CAAyB,IAAzB,CAAhB,CACA,GAAI,YAAY,WAAZ,CAAwB,SAA5B,CAAuC,CACnC,EAAE,IAAF,EAAQ,IAAR,CAAa,wBAAb,EAAuC,MAAvC,GAAgD,EAAhD,CAAmD,OAAnD,CAA4D,WAAK,CAC7D,EAAE,cAAF,GACA,QAAQ,IAAR,EACH,CAHD,EAIA,EAAE,IAAF,EAAQ,IAAR,CAAa,0BAAb,EAAyC,MAAzC,GAAkD,EAAlD,CAAqD,OAArD,CAA8D,WAAK,CAC/D,EAAE,cAAF,GACA,QAAQ,MAAR,EACH,CAHD,CAIH,CAED,GAAI,YAAY,WAAZ,CAAwB,SAA5B,CAAuC,CACnC,EAAE,IAAF,EAAQ,IAAR,CAAa,wBAAb,EAAuC,MAAvC,GAAgD,EAAhD,CAAmD,OAAnD,CAA4D,WAAK,CAC7D,EAAE,cAAF,GACA,QAAQ,IAAR,EACH,CAHD,CAIH,CAED,GAAI,YAAY,WAAZ,CAAwB,aAA5B,CAA2C,CACvC,GAAI,OAAO,IAAP,CAAY,MAAZ,IAAwB,QAA5B,CAAsC,CAClC,EAAE,IAAF,EAAQ,IAAR,CAAa,4BAAb,EAA2C,WAA3C,CAAuD,QAAvD,CACH,CACD,EAAE,IAAF,EAAQ,IAAR,CAAa,4BAAb,EAA2C,MAA3C,GAAoD,EAApD,CAAuD,OAAvD,CAAgE,WAAK,CACjE,EAAE,cAAF,GACA,QAAQ,QAAR,EACH,CAHD,CAIH,CAED,GAAI,YAAY,WAAZ,CAAwB,eAA5B,CAA6C,CACzC,GAAI,EAAE,OAAF,CAAU,CAAC,iBAAD,CAAoB,kBAApB,CAAwC,oBAAxC,CAAV,CAAyE,OAAO,IAAP,CAAY,MAAZ,CAAzE,EAA8F,MAA9F,CAAuG,CAA3G,CAA8G,CAC1G,EAAE,IAAF,EAAQ,IAAR,CAAa,8BAAb,EAA6C,WAA7C,CAAyD,QAAzD,CACH,CACD,EAAE,IAAF,EAAQ,IAAR,CAAa,8BAAb,EAA6C,MAA7C,GAAsD,EAAtD,CAAyD,OAAzD,CAAkE,WAAK,CACnE,EAAE,cAAF,GACA,QAAQ,UAAR,EACH,CAHD,CAIH,CAED,GAAI,YAAY,WAAZ,CAAwB,WAA5B,CAAyC,CACrC,EAAE,IAAF,EAAQ,IAAR,CAAa,0BAAb,EAAyC,MAAzC,GAAkD,EAAlD,CAAqD,OAArD,CAA8D,WAAK,CAC/D,EAAE,cAAF,GACA,QAAQ,MAAR,EACH,CAHD,CAIH,CAED,GAAI,YAAY,WAAZ,CAAwB,aAA5B,CAA2C,CACvC,GAAI,OAAO,IAAP,CAAY,MAAZ,IAAwB,MAA5B,CAAoC,CAChC,EAAE,IAAF,EAAQ,IAAR,CAAa,4BAAb,EAA2C,WAA3C,CAAuD,QAAvD,CACH,CACD,EAAE,IAAF,EAAQ,IAAR,CAAa,4BAAb,EAA2C,MAA3C,GAAoD,EAApD,CAAuD,OAAvD,CAAgE,WAAK,CACjE,EAAE,cAAF,GACA,QAAQ,QAAR,EACH,CAHD,CAIH,CAED,GAAI,YAAY,WAAZ,CAAwB,WAA5B,CAAyC,CACrC,EAAE,IAAF,EAAQ,IAAR,CAAa,0BAAb,EAAyC,MAAzC,GAAkD,EAAlD,CAAqD,OAArD,CAA8D,WAAK,CAC/D,EAAE,cAAF,GACA,QAAQ,MAAR,EACH,CAHD,CAIH,CAED,EAAE,MAAF,EAAU,MAAV,GAAmB,EAAnB,CAAsB,OAAtB,CAA+B,eAAS,CACpC,GAAG,EAAE,MAAM,MAAR,EAAgB,EAAhB,CAAmB,oBAAnB,CAAH,CAA6C,CACzC,MAAM,cAAN,GACA,MACH,CACD,EAAE,IAAF,EAAQ,MAAR,GAAiB,MAAjB,GACA,GAAG,CAAC,EAAE,MAAF,CAAS,OAAK,UAAd,CAAJ,CAA+B,OAAK,UAAL,CAAgB,WAAhB,CAA4B,QAA5B,CAClC,CAPD,CAQH,C,uDAEgB,CACb,EAAE,iCAAF,EAAqC,EAArC,CAAwC,OAAxC,CAAiD,SAAU,KAAV,CAAiB,CAC9D,MAAM,cAAN,GAEA,GAAM,MAAO,EAAE,IAAF,EAAQ,MAAR,GAAiB,IAAjB,CAAsB,MAAtB,GAAiC,EAA9C,CACA,GAAM,MAAO,EAAE,IAAF,EAAQ,MAAR,GAAiB,IAAjB,CAAsB,MAAtB,GAAiC,EAA9C,CAEA,OAAO,QAAP,CAAgB,IAAhB,CAAuB,mBAAmB,KAAO,IAA1B,CAAvB,CACA,MAAM,IAAN,EACH,CARD,CASH,C,+BAGL,OAAO,WAAP,CAAqB,GAAI,iBAAzB;AC1MA,a,q3BAqBM,Y,YACF,sBAAc,mCACV,KAAK,IAAL,CAAU,KAAK,UAAL,EAAV,CACH,C,0DAEI,I,CAAM,I,CAAM,gBACb,GAAI,EAAE,WAAF,CAAc,IAAd,CAAJ,CAAyB,CACrB,KAAO,KAAK,UAAL,EACV,CAED,KAAK,MAAL,CAAY,IAAZ,EACA,EAAE,IAAF,CAAO,CACH,KAAM,MADH,CAEH,IAAK,YAAY,IAAZ,CAAiB,aAFnB,CAGH,QAAS,CACL,eAAgB,YAAY,IAAZ,CAAiB,SAD5B,CAHN,CAMH,KAAM,CACF,UAAW,IADT,CANH,CAAP,EASG,IATH,CASQ,cAAQ,CACZ,MAAK,MAAL,CAAY,KAAZ,EACA,EAAE,aAAF,EAAiB,OAAjB,CAAyB,EAAzB,EAA6B,IAA7B,CAAkC,IAAlC,EAAwC,SAAxC,CAAkD,EAAlD,CAAsD,UAAM,CACxD,YAAY,GAAZ,GACA,MAAK,iBAAL,GACA,MAAK,eAAL,GACA,MAAK,UAAL,GACA,MAAK,SAAL,GACA,MAAK,iBAAL,GACA,MAAK,SAAL,GACA,GAAI,EAAE,UAAF,CAAa,IAAb,CAAJ,CAAwB,CACpB,MAAO,OACV,CACJ,CAXD,EAYA,EAAE,iBAAF,EAAqB,OAArB,GAEA,GAAI,OAAO,MAAP,mCAAO,KAAP,KAAiB,QAArB,CAA+B,CAC3B,MAAM,aAAN,CAAoB,SAAS,cAAT,CAAwB,oBAAxB,CAApB,CACH,CACJ,CA5BD,EA4BG,IA5BH,CA4BQ,eAAS,CACb,MAAK,MAAL,CAAY,KAAZ,EACA,GAAI,EAAE,UAAF,CAAa,IAAb,CAAJ,CAAwB,CACpB,MAAO,MAAK,GAAI,MAAJ,CAAU,8BAAV,CAAL,CACV,CAED,GAAK,OAAS,EAAT,EAAe,OAAS,GAAzB,EAAiC,MAAM,MAAN,GAAiB,GAAtD,CAA2D,CACvD,MAAO,OAAK,IAAL,CAAU,EAAV,CAAc,IAAd,CACV,CAED,KAAK,CACD,KAAM,OADL,CAED,MAAO,YAFN,CAGD,KAAM,MAAM,YAAN,CAAmB,MAAnB,CAA0B,CAA1B,EAA6B,MAA7B,EAAuC,+EAH5C,CAAL,EAKA,QAAQ,KAAR,CAAc,KAAd,CACH,CA5CD,CA6CH,C,sCAEM,I,CAAM,CACT,GAAI,IAAJ,CAAS,CACL,EAAE,eAAF,EAAmB,MAAnB,CAA0B,GAA1B,CACH,CAFD,IAEO,CACH,EAAE,eAAF,EAAmB,OAAnB,CAA2B,GAA3B,CACH,CACJ,C,6DAEmB,iBAChB,EAAE,+BAAF,EAAmC,MAAnC,GAA4C,EAA5C,CAA+C,OAA/C,CAAwD,UAAM,CAC1D,EAAE,+BAAF,EAAmC,QAAnC,CAA4C,SAA5C,EACA,OAAK,IAAL,EACH,CAHD,CAIH,C,+CAEY,CACT,EAAE,8BAAF,EAAkC,EAAlC,CAAqC,OAArC,CAA8C,eAAS,CACnD,MAAM,cAAN,EACH,CAFD,CAGH,C,6CAEW,CACR,EAAE,2BAAF,EAA+B,EAA/B,CAAkC,OAAlC,CAA2C,eAAS,CAChD,MAAM,cAAN,EACH,CAFD,CAGH,C,6DAEmB,CAChB,EAAE,oCAAF,EAAwC,EAAxC,CAA2C,WAA3C,CAAwD,eAAS,CAC7D,GAAI,aAAJ,GAAmB,cAAnB,EACH,CAFD,CAGH,C,yDAEiB,CACd,EAAE,4BAAF,EAAgC,MAAhC,GAAyC,EAAzC,CAA4C,OAA5C,CAAqD,UAAM,CACvD,GAAI,aAAJ,GAAmB,MAAnB,CAA0B,EAAE,eAAF,EAAmB,IAAnB,CAAwB,aAAxB,GAA0C,GAApE,CACH,CAFD,CAGH,C,6CAEW,CACV,EAAE,kBAAF,EAAsB,EAAtB,CAAyB,WAAzB,CAAsC,eAAS,CAC3C,GAAI,MAAM,KAAN,GAAgB,CAApB,CAAuB,CACnB,GAAI,EAAE,MAAM,MAAR,EAAgB,EAAhB,CAAmB,IAAnB,GAA4B,EAAE,MAAM,MAAR,EAAgB,EAAhB,CAAmB,gCAAnB,CAAhC,CAAsF,CAClF,GAAI,aAAJ,GAAmB,YAAnB,CAAgC,KAAhC,CACH,CAFD,IAEO,IAAI,EAAE,MAAM,MAAR,EAAgB,EAAhB,CAAmB,IAAnB,GAA4B,EAAE,MAAM,MAAR,EAAgB,EAAhB,CAAmB,mCAAnB,CAAhC,CAAyF,CAC5F,GAAI,aAAJ,GAAmB,eAAnB,CAAmC,KAAnC,CACH,CAED,GAAI,aAAJ,GAAmB,iBAAnB,EACH,CACJ,CAVD,CAWD,C,+CAEY,CACT,MAAO,oBAAmB,OAAO,QAAP,CAAgB,IAAhB,CAAqB,SAArB,CAA+B,CAA/B,CAAnB,CACV,C,0BAIL,OAAO,KAAP,CAAe,GAAI,YAAnB","file":"filemanager.min.js","sourcesContent":["\"use strict\";\n\n// Copyright (c) 2015 - 2017 Dane Everitt \n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass ActionsClass {\n constructor(element, menu) {\n this.element = element;\n this.menu = menu;\n }\n\n destroy() {\n this.element = undefined;\n }\n\n sanitizedString(value) {\n return $('
    ').text(value).html();\n }\n\n folder(path) {\n let inputValue\n if (path) {\n inputValue = path\n } else {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.data('name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n if ($(this.element).data('type') === 'file') {\n inputValue = currentPath;\n } else {\n inputValue = `${currentPath}${currentName}/`;\n }\n }\n\n swal({\n type: 'input',\n title: 'Create Folder',\n text: 'Please enter the path and folder name below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: inputValue\n }, (val) => {\n if (val === false) {\n return false;\n }\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/folder`,\n timeout: 10000,\n data: JSON.stringify({\n path: val,\n }),\n }).done(data => {\n swal.close();\n Files.list();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n }\n\n move() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n swal({\n type: 'input',\n title: 'Move File',\n text: 'Please enter the new path for the file below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: `${currentPath}${currentName}`,\n }, (val) => {\n if (val === false) {\n return false;\n }\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/move`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${val}`,\n }),\n }).done(data => {\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\n swal.close();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n\n }\n\n rename() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentLink = nameBlock.find('a');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const attachEditor = `\n \n \n `;\n\n nameBlock.html(attachEditor);\n const inputField = nameBlock.find('input');\n const inputLoader = nameBlock.find('.input-loader');\n\n inputField.focus();\n inputField.on('blur keydown', e => {\n // Save Field\n if (\n (e.type === 'keydown' && e.which === 27)\n || e.type === 'blur'\n || (e.type === 'keydown' && e.which === 13 && currentName === inputField.val())\n ) {\n if (!_.isEmpty(currentLink)) {\n nameBlock.html(currentLink);\n } else {\n nameBlock.html(currentName);\n }\n inputField.remove();\n ContextMenu.unbind().run();\n return;\n }\n\n if (e.type === 'keydown' && e.which !== 13) return;\n\n inputLoader.show();\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/rename`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${currentPath}${inputField.val()}`,\n }),\n }).done(data => {\n nameBlock.attr('data-name', inputField.val());\n if (!_.isEmpty(currentLink)) {\n let newLink = currentLink.attr('href');\n if (nameBlock.parent().data('type') !== 'folder') {\n newLink = newLink.substr(0, newLink.lastIndexOf('/')) + '/' + inputField.val();\n }\n currentLink.attr('href', newLink);\n nameBlock.html(\n currentLink.html(inputField.val())\n );\n } else {\n nameBlock.html(inputField.val());\n }\n inputField.remove();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n nameBlock.addClass('has-error').delay(2000).queue(() => {\n nameBlock.removeClass('has-error').dequeue();\n });\n inputField.popover({\n animation: true,\n placement: 'top',\n content: error,\n title: 'Save Error'\n }).popover('show');\n }).always(() => {\n inputLoader.remove();\n ContextMenu.unbind().run();\n });\n });\n }\n\n copy() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n\n swal({\n type: 'input',\n title: 'Copy File',\n text: 'Please enter the new path for the copied file below.',\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n inputValue: `${currentPath}${currentName}`,\n }, (val) => {\n if (val === false) {\n return false;\n }\n\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/copy`,\n timeout: 10000,\n data: JSON.stringify({\n from: `${currentPath}${currentName}`,\n to: `${val}`,\n }),\n }).done(data => {\n swal({\n type: 'success',\n title: '',\n text: 'File successfully copied.'\n });\n Files.list();\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: '',\n text: error,\n });\n });\n });\n }\n\n download() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const fileName = decodeURIComponent(nameBlock.attr('data-name'));\n const filePath = decodeURIComponent(nameBlock.data('path'));\n\n window.location = `/server/${Pterodactyl.server.uuidShort}/files/download/${filePath}${fileName}`;\n }\n\n delete() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const delPath = decodeURIComponent(nameBlock.data('path'));\n const delName = decodeURIComponent(nameBlock.data('name'));\n\n swal({\n type: 'warning',\n title: '',\n text: 'Are you sure you want to delete ' + this.sanitizedString(delName) + '?',\n html: true,\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true\n }, () => {\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`,\n timeout: 10000,\n data: JSON.stringify({\n items: [`${delPath}${delName}`]\n }),\n }).done(data => {\n nameBlock.parent().addClass('warning').delay(200).fadeOut();\n swal({\n type: 'success',\n title: 'File Deleted'\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: 'An error occurred while attempting to delete this file. Please try again.',\n });\n });\n });\n }\n\n toggleMassActions() {\n if ($('#file_listing input[type=\"checkbox\"]:checked').length) {\n $('#mass_actions').removeClass('disabled');\n } else {\n $('#mass_actions').addClass('disabled');\n }\n }\n\n toggleHighlight(event) {\n const parent = $(event.currentTarget);\n const item = $(event.currentTarget).find('input');\n\n if($(item).is(':checked')) {\n $(item).prop('checked', false);\n parent.removeClass('warning').delay(200);\n } else {\n $(item).prop('checked', true);\n parent.addClass('warning').delay(200);\n }\n }\n\n highlightAll(event) {\n let parent;\n const item = $(event.currentTarget).find('input');\n\n if($(item).is(':checked')) {\n $('#file_listing input[type=checkbox]').prop('checked', false);\n $('#file_listing input[data-action=\"addSelection\"]').each(function() {\n parent = $(this).closest('tr');\n parent.removeClass('warning').delay(200);\n });\n } else {\n $('#file_listing input[type=checkbox]').prop('checked', true);\n $('#file_listing input[data-action=\"addSelection\"]').each(function() {\n parent = $(this).closest('tr');\n parent.addClass('warning').delay(200);\n });\n }\n }\n\n deleteSelected() {\n let selectedItems = [];\n let selectedItemsElements = [];\n let parent;\n let nameBlock;\n let delLocation;\n\n $('#file_listing input[data-action=\"addSelection\"]:checked').each(function() {\n parent = $(this).closest('tr');\n nameBlock = $(parent).find('td[data-identifier=\"name\"]');\n delLocation = decodeURIComponent(nameBlock.data('path')) + decodeURIComponent(nameBlock.data('name'));\n\n selectedItems.push(delLocation);\n selectedItemsElements.push(parent);\n });\n\n if (selectedItems.length != 0)\n {\n let formattedItems = \"\";\n let i = 0;\n $.each(selectedItems, function(key, value) {\n formattedItems += (\"\" + this.sanitizedString(value) + \", \");\n i++;\n return i < 5;\n });\n\n formattedItems = formattedItems.slice(0, -2);\n if (selectedItems.length > 5) {\n formattedItems += ', and ' + (selectedItems.length - 5) + ' other(s)';\n }\n\n swal({\n type: 'warning',\n title: '',\n text: 'Are you sure you want to delete the following files: ' + this.sanitizedString(formattedItems) + '?',\n html: true,\n showCancelButton: true,\n showConfirmButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true\n }, () => {\n $.ajax({\n type: 'POST',\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/delete`,\n timeout: 10000,\n data: JSON.stringify({\n items: selectedItems\n }),\n }).done(data => {\n $('#file_listing input:checked').each(function() {\n $(this).prop('checked', false);\n });\n\n $.each(selectedItemsElements, function() {\n $(this).addClass('warning').delay(200).fadeOut();\n })\n\n swal({\n type: 'success',\n title: 'Files Deleted'\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: 'An error occurred while attempting to delete these files. Please try again.',\n });\n });\n });\n } else {\n swal({\n type: 'warning',\n title: '',\n text: 'Please select files/folders to delete.',\n });\n }\n }\n\n decompress() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const compPath = decodeURIComponent(nameBlock.data('path'));\n const compName = decodeURIComponent(nameBlock.data('name'));\n\n swal({\n title: ' Decompressing...',\n text: 'This might take a few seconds to complete.',\n html: true,\n allowOutsideClick: false,\n allowEscapeKey: false,\n showConfirmButton: false,\n });\n\n $.ajax({\n type: 'POST',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/decompress`,\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n data: JSON.stringify({\n files: `${compPath}${compName}`\n })\n }).done(data => {\n swal.close();\n Files.list(compPath);\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: error\n });\n });\n }\n\n compress() {\n const nameBlock = $(this.element).find('td[data-identifier=\"name\"]');\n const compPath = decodeURIComponent(nameBlock.data('path'));\n const compName = decodeURIComponent(nameBlock.data('name'));\n\n $.ajax({\n type: 'POST',\n url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/v1/server/file/compress`,\n headers: {\n 'X-Access-Token': Pterodactyl.server.daemonSecret,\n 'X-Access-Server': Pterodactyl.server.uuid,\n },\n contentType: 'application/json; charset=utf-8',\n data: JSON.stringify({\n files: `${compPath}${compName}`,\n to: compPath.toString()\n })\n }).done(data => {\n Files.list(compPath, err => {\n if (err) return;\n const fileListing = $('#file_listing').find(`[data-name=\"${data.saved_as}\"]`).parent();\n fileListing.addClass('success pulsate').delay(3000).queue(() => {\n fileListing.removeClass('success pulsate').dequeue();\n });\n });\n }).fail(jqXHR => {\n console.error(jqXHR);\n var error = 'An error occurred while trying to process this request.';\n if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {\n error = jqXHR.responseJSON.error;\n }\n swal({\n type: 'error',\n title: 'Whoops!',\n html: true,\n text: this.sanitizedString(error)\n });\n });\n }\n}\n","\"use strict\";\n\n// Copyright (c) 2015 - 2017 Dane Everitt \n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass ContextMenuClass {\n constructor() {\n this.activeLine = null;\n }\n\n run() {\n this.directoryClick();\n this.rightClick();\n }\n\n makeMenu(parent) {\n $(document).find('#fileOptionMenu').remove();\n if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\n\n let newFilePath = $('#file_listing').data('current-dir');\n if (parent.data('type') === 'folder') {\n const nameBlock = parent.find('td[data-identifier=\"name\"]');\n const currentName = decodeURIComponent(nameBlock.attr('data-name'));\n const currentPath = decodeURIComponent(nameBlock.data('path'));\n newFilePath = `${currentPath}${currentName}`;\n }\n\n let buildMenu = '
      ';\n\n if (Pterodactyl.permissions.moveFiles) {\n buildMenu += '
    • Rename
    • \\\n
    • Move
    • ';\n }\n\n if (Pterodactyl.permissions.copyFiles) {\n buildMenu += '
    • Copy
    • ';\n }\n\n if (Pterodactyl.permissions.compressFiles) {\n buildMenu += '
    • Compress
    • ';\n }\n\n if (Pterodactyl.permissions.decompressFiles) {\n buildMenu += '
    • Decompress
    • ';\n }\n\n if (Pterodactyl.permissions.createFiles) {\n buildMenu += '
    • \\\n
    • ').text(newFilePath).html() + '\" class=\"text-muted\"> New File
    • \\\n
    • New Folder
    • ';\n }\n\n if (Pterodactyl.permissions.downloadFiles || Pterodactyl.permissions.deleteFiles) {\n buildMenu += '
    • ';\n }\n\n if (Pterodactyl.permissions.downloadFiles) {\n buildMenu += '
    • Download
    • ';\n }\n\n if (Pterodactyl.permissions.deleteFiles) {\n buildMenu += '
    • Delete
    • ';\n }\n\n buildMenu += '
    ';\n return buildMenu;\n }\n\n rightClick() {\n $('[data-action=\"toggleMenu\"]').on('mousedown', event => {\n event.preventDefault();\n if ($(document).find('#fileOptionMenu').is(':visible')) {\n $('body').trigger('click');\n return;\n }\n this.showMenu(event);\n });\n $('#file_listing > tbody td').on('contextmenu', event => {\n this.showMenu(event);\n });\n }\n\n showMenu(event) {\n const parent = $(event.target).closest('tr');\n const menu = $(this.makeMenu(parent));\n\n if (parent.data('type') === 'disabled') return;\n event.preventDefault();\n\n $(menu).appendTo('body');\n $(menu).data('invokedOn', $(event.target)).show().css({\n position: 'absolute',\n left: event.pageX - 150,\n top: event.pageY,\n });\n\n this.activeLine = parent;\n this.activeLine.addClass('active');\n\n // Handle Events\n const Actions = new ActionsClass(parent, menu);\n if (Pterodactyl.permissions.moveFiles) {\n $(menu).find('li[data-action=\"move\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.move();\n });\n $(menu).find('li[data-action=\"rename\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.rename();\n });\n }\n\n if (Pterodactyl.permissions.copyFiles) {\n $(menu).find('li[data-action=\"copy\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.copy();\n });\n }\n\n if (Pterodactyl.permissions.compressFiles) {\n if (parent.data('type') === 'folder') {\n $(menu).find('li[data-action=\"compress\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"compress\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.compress();\n });\n }\n\n if (Pterodactyl.permissions.decompressFiles) {\n if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) {\n $(menu).find('li[data-action=\"decompress\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"decompress\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.decompress();\n });\n }\n\n if (Pterodactyl.permissions.createFiles) {\n $(menu).find('li[data-action=\"folder\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.folder();\n });\n }\n\n if (Pterodactyl.permissions.downloadFiles) {\n if (parent.data('type') === 'file') {\n $(menu).find('li[data-action=\"download\"]').removeClass('hidden');\n }\n $(menu).find('li[data-action=\"download\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.download();\n });\n }\n\n if (Pterodactyl.permissions.deleteFiles) {\n $(menu).find('li[data-action=\"delete\"]').unbind().on('click', e => {\n e.preventDefault();\n Actions.delete();\n });\n }\n\n $(window).unbind().on('click', event => {\n if($(event.target).is('.disable-menu-hide')) {\n event.preventDefault();\n return;\n }\n $(menu).unbind().remove();\n if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active');\n });\n }\n\n directoryClick() {\n $('a[data-action=\"directory-view\"]').on('click', function (event) {\n event.preventDefault();\n\n const path = $(this).parent().data('path') || '';\n const name = $(this).parent().data('name') || '';\n\n window.location.hash = encodeURIComponent(path + name);\n Files.list();\n });\n }\n}\n\nwindow.ContextMenu = new ContextMenuClass;\n","\"use strict\";\n\n// Copyright (c) 2015 - 2017 Dane Everitt \n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nclass FileManager {\n constructor() {\n this.list(this.decodeHash());\n }\n\n list(path, next) {\n if (_.isUndefined(path)) {\n path = this.decodeHash();\n }\n\n this.loader(true);\n $.ajax({\n type: 'POST',\n url: Pterodactyl.meta.directoryList,\n headers: {\n 'X-CSRF-Token': Pterodactyl.meta.csrftoken,\n },\n data: {\n directory: path,\n },\n }).done(data => {\n this.loader(false);\n $('#load_files').slideUp(10).html(data).slideDown(10, () => {\n ContextMenu.run();\n this.reloadFilesButton();\n this.addFolderButton();\n this.selectItem();\n this.selectAll();\n this.selectiveDeletion();\n this.selectRow();\n if (_.isFunction(next)) {\n return next();\n }\n });\n $('#internal_alert').slideUp();\n\n if (typeof Siofu === 'object') {\n Siofu.listenOnInput(document.getElementById(\"files_touch_target\"));\n }\n }).fail(jqXHR => {\n this.loader(false);\n if (_.isFunction(next)) {\n return next(new Error('Failed to load file listing.'));\n }\n\n if ((path !== '' && path !== '/') && jqXHR.status === 404) {\n return this.list('', next);\n }\n\n swal({\n type: 'error',\n title: 'File Error',\n text: jqXHR.responseJSON.errors[0].detail || 'An error occurred while attempting to process this request. Please try again.',\n });\n console.error(jqXHR);\n });\n }\n\n loader(show) {\n if (show){\n $('.file-overlay').fadeIn(100);\n } else {\n $('.file-overlay').fadeOut(100);\n }\n }\n\n reloadFilesButton() {\n $('i[data-action=\"reload-files\"]').unbind().on('click', () => {\n $('i[data-action=\"reload-files\"]').addClass('fa-spin');\n this.list();\n });\n }\n\n selectItem() {\n $('[data-action=\"addSelection\"]').on('click', event => {\n event.preventDefault();\n });\n }\n\n selectAll() {\n $('[data-action=\"selectAll\"]').on('click', event => {\n event.preventDefault();\n });\n }\n\n selectiveDeletion() {\n $('[data-action=\"selective-deletion\"]').on('mousedown', event => {\n new ActionsClass().deleteSelected();\n });\n }\n\n addFolderButton() {\n $('[data-action=\"add-folder\"]').unbind().on('click', () => {\n new ActionsClass().folder($('#file_listing').data('current-dir') || '/');\n });\n }\n\n selectRow() {\n $('#file_listing tr').on('mousedown', event => {\n if (event.which === 1) {\n if ($(event.target).is('th') || $(event.target).is('input[data-action=\"selectAll\"]')) {\n new ActionsClass().highlightAll(event);\n } else if ($(event.target).is('td') || $(event.target).is('input[data-action=\"addSelection\"]')) {\n new ActionsClass().toggleHighlight(event);\n }\n\n new ActionsClass().toggleMassActions();\n }\n });\n }\n\n decodeHash() {\n return decodeURIComponent(window.location.hash.substring(1));\n }\n\n}\n\nwindow.Files = new FileManager;\n"]} \ No newline at end of file diff --git a/public/themes/pterodactyl/js/frontend/files/src/actions.js b/public/themes/pterodactyl/js/frontend/files/src/actions.js index 0c3b839d6..5b9a95c54 100644 --- a/public/themes/pterodactyl/js/frontend/files/src/actions.js +++ b/public/themes/pterodactyl/js/frontend/files/src/actions.js @@ -29,6 +29,10 @@ class ActionsClass { this.element = undefined; } + sanitizedString(value) { + return $('
    ').text(value).html(); + } + folder(path) { let inputValue if (path) { @@ -296,7 +300,7 @@ class ActionsClass { swal({ type: 'warning', title: '', - text: 'Are you sure you want to delete ' + delName + '?', + text: 'Are you sure you want to delete ' + this.sanitizedString(delName) + '?', html: true, showCancelButton: true, showConfirmButton: true, @@ -394,7 +398,7 @@ class ActionsClass { let formattedItems = ""; let i = 0; $.each(selectedItems, function(key, value) { - formattedItems += ("" + value + ", "); + formattedItems += ("" + this.sanitizedString(value) + ", "); i++; return i < 5; }); @@ -407,7 +411,7 @@ class ActionsClass { swal({ type: 'warning', title: '', - text: 'Are you sure you want to delete the following files: ' + formattedItems + '?', + text: 'Are you sure you want to delete the following files: ' + this.sanitizedString(formattedItems) + '?', html: true, showCancelButton: true, showConfirmButton: true, @@ -536,7 +540,7 @@ class ActionsClass { type: 'error', title: 'Whoops!', html: true, - text: error + text: this.sanitizedString(error) }); }); } diff --git a/public/themes/pterodactyl/js/frontend/files/src/contextmenu.js b/public/themes/pterodactyl/js/frontend/files/src/contextmenu.js index 0e6904385..6796f8b09 100644 --- a/public/themes/pterodactyl/js/frontend/files/src/contextmenu.js +++ b/public/themes/pterodactyl/js/frontend/files/src/contextmenu.js @@ -62,7 +62,7 @@ class ContextMenuClass { if (Pterodactyl.permissions.createFiles) { buildMenu += '
  • \ -
  • New File
  • \ +
  • New File
  • \
  • New Folder
  • '; } diff --git a/resources/themes/pterodactyl/server/files/index.blade.php b/resources/themes/pterodactyl/server/files/index.blade.php index 0c1181b8f..4dd0c0901 100644 --- a/resources/themes/pterodactyl/server/files/index.blade.php +++ b/resources/themes/pterodactyl/server/files/index.blade.php @@ -44,7 +44,7 @@ {!! Theme::js('vendor/lodash/lodash.js') !!} {!! Theme::js('vendor/siofu/client.min.js') !!} @if(App::environment('production')) - {!! Theme::js('js/frontend/files/filemanager.min.js?updated-cancel-buttons') !!} + {!! Theme::js('js/frontend/files/filemanager.min.js?hash=cd7ec731dc633e23ec36144929a237d18c07d2f0') !!} @else {!! Theme::js('js/frontend/files/src/index.js') !!} {!! Theme::js('js/frontend/files/src/contextmenu.js') !!} From 2db7928b763378f66ddaa88aa75e98db952353e8 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 21 Jun 2019 21:39:24 -0700 Subject: [PATCH 12/42] Don't expose existence of account when an incorrect password is provided and the user has 2FA enabled --- app/Http/Controllers/Auth/LoginController.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index d00b22faa..c18b004b4 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -126,21 +126,20 @@ class LoginController extends Controller return $this->sendFailedLoginResponse($request); } - $validCredentials = password_verify($request->input('password'), $user->password); + if (! password_verify($request->input('password'), $user->password)) { + return $this->sendFailedLoginResponse($request, $user); + } + if ($user->use_totp) { $token = str_random(64); - $this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => $validCredentials], 5); + $this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => true], 5); return redirect()->route('auth.totp')->with('authentication_token', $token); } - if ($validCredentials) { - $this->auth->guard()->login($user, true); + $this->auth->guard()->login($user, true); - return $this->sendLoginResponse($request); - } - - return $this->sendFailedLoginResponse($request, $user); + return $this->sendLoginResponse($request); } /** @@ -161,12 +160,13 @@ class LoginController extends Controller /** * Handle a login where the user is required to provide a TOTP authentication - * token. In order to add additional layers of security, users are not - * informed of an incorrect password until this stage, forcing them to - * provide a token on each login attempt. + * token. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response + * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException + * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException + * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException */ public function loginUsingTotp(Request $request) { @@ -181,7 +181,7 @@ class LoginController extends Controller return $this->sendFailedLoginResponse($request); } - if (is_null($request->input('2fa_token')) || ! array_get($cache, 'valid_credentials')) { + if (is_null($request->input('2fa_token'))) { return $this->sendFailedLoginResponse($request, $user); } From 092e7e79fff858ee026608c7dbccab165a67526f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 21 Jun 2019 21:55:09 -0700 Subject: [PATCH 13/42] Change 2FA service to generate the secret on our own and use an external QR service to display the image --- .../Controllers/Base/SecurityController.php | 4 +- app/Services/Users/TwoFactorSetupService.php | 42 ++--- composer.json | 1 - composer.lock | 151 +----------------- .../Users/TwoFactorSetupServiceTest.php | 32 ++-- 5 files changed, 44 insertions(+), 186 deletions(-) diff --git a/app/Http/Controllers/Base/SecurityController.php b/app/Http/Controllers/Base/SecurityController.php index 3bb85a0b5..2aa9ac129 100644 --- a/app/Http/Controllers/Base/SecurityController.php +++ b/app/Http/Controllers/Base/SecurityController.php @@ -90,8 +90,10 @@ class SecurityController extends Controller */ public function generateTotp(Request $request) { + $totpData = $this->twoFactorSetupService->handle($request->user()); + return response()->json([ - 'qrImage' => $this->twoFactorSetupService->handle($request->user()), + 'qrImage' => 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' . $totpData, ]); } diff --git a/app/Services/Users/TwoFactorSetupService.php b/app/Services/Users/TwoFactorSetupService.php index 786cf3d35..7afb5f2bc 100644 --- a/app/Services/Users/TwoFactorSetupService.php +++ b/app/Services/Users/TwoFactorSetupService.php @@ -1,22 +1,18 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Users; +use Exception; +use RuntimeException; use Pterodactyl\Models\User; -use PragmaRX\Google2FAQRCode\Google2FA; use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Illuminate\Contracts\Config\Repository as ConfigRepository; class TwoFactorSetupService { + const VALID_BASE32_CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; + /** * @var \Illuminate\Contracts\Config\Repository */ @@ -27,11 +23,6 @@ class TwoFactorSetupService */ private $encrypter; - /** - * @var PragmaRX\Google2FAQRCode\Google2FA - */ - private $google2FA; - /** * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface */ @@ -42,24 +33,22 @@ class TwoFactorSetupService * * @param \Illuminate\Contracts\Config\Repository $config * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter - * @param PragmaRX\Google2FAQRCode\Google2FA $google2FA * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository */ public function __construct( ConfigRepository $config, Encrypter $encrypter, - Google2FA $google2FA, UserRepositoryInterface $repository ) { $this->config = $config; $this->encrypter = $encrypter; - $this->google2FA = $google2FA; $this->repository = $repository; } /** * Generate a 2FA token and store it in the database before returning the - * QR code image. + * QR code URL. This URL will need to be attached to a QR generating service in + * order to function. * * @param \Pterodactyl\Models\User $user * @return string @@ -69,13 +58,26 @@ class TwoFactorSetupService */ public function handle(User $user): string { - $secret = $this->google2FA->generateSecretKey($this->config->get('pterodactyl.auth.2fa.bytes')); - $image = $this->google2FA->getQRCodeInline($this->config->get('app.name'), $user->email, $secret); + $secret = ''; + try { + for ($i = 0; $i < $this->config->get('pterodactyl.auth.2fa.bytes', 16); $i++) { + $secret .= substr(self::VALID_BASE32_CHARACTERS, random_int(0, 31), 1); + } + } catch (Exception $exception) { + throw new RuntimeException($exception->getMessage(), 0, $exception); + } $this->repository->withoutFreshModel()->update($user->id, [ 'totp_secret' => $this->encrypter->encrypt($secret), ]); - return $image; + $company = $this->config->get('app.name'); + + return sprintf( + 'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s', + rawurlencode($company), + rawurlencode($user->email), + rawurlencode($secret) + ); } } diff --git a/composer.json b/composer.json index 95c96926d..dcac00e1d 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,6 @@ "matriphe/iso-639": "^1.2", "nesbot/carbon": "^1.22", "pragmarx/google2fa": "^5.0", - "pragmarx/google2fa-qrcode": "^1.0.3", "predis/predis": "^1.1", "prologue/alerts": "^0.4", "ramsey/uuid": "^3.7", diff --git a/composer.lock b/composer.lock index f1a7e59f2..c5530927e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9c03519785d01a8f710a0f7e65f602e8", + "content-hash": "8a99f4996405b8080a0dcabb2609c39d", "packages": [ { "name": "appstract/laravel-blade-directives", @@ -142,55 +142,6 @@ ], "time": "2018-11-21T19:18:43+00:00" }, - { - "name": "bacon/bacon-qr-code", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/Bacon/BaconQrCode.git", - "reference": "eaac909da3ccc32b748a65b127acd8918f58d9b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/eaac909da3ccc32b748a65b127acd8918f58d9b0", - "reference": "eaac909da3ccc32b748a65b127acd8918f58d9b0", - "shasum": "" - }, - "require": { - "dasprid/enum": "^1.0", - "ext-iconv": "*", - "php": "^7.1" - }, - "require-dev": { - "phly/keep-a-changelog": "^1.4", - "phpunit/phpunit": "^6.4", - "squizlabs/php_codesniffer": "^3.1" - }, - "suggest": { - "ext-imagick": "to generate QR code images" - }, - "type": "library", - "autoload": { - "psr-4": { - "BaconQrCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "http://www.dasprids.de", - "role": "Developer" - } - ], - "description": "BaconQrCode is a QR code generator for PHP.", - "homepage": "https://github.com/Bacon/BaconQrCode", - "time": "2018-04-25T17:53:56+00:00" - }, { "name": "cakephp/chronos", "version": "1.2.3", @@ -248,48 +199,6 @@ ], "time": "2018-10-18T22:02:21+00:00" }, - { - "name": "dasprid/enum", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/DASPRiD/Enum.git", - "reference": "631ef6e638e9494b0310837fa531bedd908fc22b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/631ef6e638e9494b0310837fa531bedd908fc22b", - "reference": "631ef6e638e9494b0310837fa531bedd908fc22b", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "^6.4", - "squizlabs/php_codesniffer": "^3.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DASPRiD\\Enum\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "https://dasprids.de/" - } - ], - "description": "PHP 7.1 enum implementation", - "keywords": [ - "enum", - "map" - ], - "time": "2017-10-25T22:45:27+00:00" - }, { "name": "dnoegel/php-xdg-base-dir", "version": "0.1", @@ -2253,64 +2162,6 @@ ], "time": "2019-03-19T22:44:16+00:00" }, - { - "name": "pragmarx/google2fa-qrcode", - "version": "v1.0.3", - "source": { - "type": "git", - "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", - "reference": "fd5ff0531a48b193a659309cc5fb882c14dbd03f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/fd5ff0531a48b193a659309cc5fb882c14dbd03f", - "reference": "fd5ff0531a48b193a659309cc5fb882c14dbd03f", - "shasum": "" - }, - "require": { - "bacon/bacon-qr-code": "~1.0|~2.0", - "php": ">=5.4", - "pragmarx/google2fa": ">=4.0" - }, - "require-dev": { - "khanamiryan/qrcode-detector-decoder": "^1.0", - "phpunit/phpunit": "~4|~5|~6|~7" - }, - "type": "library", - "extra": { - "component": "package", - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "PragmaRX\\Google2FAQRCode\\": "src/", - "PragmaRX\\Google2FAQRCode\\Tests\\": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "role": "Creator & Designer" - } - ], - "description": "QR Code package for Google2FA", - "keywords": [ - "2fa", - "Authentication", - "Two Factor Authentication", - "google2fa", - "qr code", - "qrcode" - ], - "time": "2019-03-20T16:42:58+00:00" - }, { "name": "predis/predis", "version": "v1.1.1", diff --git a/tests/Unit/Services/Users/TwoFactorSetupServiceTest.php b/tests/Unit/Services/Users/TwoFactorSetupServiceTest.php index 09d37b0d7..2701071a7 100644 --- a/tests/Unit/Services/Users/TwoFactorSetupServiceTest.php +++ b/tests/Unit/Services/Users/TwoFactorSetupServiceTest.php @@ -5,7 +5,6 @@ namespace Tests\Unit\Services\Users; use Mockery as m; use Tests\TestCase; use Pterodactyl\Models\User; -use PragmaRX\Google2FAQRCode\Google2FA; use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Services\Users\TwoFactorSetupService; @@ -23,11 +22,6 @@ class TwoFactorSetupServiceTest extends TestCase */ private $encrypter; - /** - * @var PragmaRX\Google2FAQRCode\Google2FA|\Mockery\Mock - */ - private $google2FA; - /** * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock */ @@ -42,7 +36,6 @@ class TwoFactorSetupServiceTest extends TestCase $this->config = m::mock(Repository::class); $this->encrypter = m::mock(Encrypter::class); - $this->google2FA = m::mock(Google2FA::class); $this->repository = m::mock(UserRepositoryInterface::class); } @@ -53,16 +46,27 @@ class TwoFactorSetupServiceTest extends TestCase { $model = factory(User::class)->make(); - $this->config->shouldReceive('get')->with('pterodactyl.auth.2fa.bytes')->once()->andReturn(32); - $this->google2FA->shouldReceive('generateSecretKey')->with(32)->once()->andReturn('secretKey'); - $this->config->shouldReceive('get')->with('app.name')->once()->andReturn('CompanyName'); - $this->google2FA->shouldReceive('getQRCodeInline')->with('CompanyName', $model->email, 'secretKey')->once()->andReturn('http://url.com'); - $this->encrypter->shouldReceive('encrypt')->with('secretKey')->once()->andReturn('encryptedSecret'); + $this->config->shouldReceive('get')->with('pterodactyl.auth.2fa.bytes', 16)->andReturn(32); + $this->config->shouldReceive('get')->with('app.name')->andReturn('Company Name'); + $this->encrypter->shouldReceive('encrypt') + ->with(m::on(function ($value) { + return preg_match('/([A-Z234567]{32})/', $value) !== false; + })) + ->once() + ->andReturn('encryptedSecret'); + $this->repository->shouldReceive('withoutFreshModel->update')->with($model->id, ['totp_secret' => 'encryptedSecret'])->once()->andReturnNull(); $response = $this->getService()->handle($model); $this->assertNotEmpty($response); - $this->assertSame('http://url.com', $response); + + $companyName = preg_quote(rawurlencode('Company Name')); + $email = preg_quote(rawurlencode($model->email)); + + $this->assertRegExp( + '/otpauth:\/\/totp\/' . $companyName . ':' . $email . '\?secret=([A-Z234567]{32})&issuer=' . $companyName . '/', + $response + ); } /** @@ -72,6 +76,6 @@ class TwoFactorSetupServiceTest extends TestCase */ private function getService(): TwoFactorSetupService { - return new TwoFactorSetupService($this->config, $this->encrypter, $this->google2FA, $this->repository); + return new TwoFactorSetupService($this->config, $this->encrypter, $this->repository); } } From a5be993796898a8a15a94cbd130200d84205f8e2 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 21 Jun 2019 21:56:30 -0700 Subject: [PATCH 14/42] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b765b87c8..1925cd799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,13 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. ## v0.7.14 (Derelict Dermodactylus) ### Fixed * **[SECURITY]** Fixes an XSS vulnerability when performing certain actions in the file manager. +* **[SECURITY]** Attempting to login as a user who has 2FA enabled will no longer request the 2FA token before validating +that their password is correct. This closes a user existence leak that would expose that an account exists if +it had 2FA enabled. ### Changed * Support for setting a node to listen on ports lower than 1024. +* QR code URLs are now generated without the use of an external library to reduce the dependency tree. * Regenerated database passwords now respect the same settings that were used when initially created. * Cleaned up 2FA QR code generation to use a more up-to-date library and API. * Console charts now properly start at 0 and scale based on server configuration. No more crazy spikes that From 93d0f1803f40b4cddadbe619b070337f685dd6eb Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 21 Jun 2019 21:57:28 -0700 Subject: [PATCH 15/42] Fix for styleci --- config/pterodactyl.php | 1 - 1 file changed, 1 deletion(-) diff --git a/config/pterodactyl.php b/config/pterodactyl.php index ba488dad2..4379c753b 100644 --- a/config/pterodactyl.php +++ b/config/pterodactyl.php @@ -1,6 +1,5 @@ Date: Fri, 21 Jun 2019 22:01:42 -0700 Subject: [PATCH 16/42] Fix broken test --- tests/Unit/Http/Controllers/Base/SecurityControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Http/Controllers/Base/SecurityControllerTest.php b/tests/Unit/Http/Controllers/Base/SecurityControllerTest.php index 363a59801..83cd7015d 100644 --- a/tests/Unit/Http/Controllers/Base/SecurityControllerTest.php +++ b/tests/Unit/Http/Controllers/Base/SecurityControllerTest.php @@ -95,7 +95,7 @@ class SecurityControllerTest extends ControllerTestCase $response = $this->getController()->generateTotp($this->request); $this->assertIsJsonResponse($response); - $this->assertResponseJsonEquals(['qrImage' => 'qrCodeImage'], $response); + $this->assertResponseJsonEquals(['qrImage' => 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=qrCodeImage'], $response); } /** From c38f78df84a8068ac32bddf360cf3167b3903340 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 21 Jun 2019 22:06:07 -0700 Subject: [PATCH 17/42] =?UTF-8?q?Bump=20for=20security=20release=20?= =?UTF-8?q?=F0=9F=94=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/app.php b/config/app.php index fccb8b888..4082feaa0 100644 --- a/config/app.php +++ b/config/app.php @@ -9,7 +9,7 @@ return [ | change this value if you are not maintaining your own internal versions. */ - 'version' => 'canary', + 'version' => '0.7.14', /* |-------------------------------------------------------------------------- From 7f3ab8aadfad1de9f52452e8173cfa34d2836ba5 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 13:11:27 -0700 Subject: [PATCH 18/42] Redesign the login form to not use the weird open fields --- resources/assets/styles/components/forms.css | 39 ++------------- .../auth/ForgotPasswordContainer.tsx | 25 +++++----- .../auth/LoginCheckpointContainer.tsx | 48 ------------------- .../components/auth/LoginContainer.tsx | 30 ++++++------ .../auth/ResetPasswordContainer.tsx | 27 +++++------ .../components/forms/OpenInputField.tsx | 43 ----------------- 6 files changed, 43 insertions(+), 169 deletions(-) delete mode 100644 resources/scripts/components/forms/OpenInputField.tsx diff --git a/resources/assets/styles/components/forms.css b/resources/assets/styles/components/forms.css index b083a8f79..93fca8d36 100644 --- a/resources/assets/styles/components/forms.css +++ b/resources/assets/styles/components/forms.css @@ -12,43 +12,10 @@ input[type=number] { -moz-appearance: textfield !important; } -/** - * Styles for the login form open input boxes. Label floats up above it when content - * is input and then sinks back down into the field if left empty. - */ -.input-open { - @apply .w-full .relative; -} - -.input-open > .input, .input-open > .input:disabled { - @apply .appearance-none .block .w-full .text-neutral-800 .border-b-2 .border-neutral-200 .py-3 .px-2 .bg-white; - - &:focus { - @apply .border-primary-400; - outline: 0; - transition: border 500ms ease-out; - } - - &:focus + label, &:valid + label, &.has-content + label { - @apply .text-neutral-800 .px-0 .cursor-pointer; - transform:translateY(-26px) - } - - &:required { - box-shadow: none; - } -} - -.input-open > label { - @apply .block .uppercase .tracking-wide .text-neutral-500 .text-xs .mb-2 .px-2 .absolute; - top: 14px; - transition: padding 200ms linear, transform 200ms ease-out; -} - /** * Styling for other forms throughout the Panel. */ -.input:not(.open-label) { +.input { @apply .appearance-none .p-3 .rounded .border .border-neutral-200 .text-neutral-800 .w-full; min-width: 0; transition: border 150ms linear; @@ -70,6 +37,10 @@ input[type=number] { @apply .bg-neutral-100 .border-neutral-200; } +label { + @apply .block .text-xs .font-medium .uppercase .text-neutral-700 .mb-2; +} + select:not(.appearance-none) { @apply .outline-none .appearance-none .block .bg-white .border .border-neutral-200 .text-neutral-400 .p-3 .pr-8 rounded; transition: border-color 150ms linear, color 150ms linear; diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index 9005c04b9..cbc4dfcfc 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -63,18 +63,19 @@ class ForgotPasswordContainer extends React.PureComponent { Request Password Reset
    -
    - -
    + + +

    + Enter your account email address to receive instructions on resetting your password. +

    +
    + + Return to Login + +
    ); diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index 2b0385e2a..f1600ffe0 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -12,8 +12,6 @@ type State = Readonly<{ }>; export default class LoginContainer extends React.PureComponent { - username = React.createRef(); - state: State = { isLoading: false, }; @@ -33,7 +31,7 @@ export default class LoginContainer extends React.PureComponent this.setState({ diff --git a/resources/scripts/components/auth/ResetPasswordContainer.tsx b/resources/scripts/components/auth/ResetPasswordContainer.tsx index 3483207ee..5e0783630 100644 --- a/resources/scripts/components/auth/ResetPasswordContainer.tsx +++ b/resources/scripts/components/auth/ResetPasswordContainer.tsx @@ -95,9 +95,9 @@ class ResetPasswordContainer extends React.PureComponent {
    - + {

    - + Date: Sat, 22 Jun 2019 13:53:41 -0700 Subject: [PATCH 20/42] New login page design --- .gitignore | 2 +- public/assets/pterodactyl.svg | 1 + .../auth/ForgotPasswordContainer.tsx | 5 +++-- .../auth/LoginCheckpointContainer.tsx | 5 +++-- .../scripts/components/auth/LoginContainer.tsx | 5 +++-- .../components/auth/LoginFormContainer.tsx | 18 ++++++++++++++++++ .../components/auth/ResetPasswordContainer.tsx | 5 +++-- .../pterodactyl/templates/auth/core.blade.php | 2 +- 8 files changed, 33 insertions(+), 10 deletions(-) create mode 100755 public/assets/pterodactyl.svg create mode 100644 resources/scripts/components/auth/LoginFormContainer.tsx diff --git a/.gitignore b/.gitignore index 99ba2aecf..3352c6ad9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ node_modules _ide_helper.php .phpstorm.meta.php .php_cs.cache -public/assets/* +public/assets/manifest.json # For local development with docker # Remove if we ever put the Dockerfile in the repo diff --git a/public/assets/pterodactyl.svg b/public/assets/pterodactyl.svg new file mode 100755 index 000000000..f3582adf2 --- /dev/null +++ b/public/assets/pterodactyl.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index aa6520c0b..38741f7fc 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -4,6 +4,7 @@ import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail'; import { connect } from 'react-redux'; import { pushFlashMessage, clearAllFlashMessages } from '@/redux/actions/flash'; import { httpErrorToHuman } from '@/api/http'; +import LoginFormContainer from '@/components/auth/LoginFormContainer'; type Props = Readonly<{ pushFlashMessage: typeof pushFlashMessage; @@ -60,7 +61,7 @@ class ForgotPasswordContainer extends React.PureComponent {

    Request Password Reset

    -
    + { Return to Login
    - +
    ); } diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx index 1f2dd89d2..b4c3efb05 100644 --- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx +++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx @@ -7,6 +7,7 @@ import MessageBox from '@/components/MessageBox'; import { Link } from 'react-router-dom'; import loginCheckpoint from '@/api/auth/loginCheckpoint'; import { httpErrorToHuman } from '@/api/http'; +import LoginFormContainer from '@/components/auth/LoginFormContainer'; type State = Readonly<{ isLoading: boolean; @@ -61,7 +62,7 @@ class LoginCheckpointContainer extends React.PureComponent -
    + This account is protected with two-factor authentication. A valid authentication token must be provided in order to continue. @@ -97,7 +98,7 @@ class LoginCheckpointContainer extends React.PureComponent
    - + ); } diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index f1600ffe0..e111a0305 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -3,6 +3,7 @@ import { Link, RouteComponentProps } from 'react-router-dom'; import login from '@/api/auth/login'; import { httpErrorToHuman } from '@/api/http'; import NetworkErrorMessage from '@/components/NetworkErrorMessage'; +import LoginFormContainer from '@/components/auth/LoginFormContainer'; type State = Readonly<{ errorMessage?: string; @@ -61,7 +62,7 @@ export default class LoginContainer extends React.PureComponent -
    + - +
    ); } diff --git a/resources/scripts/components/auth/LoginFormContainer.tsx b/resources/scripts/components/auth/LoginFormContainer.tsx new file mode 100644 index 000000000..f6f0223c4 --- /dev/null +++ b/resources/scripts/components/auth/LoginFormContainer.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; + +export default ({ className, ...props }: React.DetailedHTMLProps, HTMLFormElement>) => ( +
    +
    + +
    +
    + {props.children} +
    +
    +); diff --git a/resources/scripts/components/auth/ResetPasswordContainer.tsx b/resources/scripts/components/auth/ResetPasswordContainer.tsx index 5e0783630..dd4664b35 100644 --- a/resources/scripts/components/auth/ResetPasswordContainer.tsx +++ b/resources/scripts/components/auth/ResetPasswordContainer.tsx @@ -7,6 +7,7 @@ import performPasswordReset from '@/api/auth/performPasswordReset'; import { httpErrorToHuman } from '@/api/http'; import { connect } from 'react-redux'; import { pushFlashMessage, clearAllFlashMessages } from '@/redux/actions/flash'; +import LoginFormContainer from '@/components/auth/LoginFormContainer'; type State = Readonly<{ email?: string; @@ -91,7 +92,7 @@ class ResetPasswordContainer extends React.PureComponent { Reset Password -
    +
    @@ -136,7 +137,7 @@ class ResetPasswordContainer extends React.PureComponent { Return to Login
    - +
    ); } diff --git a/resources/themes/pterodactyl/templates/auth/core.blade.php b/resources/themes/pterodactyl/templates/auth/core.blade.php index 6c106020b..0889bb3a6 100644 --- a/resources/themes/pterodactyl/templates/auth/core.blade.php +++ b/resources/themes/pterodactyl/templates/auth/core.blade.php @@ -3,7 +3,7 @@ ]) @section('container') -
    +
    @endsection From ad61774171f2f14ac3eab2d4ec8b82cfebe64712 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 13:54:36 -0700 Subject: [PATCH 21/42] Whoops, dont mess up logging in if the code starts with 0 --- app/Http/Requests/Auth/LoginCheckpointRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Requests/Auth/LoginCheckpointRequest.php b/app/Http/Requests/Auth/LoginCheckpointRequest.php index 64fa7859e..158f5c465 100644 --- a/app/Http/Requests/Auth/LoginCheckpointRequest.php +++ b/app/Http/Requests/Auth/LoginCheckpointRequest.php @@ -25,7 +25,7 @@ class LoginCheckpointRequest extends FormRequest { return [ 'confirmation_token' => 'required|string', - 'authentication_code' => 'required|int', + 'authentication_code' => 'required|numeric', ]; } } From aabf9b8a703183fe4e220eb7378dfd090fb5bfd4 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 15:23:02 -0700 Subject: [PATCH 22/42] Some adjustments to begin working on a dark theme --- .../assets/styles/components/typography.css | 12 +++++ resources/assets/styles/main.css | 1 + resources/scripts/components/App.tsx | 9 ++-- .../components/ServerOverviewContainer.tsx | 7 +++ .../components/account/AccountOverview.tsx | 7 +++ .../components/account/DesignElements.tsx | 49 +++++++++++++++++++ resources/scripts/routers/AccountRouter.tsx | 38 ++++++++++++++ .../pterodactyl/templates/base/core.blade.php | 13 ++--- tailwind.js | 22 ++++----- 9 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 resources/assets/styles/components/typography.css create mode 100644 resources/scripts/components/ServerOverviewContainer.tsx create mode 100644 resources/scripts/components/account/AccountOverview.tsx create mode 100644 resources/scripts/components/account/DesignElements.tsx create mode 100644 resources/scripts/routers/AccountRouter.tsx diff --git a/resources/assets/styles/components/typography.css b/resources/assets/styles/components/typography.css new file mode 100644 index 000000000..679688f1a --- /dev/null +++ b/resources/assets/styles/components/typography.css @@ -0,0 +1,12 @@ +@import url('//fonts.googleapis.com/css?family=Rubik:300,400,500&display=swap'); +@import url('https://fonts.googleapis.com/css?family=IBM+Plex+Sans:500&display=swap'); + +h1, h2, h3, h4, h5, h6 { + @apply .font-medium; + font-family: 'IBM Plex Sans', -apple-system, '"Roboto"', 'system-ui', 'sans-serif'; +} + +p { + @apply .text-neutral-200; + letter-spacing: 0.015em; +} diff --git a/resources/assets/styles/main.css b/resources/assets/styles/main.css index 01a28e8dc..d77937e72 100644 --- a/resources/assets/styles/main.css +++ b/resources/assets/styles/main.css @@ -9,6 +9,7 @@ /** * Pterodactyl Specific CSS */ +@import "components/typography.css"; @import "components/animations.css"; @import "components/authentication.css"; @import "components/forms.css"; diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 9405bf524..c5b4d9f84 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -5,16 +5,19 @@ import AuthenticationRouter from '@/routers/AuthenticationRouter'; import { Provider } from 'react-redux'; import { persistor, store } from '@/redux/configure'; import { PersistGate } from 'redux-persist/integration/react'; +import AccountRouter from '@/routers/AccountRouter'; +import ServerOverviewContainer from '@/components/ServerOverviewContainer'; class App extends React.PureComponent { render () { return ( - -
    - + +
    + +
    diff --git a/resources/scripts/components/ServerOverviewContainer.tsx b/resources/scripts/components/ServerOverviewContainer.tsx new file mode 100644 index 000000000..20891bac9 --- /dev/null +++ b/resources/scripts/components/ServerOverviewContainer.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export default class ServerOverviewContainer extends React.PureComponent { + render () { + return

    Test

    ; + } +} diff --git a/resources/scripts/components/account/AccountOverview.tsx b/resources/scripts/components/account/AccountOverview.tsx new file mode 100644 index 000000000..3bff15eed --- /dev/null +++ b/resources/scripts/components/account/AccountOverview.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export default class AccountOverview extends React.PureComponent { + render () { + return null; + } +} diff --git a/resources/scripts/components/account/DesignElements.tsx b/resources/scripts/components/account/DesignElements.tsx new file mode 100644 index 000000000..66950b6da --- /dev/null +++ b/resources/scripts/components/account/DesignElements.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; + +export default class DesignElements extends React.PureComponent { + render () { + return ( +
    +
    +
    +

    A Special Announcement

    +
    +

    Your demands have been received: Dark Mode will be default in Pterodactyl 0.8!

    +
    +
    +
    +

    Form Elements

    +
    + + +

    + This is some descriptive helper text to explain how things work. +

    +
    + + +
    + + +
    +
    +
    +
    + ); + } +} diff --git a/resources/scripts/routers/AccountRouter.tsx b/resources/scripts/routers/AccountRouter.tsx new file mode 100644 index 000000000..46119c1ee --- /dev/null +++ b/resources/scripts/routers/AccountRouter.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { BrowserRouter, Route, Switch } from 'react-router-dom'; +import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import DesignElements from '@/components/account/DesignElements'; + +export default class AccountRouter extends React.PureComponent { + render () { + return ( + + ( + + +
    + + + + + +

    + © 2015 - 2019  + + Pterodactyl Software + +

    +
    +
    +
    + )} + /> +
    + ); + } +} diff --git a/resources/themes/pterodactyl/templates/base/core.blade.php b/resources/themes/pterodactyl/templates/base/core.blade.php index 6fdb68687..c726c4f9f 100644 --- a/resources/themes/pterodactyl/templates/base/core.blade.php +++ b/resources/themes/pterodactyl/templates/base/core.blade.php @@ -1,14 +1,7 @@ -@extends('templates/wrapper') +@extends('templates/wrapper', [ + 'css' => ['body' => 'bg-neutral-800'], +]) @section('container')
    @endsection - -@section('below-container') -
    -
    -

    - {!! trans('strings.copyright', ['year' => date('Y')]) !!} -

    -
    -@endsection diff --git a/tailwind.js b/tailwind.js index 499f220d5..1ca42c0cb 100644 --- a/tailwind.js +++ b/tailwind.js @@ -12,7 +12,6 @@ we've done our very best to explain each section. View the full documentation at https://tailwindcss.com. - |------------------------------------------------------------------------------- | The default config |------------------------------------------------------------------------------- @@ -60,7 +59,7 @@ let colors = { 'primary-600': 'hsl(214, 95%, 36%)', // dark 'primary-700': 'hsl(215, 96%, 32%)', 'primary-800': 'hsl(216, 98%, 25%)', // darker - 'primary-900': 'hsl(218, 100%, 17%)', //darkest + 'primary-900': 'hsl(218, 100%, 17%)', // darkest // Color used the most in the design and make up the majority of the UI. 'neutral-50': 'hsl(216, 33%, 97%)', @@ -163,10 +162,10 @@ module.exports = { 'lg': '992px', 'xl': '1200px', - 'xsx': {'max': '575px'}, - 'smx': {'max': '767px'}, - 'mdx': {'max': '991px'}, - 'lgx': {'max': '1999px'}, + 'xsx': { 'max': '575px' }, + 'smx': { 'max': '767px' }, + 'mdx': { 'max': '991px' }, + 'lgx': { 'max': '1999px' }, }, /* @@ -189,6 +188,7 @@ module.exports = { fonts: { 'sans': [ + 'Rubik', '-apple-system', 'BlinkMacSystemFont', '"Helvetica Neue"', @@ -209,7 +209,7 @@ module.exports = { 'Monaco', 'Consolas', 'monospace', - ] + ], }, /* @@ -394,7 +394,7 @@ module.exports = { | */ - borderColors: global.Object.assign({default: colors['neutral-400']}, colors), + borderColors: global.Object.assign({ default: colors['neutral-400'] }, colors), /* |----------------------------------------------------------------------------- @@ -469,7 +469,7 @@ module.exports = { '1/6': '16.66667%', '5/6': '83.33333%', 'full': '100%', - 'screen': '100vw' + 'screen': '100vw', }, /* @@ -504,7 +504,7 @@ module.exports = { '48': '12rem', '64': '16rem', 'full': '100%', - 'screen': '100vh' + 'screen': '100vh', }, /* @@ -543,7 +543,7 @@ module.exports = { minHeight: { '0': '0', 'full': '100%', - 'screen': '100vh' + 'screen': '100vh', }, /* From 328347fab7fe7bcc5f6750bf3ce1eef73dc6078b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 16:45:51 -0700 Subject: [PATCH 23/42] Convert all of the login components into hook based ones --- package.json | 11 +- resources/scripts/components/App.tsx | 33 ++- .../scripts/components/FlashMessageRender.tsx | 53 ++--- .../auth/ForgotPasswordContainer.tsx | 159 ++++++------- .../auth/LoginCheckpointContainer.tsx | 169 ++++++-------- .../components/auth/LoginContainer.tsx | 173 +++++++------- .../components/auth/LoginFormContainer.tsx | 2 +- .../auth/ResetPasswordContainer.tsx | 214 +++++++----------- resources/scripts/redux/actions/flash.ts | 17 -- resources/scripts/redux/configure.ts | 14 -- resources/scripts/redux/reducers.ts | 7 - resources/scripts/redux/reducers/flash.ts | 21 -- resources/scripts/redux/types.d.ts | 17 -- resources/scripts/routers/AccountRouter.tsx | 2 - .../scripts/routers/AuthenticationRouter.tsx | 2 - resources/scripts/state/index.ts | 16 ++ resources/scripts/state/types.d.ts | 19 ++ tsconfig.json | 1 + webpack.config.js | 2 - yarn.lock | 101 +++++---- 20 files changed, 435 insertions(+), 598 deletions(-) delete mode 100644 resources/scripts/redux/actions/flash.ts delete mode 100644 resources/scripts/redux/configure.ts delete mode 100644 resources/scripts/redux/reducers.ts delete mode 100644 resources/scripts/redux/reducers/flash.ts delete mode 100644 resources/scripts/redux/types.d.ts create mode 100644 resources/scripts/state/index.ts create mode 100644 resources/scripts/state/types.d.ts diff --git a/package.json b/package.json index 987d66ef3..bb3dd4aa0 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,11 @@ "name": "pterodactyl-panel", "dependencies": { "@hot-loader/react-dom": "^16.8.6", - "@types/react-redux": "^7.0.9", "axios": "^0.18.0", "brace": "^0.11.1", "classnames": "^2.2.6", "date-fns": "^1.29.0", + "easy-peasy": "^2.5.0", "feather-icons": "^4.10.0", "jquery": "^3.3.1", "lodash": "^4.17.11", @@ -14,12 +14,10 @@ "react": "^16.8.6", "react-dom": "^16.8.6", "react-hot-loader": "^4.9.0", - "react-redux": "^7.1.0", "react-router-dom": "^5.0.1", "react-transition-group": "^4.1.0", - "redux": "^4.0.1", - "redux-persist": "^5.10.0", "socket.io-client": "^2.2.0", + "use-react-router": "^1.0.7", "ws-wrapper": "^2.0.0", "xterm": "^3.5.1" }, @@ -36,12 +34,10 @@ "@types/react-dom": "^16.8.4", "@types/react-router-dom": "^4.3.3", "@types/react-transition-group": "^2.9.2", - "@types/redux-persist": "^4.3.1", "@types/webpack-env": "^1.13.6", "@typescript-eslint/eslint-plugin": "^1.10.1", "@typescript-eslint/parser": "^1.10.1", "babel-loader": "^8.0.5", - "clean-webpack-plugin": "^0.1.19", "css-loader": "^2.1.0", "cssnano": "^4.0.3", "eslint": "^5.16.0", @@ -74,10 +70,11 @@ "webpack-manifest-plugin": "^2.0.3" }, "scripts": { + "clean": "rm -rf public/assets/*.js && rm -rf public/assets/*.css", "watch": "NODE_ENV=development ./node_modules/.bin/webpack --watch --progress", "build": "NODE_ENV=development ./node_modules/.bin/webpack --progress", "build:production": "NODE_ENV=production ./node_modules/.bin/webpack", - "serve": "NODE_ENV=development webpack-dev-server --host 0.0.0.0 --hot", + "serve": "yarn run clean && NODE_ENV=development webpack-dev-server --host 0.0.0.0 --hot", "v:serve": "PUBLIC_PATH=http://pterodactyl.test:8080 yarn run serve" } } diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index c5b4d9f84..db8d6c3ef 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -2,32 +2,27 @@ import * as React from 'react'; import { hot } from 'react-hot-loader/root'; import { BrowserRouter as Router, Route } from 'react-router-dom'; import AuthenticationRouter from '@/routers/AuthenticationRouter'; -import { Provider } from 'react-redux'; -import { persistor, store } from '@/redux/configure'; -import { PersistGate } from 'redux-persist/integration/react'; import AccountRouter from '@/routers/AccountRouter'; import ServerOverviewContainer from '@/components/ServerOverviewContainer'; +import { StoreProvider } from 'easy-peasy'; +import { store } from '@/state'; class App extends React.PureComponent { - render () { - return ( - - - -
    - - - -
    -
    -
    -
    - ); + componentDidMount () { + } - renderLoading () { + render () { return ( -
    + + +
    + + + +
    +
    +
    ); } } diff --git a/resources/scripts/components/FlashMessageRender.tsx b/resources/scripts/components/FlashMessageRender.tsx index 3d2a5ce4d..ae9648784 100644 --- a/resources/scripts/components/FlashMessageRender.tsx +++ b/resources/scripts/components/FlashMessageRender.tsx @@ -1,38 +1,33 @@ -import * as React from 'react'; -import { FlashMessage, ReduxState } from '@/redux/types'; -import { connect } from 'react-redux'; +import React from 'react'; import MessageBox from '@/components/MessageBox'; +import { State, useStoreState } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; type Props = Readonly<{ spacerClass?: string; - flashes: FlashMessage[]; + withBottomSpace?: boolean; }>; -class FlashMessageRender extends React.PureComponent { - render () { - if (this.props.flashes.length === 0) { - return null; - } +export default ({ withBottomSpace, spacerClass }: Props) => { + const flashes = useStoreState((state: State) => state.flashes.items); - return ( - - { - this.props.flashes.map((flash, index) => ( - - {index > 0 &&
    } - - {flash.message} - -
    - )) - } -
    - ); + if (flashes.length === 0) { + return null; } -} -const mapStateToProps = (state: ReduxState) => ({ - flashes: state.flashes, -}); - -export default connect(mapStateToProps)(FlashMessageRender); + // noinspection PointlessBooleanExpressionJS + return ( +
    + { + flashes.map((flash, index) => ( + + {index > 0 &&
    } + + {flash.message} + +
    + )) + } +
    + ); +}; diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index 38741f7fc..5a8e1676f 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -1,109 +1,78 @@ import * as React from 'react'; import { Link } from 'react-router-dom'; import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail'; -import { connect } from 'react-redux'; -import { pushFlashMessage, clearAllFlashMessages } from '@/redux/actions/flash'; import { httpErrorToHuman } from '@/api/http'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; +import { Actions, useStoreActions } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; +import FlashMessageRender from '@/components/FlashMessageRender'; -type Props = Readonly<{ - pushFlashMessage: typeof pushFlashMessage; - clearAllFlashMessages: typeof clearAllFlashMessages; -}>; +export default () => { + const [ isSubmitting, setSubmitting ] = React.useState(false); + const [ email, setEmail ] = React.useState(''); -type State = Readonly<{ - email: string; - isSubmitting: boolean; -}>; + const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); -class ForgotPasswordContainer extends React.PureComponent { - emailField = React.createRef(); + const handleFieldUpdate = (e: React.ChangeEvent) => setEmail(e.target.value); - state: State = { - email: '', - isSubmitting: false, - }; - - handleFieldUpdate = (e: React.ChangeEvent) => this.setState({ - email: e.target.value, - }); - - handleSubmission = (e: React.FormEvent) => { + const handleSubmission = (e: React.FormEvent) => { e.preventDefault(); - this.setState({ isSubmitting: true }, () => { - this.props.clearAllFlashMessages(); - requestPasswordResetEmail(this.state.email) - .then(response => { - if (this.emailField.current) { - this.emailField.current.value = ''; - } - - this.props.pushFlashMessage({ - type: 'success', title: 'Success', message: response, - }); - }) - .catch(error => { - console.error(error); - this.props.pushFlashMessage({ - type: 'error', - title: 'Error', - message: httpErrorToHuman(error), - }); - }) - .then(() => this.setState({ isSubmitting: false })); - }); + setSubmitting(true); + clearFlashes(); + requestPasswordResetEmail(email) + .then(response => { + setEmail(''); + addFlash({ type: 'success', title: 'Success', message: response }); + }) + .catch(error => { + console.error(error); + addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); + }) + .then(() => setSubmitting(false)); }; - render () { - return ( -
    -

    - Request Password Reset -

    - - - -

    - Enter your account email address to receive instructions on resetting your password. -

    -
    - -
    -
    - - Return to Login - -
    -
    -
    - ); - } -} - -const mapDispatchToProps = { - pushFlashMessage, - clearAllFlashMessages, + return ( +
    +

    + Request Password Reset +

    + + + + +

    + Enter your account email address to receive instructions on resetting your password. +

    +
    + +
    +
    + + Return to Login + +
    +
    +
    + ); }; - -export default connect(null, mapDispatchToProps)(ForgotPasswordContainer); diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx index b4c3efb05..e3a0857fb 100644 --- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx +++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx @@ -1,112 +1,91 @@ -import * as React from 'react'; -import { RouteComponentProps, StaticContext } from 'react-router'; -import { connect } from 'react-redux'; -import { pushFlashMessage, clearAllFlashMessages } from '@/redux/actions/flash'; -import NetworkErrorMessage from '@/components/NetworkErrorMessage'; -import MessageBox from '@/components/MessageBox'; +import React, { useState } from 'react'; import { Link } from 'react-router-dom'; import loginCheckpoint from '@/api/auth/loginCheckpoint'; import { httpErrorToHuman } from '@/api/http'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; +import { Actions, useStoreActions } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; +import useRouter from 'use-react-router'; +import { StaticContext } from 'react-router'; +import FlashMessageRender from '@/components/FlashMessageRender'; -type State = Readonly<{ - isLoading: boolean; - errorMessage?: string; - code: string; -}>; +export default () => { + const [ code, setCode ] = useState(''); + const [ isLoading, setIsLoading ] = useState(false); -class LoginCheckpointContainer extends React.PureComponent, State> { - state: State = { - code: '', - isLoading: false, - }; + const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); + const { history, location: { state } } = useRouter<{}, StaticContext, { token?: string }>(); - componentDidMount () { - const { state } = this.props.location; - if (!state || !state.token) { - this.props.history.replace('/login'); - } + if (!state || !state.token) { + return history.replace('/login'); } - onChangeHandler = (e: React.ChangeEvent) => { - if (e.target.value.length > 6) { - e.target.value = e.target.value.substring(0, 6); - return e.preventDefault(); + const onChangeHandler = (e: React.ChangeEvent) => { + if (e.target.value.length <= 6) { + setCode(e.target.value); } - - this.setState({ code: e.target.value }); }; - submit = (e: React.FormEvent) => { + const submit = (e: React.FormEvent) => { e.preventDefault(); - this.setState({ isLoading: true }, () => { - loginCheckpoint(this.props.location.state.token, this.state.code) - .then(response => { - if (response.complete) { - // @ts-ignore - window.location = response.intended || '/'; - } - }) - .catch(error => { - console.error(error); - this.setState({ errorMessage: httpErrorToHuman(error), isLoading: false }); - }); - }); + setIsLoading(true); + clearFlashes(); + + loginCheckpoint(state.token!, code) + .then(response => { + if (response.complete) { + // @ts-ignore + window.location = response.intended || '/'; + } + }) + .catch(error => { + console.error(error); + addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); + setIsLoading(false); + }); }; - render () { - return ( - -

    - Device Checkpoint -

    - - - - This account is protected with two-factor authentication. A valid authentication token must - be provided in order to continue. - -
    - - -
    -
    - -
    -
    - - Return to Login - -
    -
    -
    - ); - } -} - -const mapDispatchToProps = { - pushFlashMessage, - clearAllFlashMessages, + return ( + +

    + Device Checkpoint +

    + + +
    + + +
    +
    + +
    +
    + + Return to Login + +
    +
    +
    + ); }; - -export default connect(null, mapDispatchToProps)(LoginCheckpointContainer); diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index e111a0305..b5d61d344 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -1,111 +1,96 @@ -import * as React from 'react'; -import { Link, RouteComponentProps } from 'react-router-dom'; +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; import login from '@/api/auth/login'; import { httpErrorToHuman } from '@/api/http'; -import NetworkErrorMessage from '@/components/NetworkErrorMessage'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import { Actions, useStoreActions } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; +import useRouter from 'use-react-router'; -type State = Readonly<{ - errorMessage?: string; - isLoading: boolean; - username?: string; - password?: string; -}>; +export default () => { + const [ username, setUsername ] = useState(''); + const [ password, setPassword ] = useState(''); + const [ isLoading, setLoading ] = useState(false); + const { history } = useRouter(); -export default class LoginContainer extends React.PureComponent { - state: State = { - isLoading: false, - }; + const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); - submit = (e: React.FormEvent) => { + const submit = (e: React.FormEvent) => { e.preventDefault(); - const { username, password } = this.state; + setLoading(true); + clearFlashes(); - this.setState({ isLoading: true }, () => { - login(username!, password!) - .then(response => { - if (response.complete) { - // @ts-ignore - window.location = response.intended || '/'; - return; - } + login(username!, password!) + .then(response => { + if (response.complete) { + // @ts-ignore + window.location = response.intended || '/'; + return; + } - this.props.history.replace('/login/checkpoint', { - token: response.confirmationToken, - }); - }) - .catch(error => this.setState({ - isLoading: false, - errorMessage: httpErrorToHuman(error), - }, () => console.error(error))); - }); + history.replace('/login/checkpoint', { token: response.confirmationToken }); + }) + .catch(error => { + console.error(error); + + setLoading(false); + addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); + }); }; - canSubmit () { - if (!this.state.username || !this.state.password) { - return false; - } + const canSubmit = () => username && password && username.length > 0 && password.length > 0; - return this.state.username.length > 0 && this.state.password.length > 0; - } - - // @ts-ignore - handleFieldUpdate = (e: React.ChangeEvent) => this.setState({ - [e.target.id]: e.target.value, - }); - - render () { - return ( - -

    - Login to Continue -

    - - - + return ( + +

    + Login to Continue +

    + + + + setUsername(e.target.value)} + disabled={isLoading} + /> +
    + setPassword(e.target.value)} + disabled={isLoading} /> -
    - - -
    -
    - -
    -
    - - Forgot password? - -
    - - - ); - } -} +
    +
    + +
    +
    + + Forgot password? + +
    +
    +
    + ); +}; diff --git a/resources/scripts/components/auth/LoginFormContainer.tsx b/resources/scripts/components/auth/LoginFormContainer.tsx index f6f0223c4..8f364e335 100644 --- a/resources/scripts/components/auth/LoginFormContainer.tsx +++ b/resources/scripts/components/auth/LoginFormContainer.tsx @@ -8,7 +8,7 @@ export default ({ className, ...props }: React.DetailedHTMLProps -
    +
    diff --git a/resources/scripts/components/auth/ResetPasswordContainer.tsx b/resources/scripts/components/auth/ResetPasswordContainer.tsx index dd4664b35..22734bb72 100644 --- a/resources/scripts/components/auth/ResetPasswordContainer.tsx +++ b/resources/scripts/components/auth/ResetPasswordContainer.tsx @@ -1,151 +1,109 @@ -import * as React from 'react'; +import React, { useState } from 'react'; import { RouteComponentProps } from 'react-router'; import { parse } from 'query-string'; import { Link } from 'react-router-dom'; -import NetworkErrorMessage from '@/components/NetworkErrorMessage'; import performPasswordReset from '@/api/auth/performPasswordReset'; import { httpErrorToHuman } from '@/api/http'; -import { connect } from 'react-redux'; -import { pushFlashMessage, clearAllFlashMessages } from '@/redux/actions/flash'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import { Actions, useStoreActions } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; -type State = Readonly<{ - email?: string; - password?: string; - passwordConfirm?: string; - isLoading: boolean; - errorMessage?: string; -}>; +type Props = Readonly & {}>; -type Props = Readonly & { - pushFlashMessage: typeof pushFlashMessage; - clearAllFlashMessages: typeof clearAllFlashMessages; -}>; +export default (props: Props) => { + const [ isLoading, setIsLoading ] = useState(false); + const [ email, setEmail ] = useState(''); + const [ password, setPassword ] = useState(''); + const [ passwordConfirm, setPasswordConfirm ] = useState(''); -class ResetPasswordContainer extends React.PureComponent { - state: State = { - isLoading: false, - }; + const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); - componentDidMount () { - const parsed = parse(this.props.location.search); - - this.setState({ email: parsed.email as string || undefined }); + const parsed = parse(props.location.search); + if (email.length === 0 && parsed.email) { + setEmail(parsed.email as string); } - canSubmit () { - if (!this.state.password || !this.state.email) { - return false; - } + const canSubmit = () => password && email && password.length >= 8 && password === passwordConfirm; - return this.state.password.length >= 8 && this.state.password === this.state.passwordConfirm; - } - - onPasswordChange = (e: React.ChangeEvent) => this.setState({ - password: e.target.value, - }); - - onPasswordConfirmChange = (e: React.ChangeEvent) => this.setState({ - passwordConfirm: e.target.value, - }); - - onSubmit = (e: React.FormEvent) => { + const submit = (e: React.FormEvent) => { e.preventDefault(); - const { password, passwordConfirm, email } = this.state; if (!password || !email || !passwordConfirm) { return; } - this.props.clearAllFlashMessages(); - this.setState({ isLoading: true }, () => { - performPasswordReset(email, { - token: this.props.match.params.token, - password: password, - passwordConfirmation: passwordConfirm, - }) - .then(response => { - if (response.redirectTo) { - // @ts-ignore - window.location = response.redirectTo; - return; - } + setIsLoading(true); + clearFlashes(); - this.props.pushFlashMessage({ - type: 'success', - message: 'Your password has been reset, please login to continue.', - }); - this.props.history.push('/login'); - }) - .catch(error => { - console.error(error); - this.setState({ errorMessage: httpErrorToHuman(error) }); - }) - .then(() => this.setState({ isLoading: false })); - }); + performPasswordReset(email, { + token: props.match.params.token, password, passwordConfirmation: passwordConfirm, + }) + .then(() => { + addFlash({ type: 'success', message: 'Your password has been reset, please login to continue.' }); + props.history.push('/login'); + }) + .catch(error => { + console.error(error); + addFlash({ type: 'error', title: 'Error', message: httpErrorToHuman(error) }); + }) + .then(() => setIsLoading(false)); }; - render () { - return ( -
    -

    - Reset Password -

    - - - - -
    - - -

    - Passwords must be at least 8 characters in length. -

    -
    -
    - - -
    -
    - -
    -
    - - Return to Login - -
    -
    -
    - ); - } -} - -const mapDispatchToProps = { - pushFlashMessage, - clearAllFlashMessages, + return ( +
    +

    + Reset Password +

    + + + + +
    + + setPassword(e.target.value)} + /> +

    + Passwords must be at least 8 characters in length. +

    +
    +
    + + setPasswordConfirm(e.target.value)} + /> +
    +
    + +
    +
    + + Return to Login + +
    +
    +
    + ); }; - -export default connect(null, mapDispatchToProps)(ResetPasswordContainer); diff --git a/resources/scripts/redux/actions/flash.ts b/resources/scripts/redux/actions/flash.ts deleted file mode 100644 index 56f860a12..000000000 --- a/resources/scripts/redux/actions/flash.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { FlashMessage } from '@/redux/types'; - -export const PUSH_FLASH_MESSAGE = 'PUSH_FLASH_MESSAGE'; -export const REMOVE_FLASH_MESSAGE = 'REMOVE_FLASH_MESSAGE'; -export const CLEAR_ALL_FLASH_MESSAGES = 'CLEAR_ALL_FLASH_MESSAGES'; - -export const pushFlashMessage = (payload: FlashMessage) => ({ - type: PUSH_FLASH_MESSAGE, payload, -}); - -export const removeFlashMessage = (id: string) => ({ - type: REMOVE_FLASH_MESSAGE, payload: id, -}); - -export const clearAllFlashMessages = () => ({ - type: CLEAR_ALL_FLASH_MESSAGES, -}); diff --git a/resources/scripts/redux/configure.ts b/resources/scripts/redux/configure.ts deleted file mode 100644 index 70f88a83e..000000000 --- a/resources/scripts/redux/configure.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createStore } from 'redux'; -import { persistStore, persistReducer, PersistConfig } from 'redux-persist'; -import storage from 'redux-persist/lib/storage'; -import { reducers } from './reducers'; - -const persistConfig: PersistConfig = { - key: 'root', - storage, -}; - -const persistedReducer = persistReducer(persistConfig, reducers); - -export const store = createStore(persistedReducer); -export const persistor = persistStore(store); diff --git a/resources/scripts/redux/reducers.ts b/resources/scripts/redux/reducers.ts deleted file mode 100644 index 6ae666b01..000000000 --- a/resources/scripts/redux/reducers.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { combineReducers } from 'redux'; -import flashReducer from './reducers/flash'; -import { ReduxState } from '@/redux/types'; - -export const reducers = combineReducers({ - flashes: flashReducer, -}); diff --git a/resources/scripts/redux/reducers/flash.ts b/resources/scripts/redux/reducers/flash.ts deleted file mode 100644 index 3dc9a7d83..000000000 --- a/resources/scripts/redux/reducers/flash.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { FlashMessage, ReduxReducerAction } from '@/redux/types'; -import { CLEAR_ALL_FLASH_MESSAGES, PUSH_FLASH_MESSAGE, REMOVE_FLASH_MESSAGE } from '@/redux/actions/flash'; - -export default (state: FlashMessage[] = [], action: ReduxReducerAction) => { - switch (action.type) { - case PUSH_FLASH_MESSAGE: - return [ ...state.filter(flash => { - if (action.payload.id && flash.id) { - return flash.id !== action.payload.id; - } - - return true; - }), action.payload ]; - case REMOVE_FLASH_MESSAGE: - return [ ...state.filter(flash => flash.id !== action.payload) ]; - case CLEAR_ALL_FLASH_MESSAGES: - return []; - default: - return [ ...state ]; - } -}; diff --git a/resources/scripts/redux/types.d.ts b/resources/scripts/redux/types.d.ts deleted file mode 100644 index 707c6da8c..000000000 --- a/resources/scripts/redux/types.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { FlashMessageType } from '@/components/MessageBox'; - -export interface ReduxReducerAction { - type: string; - payload?: any; -} - -export interface FlashMessage { - id?: string; - type: FlashMessageType; - title?: string; - message: string; -} - -export interface ReduxState { - flashes: FlashMessage[]; -} diff --git a/resources/scripts/routers/AccountRouter.tsx b/resources/scripts/routers/AccountRouter.tsx index 46119c1ee..d70a6b343 100644 --- a/resources/scripts/routers/AccountRouter.tsx +++ b/resources/scripts/routers/AccountRouter.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; -import FlashMessageRender from '@/components/FlashMessageRender'; import DesignElements from '@/components/account/DesignElements'; export default class AccountRouter extends React.PureComponent { @@ -13,7 +12,6 @@ export default class AccountRouter extends React.PureComponent {
    - diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx index 673278c7e..8d5ed492f 100644 --- a/resources/scripts/routers/AuthenticationRouter.tsx +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -3,7 +3,6 @@ import { BrowserRouter, Route, Switch } from 'react-router-dom'; import LoginContainer from '@/components/auth/LoginContainer'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer'; -import FlashMessageRender from '@/components/FlashMessageRender'; import ResetPasswordContainer from '@/components/auth/ResetPasswordContainer'; import LoginCheckpointContainer from '@/components/auth/LoginCheckpointContainer'; @@ -16,7 +15,6 @@ export default class AuthenticationRouter extends React.PureComponent {
    - diff --git a/resources/scripts/state/index.ts b/resources/scripts/state/index.ts new file mode 100644 index 000000000..e1b4470f5 --- /dev/null +++ b/resources/scripts/state/index.ts @@ -0,0 +1,16 @@ +import { action, createStore } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; + +const state: ApplicationState = { + flashes: { + items: [], + addFlash: action((state, payload) => { + state.items.push(payload); + }), + clearFlashes: action(state => { + state.items = []; + }), + }, +}; + +export const store = createStore(state); diff --git a/resources/scripts/state/types.d.ts b/resources/scripts/state/types.d.ts new file mode 100644 index 000000000..742b68320 --- /dev/null +++ b/resources/scripts/state/types.d.ts @@ -0,0 +1,19 @@ +import { FlashMessageType } from '@/components/MessageBox'; +import { Action } from 'easy-peasy'; + +export interface ApplicationState { + flashes: FlashState; +} + +export interface FlashState { + items: FlashMessage[]; + addFlash: Action; + clearFlashes: Action; +} + +export interface FlashMessage { + id?: string; + type: FlashMessageType; + title?: string; + message: string; +} diff --git a/tsconfig.json b/tsconfig.json index 454ab76ff..27f5324d6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "strict": true, "noImplicitReturns": true, "moduleResolution": "node", + "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": ".", "lib": ["es2015", "dom"], diff --git a/webpack.config.js b/webpack.config.js index b78f76126..95138b14d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,6 @@ const tailwind = require('tailwindcss'); const glob = require('glob-all'); const AssetsManifestPlugin = require('webpack-assets-manifest'); -const CleanPlugin = require('clean-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const PurgeCssPlugin = require('purgecss-webpack-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); @@ -13,7 +12,6 @@ const TerserPlugin = require('terser-webpack-plugin'); const isProduction = process.env.NODE_ENV === 'production'; let plugins = [ - new CleanPlugin(path.resolve(__dirname, 'public/assets')), new MiniCssExtractPlugin({ filename: isProduction ? 'bundle.[chunkhash:8].css' : 'bundle.[hash:8].css' }), new AssetsManifestPlugin({ writeToDisk: true, diff --git a/yarn.lock b/yarn.lock index 56197c658..4d7afe1ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -605,7 +605,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" dependencies: @@ -751,13 +751,6 @@ version "4.7.2" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220" -"@types/hoist-non-react-statics@^3.3.0": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" - dependencies: - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - "@types/lodash@^4.14.119": version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" @@ -778,15 +771,6 @@ dependencies: "@types/react" "*" -"@types/react-redux@^7.0.9": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.0.9.tgz#4825ee2872c44768916304b6bb8df5b46d443b88" - dependencies: - "@types/hoist-non-react-statics" "^3.3.0" - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - redux "^4.0.0" - "@types/react-router-dom@^4.3.3": version "4.3.3" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.3.tgz#7837e3e9fefbc84a8f6c8a51dca004f4e83e94e3" @@ -815,12 +799,6 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/redux-persist@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/redux-persist/-/redux-persist-4.3.1.tgz#aa4c876859e0bea5155e5f7980e5b8c4699dc2e6" - dependencies: - redux-persist "*" - "@types/webpack-env@^1.13.6": version "1.13.6" resolved "http://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976" @@ -1908,12 +1886,6 @@ clean-css@4.1.x: dependencies: source-map "0.5.x" -clean-webpack-plugin@^0.1.19: - version "0.1.19" - resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz#ceda8bb96b00fe168e9b080272960d20fdcadd6d" - dependencies: - rimraf "^2.6.1" - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2671,6 +2643,18 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +easy-peasy@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/easy-peasy/-/easy-peasy-2.5.0.tgz#5366ff59c71c980cc31ec230dc8855f72b8d2875" + dependencies: + immer "^3.1.3" + memoizerific "^1.11.3" + redux "^4.0.1" + redux-thunk "^2.3.0" + shallowequal "^1.1.0" + type-zoo "^3.3.0" + typelevel-ts "^0.3.5" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -3773,6 +3757,10 @@ ignore@^5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.2.tgz#e28e584d43ad7e92f96995019cc43b9e1ac49558" +immer@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/immer/-/immer-3.1.3.tgz#59bc742b2aab6e2c676445edb653e588a23c70fc" + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -3884,7 +3872,7 @@ interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -invariant@^2.2.0, invariant@^2.2.2, invariant@^2.2.4: +invariant@^2.2.0, invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -4535,6 +4523,10 @@ map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" +map-or-similar@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -4570,6 +4562,12 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^2.0.0" +memoizerific@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" + dependencies: + map-or-similar "^1.5.0" + memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -5978,7 +5976,7 @@ promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" -prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.6.1, prop-types@^15.6.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" dependencies: @@ -6148,7 +6146,7 @@ react-hot-loader@^4.9.0: shallowequal "^1.0.2" source-map "^0.7.3" -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" @@ -6156,17 +6154,6 @@ react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" -react-redux@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.0.tgz#72af7cf490a74acdc516ea9c1dd80e25af9ea0b2" - dependencies: - "@babel/runtime" "^7.4.5" - hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - loose-envify "^1.4.0" - prop-types "^15.7.2" - react-is "^16.8.6" - react-router-dom@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" @@ -6291,11 +6278,11 @@ reduce-css-calc@^2.0.0: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" -redux-persist@*, redux-persist@^5.10.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-5.10.0.tgz#5d8d802c5571e55924efc1c3a9b23575283be62b" +redux-thunk@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" -redux@^4.0.0, redux@^4.0.1: +redux@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" dependencies: @@ -6734,7 +6721,7 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -shallowequal@^1.0.2: +shallowequal@^1.0.2, shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" @@ -7378,10 +7365,18 @@ type-is@~1.6.16: media-typer "0.3.0" mime-types "~2.1.18" +type-zoo@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/type-zoo/-/type-zoo-3.3.0.tgz#d5b59393b09a48ac30380c50e2369e828817e9b3" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +typelevel-ts@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/typelevel-ts/-/typelevel-ts-0.3.5.tgz#9f242c193fa6c5489d2342c642ab8912a065af9d" + typescript@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" @@ -7537,6 +7532,16 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +use-force-update@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/use-force-update/-/use-force-update-1.0.5.tgz#9b7192f73f6cb90d592b225858c1562719d7c184" + +use-react-router@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/use-react-router/-/use-react-router-1.0.7.tgz#04216066d87e45040309f24d2fd5e9f28308b3e2" + dependencies: + use-force-update "^1.0.5" + use@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" From e20a76818200286fdd001539062e24eae2b0e7bb Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 17:07:28 -0700 Subject: [PATCH 24/42] Plop user data into the store when loading up the base component --- resources/scripts/components/App.tsx | 58 ++++++++++++++++------- resources/scripts/state/index.ts | 15 ++---- resources/scripts/state/models/flashes.ts | 14 ++++++ resources/scripts/state/models/user.ts | 11 +++++ resources/scripts/state/types.d.ts | 17 +++++++ 5 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 resources/scripts/state/models/flashes.ts create mode 100644 resources/scripts/state/models/user.ts diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index db8d6c3ef..e870787dd 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -6,25 +6,47 @@ import AccountRouter from '@/routers/AccountRouter'; import ServerOverviewContainer from '@/components/ServerOverviewContainer'; import { StoreProvider } from 'easy-peasy'; import { store } from '@/state'; +import { UserData } from '@/state/types'; -class App extends React.PureComponent { - componentDidMount () { - - } - - render () { - return ( - - -
    - - - -
    -
    -
    - ); - } +interface WindowWithUser extends Window { + PterodactylUser?: { + uuid: string; + username: string; + email: string; + root_admin: boolean; + use_totp: boolean; + language: string; + updated_at: string; + created_at: string; + }; } +const App = () => { + const data = (window as WindowWithUser).PterodactylUser; + if (data) { + store.getActions().user.setUserData({ + uuid: data.uuid, + username: data.username, + email: data.email, + language: data.language, + rootAdmin: data.root_admin, + useTotp: data.use_totp, + createdAt: new Date(data.created_at), + updatedAt: new Date(data.updated_at), + }); + } + + return ( + + +
    + + + +
    +
    +
    + ); +}; + export default hot(App); diff --git a/resources/scripts/state/index.ts b/resources/scripts/state/index.ts index e1b4470f5..5df635796 100644 --- a/resources/scripts/state/index.ts +++ b/resources/scripts/state/index.ts @@ -1,16 +1,11 @@ -import { action, createStore } from 'easy-peasy'; +import { createStore } from 'easy-peasy'; import { ApplicationState } from '@/state/types'; +import flashes from '@/state/models/flashes'; +import user from '@/state/models/user'; const state: ApplicationState = { - flashes: { - items: [], - addFlash: action((state, payload) => { - state.items.push(payload); - }), - clearFlashes: action(state => { - state.items = []; - }), - }, + flashes, + user, }; export const store = createStore(state); diff --git a/resources/scripts/state/models/flashes.ts b/resources/scripts/state/models/flashes.ts new file mode 100644 index 000000000..5c9a7f0a3 --- /dev/null +++ b/resources/scripts/state/models/flashes.ts @@ -0,0 +1,14 @@ +import { action } from 'easy-peasy'; +import { FlashState } from '@/state/types'; + +const flashes: FlashState = { + items: [], + addFlash: action((state, payload) => { + state.items.push(payload); + }), + clearFlashes: action(state => { + state.items = []; + }), +}; + +export default flashes; diff --git a/resources/scripts/state/models/user.ts b/resources/scripts/state/models/user.ts new file mode 100644 index 000000000..3997d009f --- /dev/null +++ b/resources/scripts/state/models/user.ts @@ -0,0 +1,11 @@ +import { UserState } from '@/state/types'; +import { action } from 'easy-peasy'; + +const user: UserState = { + data: undefined, + setUserData: action((state, payload) => { + state.data = payload; + }), +}; + +export default user; diff --git a/resources/scripts/state/types.d.ts b/resources/scripts/state/types.d.ts index 742b68320..7c700a692 100644 --- a/resources/scripts/state/types.d.ts +++ b/resources/scripts/state/types.d.ts @@ -3,6 +3,7 @@ import { Action } from 'easy-peasy'; export interface ApplicationState { flashes: FlashState; + user: UserState; } export interface FlashState { @@ -11,6 +12,22 @@ export interface FlashState { clearFlashes: Action; } +export interface UserState { + data?: UserData; + setUserData: Action; +} + +export interface UserData { + uuid: string; + username: string; + email: string; + language: string; + rootAdmin: boolean; + useTotp: boolean; + createdAt: Date; + updatedAt: Date; +} + export interface FlashMessage { id?: string; type: FlashMessageType; From d22747b0b1c4151d9f7d846c2ab5ba424b833871 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 17:07:54 -0700 Subject: [PATCH 25/42] Only do this if no user is in the state already --- resources/scripts/components/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index e870787dd..2512d368d 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -23,7 +23,7 @@ interface WindowWithUser extends Window { const App = () => { const data = (window as WindowWithUser).PterodactylUser; - if (data) { + if (data && !store.getState().user.data) { store.getActions().user.setUserData({ uuid: data.uuid, username: data.username, From f34593e77725aee90d6963d838cec5b97e5d9a91 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 17:45:32 -0700 Subject: [PATCH 26/42] Make the transition based router be grouped more cleanly. --- resources/scripts/TransitionRouter.tsx | 36 +++++++++++++ .../auth/LoginCheckpointContainer.tsx | 10 ++-- .../components/auth/LoginContainer.tsx | 6 +-- resources/scripts/routers/AccountRouter.tsx | 41 +++------------ .../scripts/routers/AuthenticationRouter.tsx | 51 +++++-------------- 5 files changed, 65 insertions(+), 79 deletions(-) create mode 100644 resources/scripts/TransitionRouter.tsx diff --git a/resources/scripts/TransitionRouter.tsx b/resources/scripts/TransitionRouter.tsx new file mode 100644 index 000000000..dc0ec7a95 --- /dev/null +++ b/resources/scripts/TransitionRouter.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { Route, Switch } from 'react-router'; +import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import { BrowserRouter } from 'react-router-dom'; + +type Props = Readonly<{ + basename: string; + children: React.ReactNode; +}>; + +export default ({ basename, children }: Props) => ( + + ( + + +
    + + {children} + +

    + © 2015 - 2019  + + Pterodactyl Software + +

    +
    +
    +
    + )} + /> +
    +); diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx index e3a0857fb..b3c508076 100644 --- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx +++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx @@ -1,23 +1,23 @@ import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; +import { Link, RouteComponentProps } from 'react-router-dom'; import loginCheckpoint from '@/api/auth/loginCheckpoint'; import { httpErrorToHuman } from '@/api/http'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationState } from '@/state/types'; -import useRouter from 'use-react-router'; import { StaticContext } from 'react-router'; import FlashMessageRender from '@/components/FlashMessageRender'; -export default () => { +export default ({ history, location: { state } }: RouteComponentProps<{}, StaticContext, { token?: string }>) => { const [ code, setCode ] = useState(''); const [ isLoading, setIsLoading ] = useState(false); const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); - const { history, location: { state } } = useRouter<{}, StaticContext, { token?: string }>(); if (!state || !state.token) { - return history.replace('/login'); + history.replace('/login'); + + return null; } const onChangeHandler = (e: React.ChangeEvent) => { diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index b5d61d344..7a33b7dcc 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -1,18 +1,16 @@ import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; +import { Link, RouteComponentProps } from 'react-router-dom'; import login from '@/api/auth/login'; import { httpErrorToHuman } from '@/api/http'; import LoginFormContainer from '@/components/auth/LoginFormContainer'; import FlashMessageRender from '@/components/FlashMessageRender'; import { Actions, useStoreActions } from 'easy-peasy'; import { ApplicationState } from '@/state/types'; -import useRouter from 'use-react-router'; -export default () => { +export default ({ history }: RouteComponentProps) => { const [ username, setUsername ] = useState(''); const [ password, setPassword ] = useState(''); const [ isLoading, setLoading ] = useState(false); - const { history } = useRouter(); const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); diff --git a/resources/scripts/routers/AccountRouter.tsx b/resources/scripts/routers/AccountRouter.tsx index d70a6b343..7c5166285 100644 --- a/resources/scripts/routers/AccountRouter.tsx +++ b/resources/scripts/routers/AccountRouter.tsx @@ -1,36 +1,11 @@ import * as React from 'react'; -import { BrowserRouter, Route, Switch } from 'react-router-dom'; -import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import { Route } from 'react-router-dom'; import DesignElements from '@/components/account/DesignElements'; +import TransitionRouter from '@/TransitionRouter'; -export default class AccountRouter extends React.PureComponent { - render () { - return ( - - ( - - -
    - - - - -

    - © 2015 - 2019  - - Pterodactyl Software - -

    -
    -
    -
    - )} - /> -
    - ); - } -} +export default () => ( + + + + +); diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx index 8d5ed492f..5a36a16e6 100644 --- a/resources/scripts/routers/AuthenticationRouter.tsx +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -1,42 +1,19 @@ -import * as React from 'react'; -import { BrowserRouter, Route, Switch } from 'react-router-dom'; +import React from 'react'; +import { Route } from 'react-router-dom'; import LoginContainer from '@/components/auth/LoginContainer'; -import { CSSTransition, TransitionGroup } from 'react-transition-group'; import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer'; import ResetPasswordContainer from '@/components/auth/ResetPasswordContainer'; +import TransitionRouter from '@/TransitionRouter'; import LoginCheckpointContainer from '@/components/auth/LoginCheckpointContainer'; -export default class AuthenticationRouter extends React.PureComponent { - render () { - return ( - - ( - - -
    - - - - - - - -

    - © 2015 - 2019  - - Pterodactyl Software - -

    -
    -
    -
    - )} - /> -
    - ); - } -} +export default () => ( + +
    + + + + + +
    +
    +); From 60f32f055ecf8a900cd3e373f855fb01aea897ef Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 18:09:42 -0700 Subject: [PATCH 27/42] Fix router to make it easier to actually navigate around the app --- resources/scripts/components/App.tsx | 14 +++++++------ .../components/ServerOverviewContainer.tsx | 8 ++++++- .../components/account/DesignElements.tsx | 6 +++++- .../auth/ForgotPasswordContainer.tsx | 2 +- .../auth/LoginCheckpointContainer.tsx | 4 ++-- .../components/auth/LoginContainer.tsx | 4 ++-- .../auth/ResetPasswordContainer.tsx | 4 ++-- resources/scripts/routers/AccountRouter.tsx | 13 ++++++------ .../scripts/routers/AuthenticationRouter.tsx | 21 ++++++++----------- 9 files changed, 42 insertions(+), 34 deletions(-) diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 2512d368d..f65255843 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -6,7 +6,7 @@ import AccountRouter from '@/routers/AccountRouter'; import ServerOverviewContainer from '@/components/ServerOverviewContainer'; import { StoreProvider } from 'easy-peasy'; import { store } from '@/state'; -import { UserData } from '@/state/types'; +import TransitionRouter from '@/TransitionRouter'; interface WindowWithUser extends Window { PterodactylUser?: { @@ -39,11 +39,13 @@ const App = () => { return ( -
    - - - -
    + +
    + + + +
    +
    ); diff --git a/resources/scripts/components/ServerOverviewContainer.tsx b/resources/scripts/components/ServerOverviewContainer.tsx index 20891bac9..8cc5cf7cb 100644 --- a/resources/scripts/components/ServerOverviewContainer.tsx +++ b/resources/scripts/components/ServerOverviewContainer.tsx @@ -1,7 +1,13 @@ import * as React from 'react'; +import { NavLink } from 'react-router-dom'; export default class ServerOverviewContainer extends React.PureComponent { render () { - return

    Test

    ; + return ( +
    + Account + Design +
    + ); } } diff --git a/resources/scripts/components/account/DesignElements.tsx b/resources/scripts/components/account/DesignElements.tsx index 66950b6da..319001987 100644 --- a/resources/scripts/components/account/DesignElements.tsx +++ b/resources/scripts/components/account/DesignElements.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { Link } from 'react-router-dom'; export default class DesignElements extends React.PureComponent { render () { @@ -8,7 +9,10 @@ export default class DesignElements extends React.PureComponent {

    A Special Announcement

    -

    Your demands have been received: Dark Mode will be default in Pterodactyl 0.8!

    +

    + Your demands have been received: Dark Mode will be default in Pterodactyl 0.8! +

    +

    Back

    diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx index 5a8e1676f..fbf0eb704 100644 --- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx +++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx @@ -66,7 +66,7 @@ export default () => {
    Return to Login diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx index b3c508076..ccc53e5bc 100644 --- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx +++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx @@ -15,7 +15,7 @@ export default ({ history, location: { state } }: RouteComponentProps<{}, Static const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); if (!state || !state.token) { - history.replace('/login'); + history.replace('/auth/login'); return null; } @@ -79,7 +79,7 @@ export default ({ history, location: { state } }: RouteComponentProps<{}, Static
    Return to Login diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx index 7a33b7dcc..f29a8c599 100644 --- a/resources/scripts/components/auth/LoginContainer.tsx +++ b/resources/scripts/components/auth/LoginContainer.tsx @@ -28,7 +28,7 @@ export default ({ history }: RouteComponentProps) => { return; } - history.replace('/login/checkpoint', { token: response.confirmationToken }); + history.replace('/auth/login/checkpoint', { token: response.confirmationToken }); }) .catch(error => { console.error(error); @@ -82,7 +82,7 @@ export default ({ history }: RouteComponentProps) => {
    Forgot password? diff --git a/resources/scripts/components/auth/ResetPasswordContainer.tsx b/resources/scripts/components/auth/ResetPasswordContainer.tsx index 22734bb72..9467bb92b 100644 --- a/resources/scripts/components/auth/ResetPasswordContainer.tsx +++ b/resources/scripts/components/auth/ResetPasswordContainer.tsx @@ -41,7 +41,7 @@ export default (props: Props) => { }) .then(() => { addFlash({ type: 'success', message: 'Your password has been reset, please login to continue.' }); - props.history.push('/login'); + props.history.push('/auth/login'); }) .catch(error => { console.error(error); @@ -97,7 +97,7 @@ export default (props: Props) => {
    Return to Login diff --git a/resources/scripts/routers/AccountRouter.tsx b/resources/scripts/routers/AccountRouter.tsx index 7c5166285..47f07c387 100644 --- a/resources/scripts/routers/AccountRouter.tsx +++ b/resources/scripts/routers/AccountRouter.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, RouteComponentProps } from 'react-router-dom'; import DesignElements from '@/components/account/DesignElements'; -import TransitionRouter from '@/TransitionRouter'; -export default () => ( - - - - +export default ({ match }: RouteComponentProps) => ( +
    + + +
    ); diff --git a/resources/scripts/routers/AuthenticationRouter.tsx b/resources/scripts/routers/AuthenticationRouter.tsx index 5a36a16e6..4054f55d5 100644 --- a/resources/scripts/routers/AuthenticationRouter.tsx +++ b/resources/scripts/routers/AuthenticationRouter.tsx @@ -1,19 +1,16 @@ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, RouteComponentProps } from 'react-router-dom'; import LoginContainer from '@/components/auth/LoginContainer'; import ForgotPasswordContainer from '@/components/auth/ForgotPasswordContainer'; import ResetPasswordContainer from '@/components/auth/ResetPasswordContainer'; -import TransitionRouter from '@/TransitionRouter'; import LoginCheckpointContainer from '@/components/auth/LoginCheckpointContainer'; -export default () => ( - -
    - - - - - -
    -
    +export default ({ match }: RouteComponentProps) => ( +
    + + + + + +
    ); From 0789b814dd5cd8b80c563971ca7dbabb1cc598b2 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 18:10:57 -0700 Subject: [PATCH 28/42] Fix design issue --- resources/scripts/components/App.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index f65255843..3db5c0886 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -39,13 +39,13 @@ const App = () => { return ( - -
    +
    + -
    - + +
    ); From adcd2682ef64747035ef3feacc1c40d5c533bd6e Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 18:53:50 -0700 Subject: [PATCH 29/42] Start working on some defined styles, begin implementing password update in account --- resources/assets/styles/components/forms.css | 97 +++++++++++++------ .../components/account/AccountOverview.tsx | 7 -- .../account/AccountOverviewContainer.tsx | 19 ++++ .../components/account/DesignElements.tsx | 53 ---------- .../account/DesignElementsContainer.tsx | 69 +++++++++++++ .../account/forms/UpdatePasswordForm.tsx | 47 +++++++++ .../components/elements/ContentBox.tsx | 18 ++++ resources/scripts/routers/AccountRouter.tsx | 7 +- 8 files changed, 226 insertions(+), 91 deletions(-) delete mode 100644 resources/scripts/components/account/AccountOverview.tsx create mode 100644 resources/scripts/components/account/AccountOverviewContainer.tsx delete mode 100644 resources/scripts/components/account/DesignElements.tsx create mode 100644 resources/scripts/components/account/DesignElementsContainer.tsx create mode 100644 resources/scripts/components/account/forms/UpdatePasswordForm.tsx create mode 100644 resources/scripts/components/elements/ContentBox.tsx diff --git a/resources/assets/styles/components/forms.css b/resources/assets/styles/components/forms.css index 93fca8d36..d734fca9d 100644 --- a/resources/assets/styles/components/forms.css +++ b/resources/assets/styles/components/forms.css @@ -15,19 +15,23 @@ input[type=number] { /** * Styling for other forms throughout the Panel. */ -.input { - @apply .appearance-none .p-3 .rounded .border .border-neutral-200 .text-neutral-800 .w-full; +.input, .input-dark { + @apply .appearance-none .w-full; min-width: 0; + + &:required, &:invalid { + box-shadow: none; + } +} + +.input { + @apply .p-3 .rounded .border .border-neutral-200 .text-neutral-800; transition: border 150ms linear; &:focus { @apply .border-primary-400; } - &:required, &:invalid { - box-shadow: none; - } - &.error { @apply .text-red-600 .border-red-500; } @@ -37,6 +41,39 @@ input[type=number] { @apply .bg-neutral-100 .border-neutral-200; } +.input + .input-help { + @apply .text-xs .text-neutral-400 .pt-2; + + &.error { + @apply .text-red-600; + } +} + +.input-dark { + @apply .p-3 .bg-neutral-600 .border .border-neutral-500 .text-sm .rounded .text-neutral-200; + transition: border 150ms linear, box-shaodw 150ms ease-in; + + &:focus { + @apply .shadow; + } + + & + .input-help { + @apply .text-xs .text-neutral-400 .mt-2 + } + + &.error { + @apply .text-red-100 .border-red-400; + } + + &.error + .input-help { + @apply .text-red-400; + } + + &:disabled { + @apply .opacity-75; + } +} + label { @apply .block .text-xs .font-medium .uppercase .text-neutral-700 .mb-2; } @@ -61,6 +98,10 @@ select:not(.appearance-none) { background-position-x: calc(100% - 0.75rem); } +.input-dark-label { + @apply .uppercase .text-neutral-200; +} + .input-label { @apply .block .uppercase .tracking-wide .text-neutral-800 .text-xs .font-bold; @@ -69,60 +110,60 @@ select:not(.appearance-none) { } } -.input-help { - @apply .text-xs .text-neutral-400 .pt-2; - - &.error { - @apply .text-red-600; - } -} - a.btn { @apply .no-underline; } .btn { - @apply .rounded .p-2; + @apply .rounded .p-2 .uppercase .tracking-wide .text-sm; transition: all 150ms linear; /** * Button Colors */ &.btn-primary { - @apply .bg-primary-500 .border-primary-600 .border .text-white; + @apply .bg-primary-500 .border-primary-600 .border .text-primary-50; &:hover:not(:disabled) { - @apply .bg-primary-600 .border-primary-800; + @apply .bg-primary-600 .border-primary-700; } } &.btn-green { - @apply .bg-green-500 .border-green-600 .border .text-white; + @apply .bg-green-500 .border-green-600 .border .text-green-50; &:hover:not(:disabled) { - @apply .bg-green-600 .border-green-800; + @apply .bg-green-600 .border-green-700; } } &.btn-red { &:not(.btn-secondary) { - @apply .bg-red-500 .border-red-600 .border .text-white; + @apply .bg-red-500 .border-red-600 .text-red-50; } &:hover:not(:disabled) { - @apply .bg-red-600 .border-red-800; + @apply .bg-red-600 .border-red-700; + } + } + + &.btn-grey { + @apply .border .border-neutral-600 .bg-neutral-500 .text-neutral-50; + + &:hover:not(:disabled) { + @apply .bg-neutral-600 .border-neutral-700; } } &.btn-secondary { - @apply .border .border-neutral-200 .text-neutral-400; + @apply .border .border-neutral-600 .bg-transparent .text-neutral-200; &:hover:not(:disabled) { - @apply .border-neutral-500 .text-neutral-700; + @apply .border-neutral-500 .text-neutral-100; } &.btn-red:hover:not(:disabled) { - @apply .bg-red-600 .border-red-800 .text-white; + @apply .bg-red-500 .border-red-600 .text-red-50; } } @@ -130,19 +171,19 @@ a.btn { * Button Sizes */ &.btn-jumbo { - @apply .p-4 .w-full .uppercase .tracking-wide .text-sm; + @apply .p-4 .w-full; } &.btn-lg { - @apply .p-4 .uppercase .tracking-wide .text-sm; + @apply .p-4 .text-sm; } &.btn-sm { - @apply .px-6 .py-3 .uppercase .tracking-wide .text-sm; + @apply .p-3; } &.btn-xs { - @apply .py-2 .px-2 .uppercase .text-xs; + @apply .p-2 .text-xs; } &:disabled, &.disabled { diff --git a/resources/scripts/components/account/AccountOverview.tsx b/resources/scripts/components/account/AccountOverview.tsx deleted file mode 100644 index 3bff15eed..000000000 --- a/resources/scripts/components/account/AccountOverview.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import * as React from 'react'; - -export default class AccountOverview extends React.PureComponent { - render () { - return null; - } -} diff --git a/resources/scripts/components/account/AccountOverviewContainer.tsx b/resources/scripts/components/account/AccountOverviewContainer.tsx new file mode 100644 index 000000000..158d30ed8 --- /dev/null +++ b/resources/scripts/components/account/AccountOverviewContainer.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import ContentBox from '@/components/elements/ContentBox'; +import UpdatePasswordForm from '@/components/account/forms/UpdatePasswordForm'; + +export default () => { + return ( +
    + + + +
    + + + + +
    +
    + ); +}; diff --git a/resources/scripts/components/account/DesignElements.tsx b/resources/scripts/components/account/DesignElements.tsx deleted file mode 100644 index 319001987..000000000 --- a/resources/scripts/components/account/DesignElements.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from 'react'; -import { Link } from 'react-router-dom'; - -export default class DesignElements extends React.PureComponent { - render () { - return ( -
    -
    -
    -

    A Special Announcement

    -
    -

    - Your demands have been received: Dark Mode will be default in Pterodactyl 0.8! -

    -

    Back

    -
    -
    -
    -

    Form Elements

    -
    - - -

    - This is some descriptive helper text to explain how things work. -

    -
    - - -
    - - -
    -
    -
    -
    - ); - } -} diff --git a/resources/scripts/components/account/DesignElementsContainer.tsx b/resources/scripts/components/account/DesignElementsContainer.tsx new file mode 100644 index 000000000..ffa9c0ac7 --- /dev/null +++ b/resources/scripts/components/account/DesignElementsContainer.tsx @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { Link } from 'react-router-dom'; +import ContentBox from '@/components/elements/ContentBox'; + +export default class DesignElementsContainer extends React.PureComponent { + render () { + return ( +
    +
    + +

    + Your demands have been received: Dark Mode will be default in Pterodactyl 0.8! +

    +

    Back

    +
    +
    +

    Form Elements

    +
    + + +

    + This is some descriptive helper text to explain how things work. +

    +
    + + +

    + This field has an error. +

    +
    + + +
    + + +
    + + + + +
    + + +
    + + +
    +
    +
    +
    + ); + } +} diff --git a/resources/scripts/components/account/forms/UpdatePasswordForm.tsx b/resources/scripts/components/account/forms/UpdatePasswordForm.tsx new file mode 100644 index 000000000..1e1a720c7 --- /dev/null +++ b/resources/scripts/components/account/forms/UpdatePasswordForm.tsx @@ -0,0 +1,47 @@ +import React, { useState } from 'react'; +import { State, useStoreState } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; + +export default () => { + const [ isLoading, setIsLoading ] = useState(false); + const user = useStoreState((state: State) => state.user.data); + + if (!user) { + return null; + } + + return ( +
    + + +
    + + +

    + Your new password must be at least 8 characters in length. +

    +
    +
    + + +
    +
    + +
    +
    + ); +}; diff --git a/resources/scripts/components/elements/ContentBox.tsx b/resources/scripts/components/elements/ContentBox.tsx new file mode 100644 index 000000000..2862789b8 --- /dev/null +++ b/resources/scripts/components/elements/ContentBox.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import classNames from 'classnames'; + +type Props = Readonly, HTMLDivElement> & { + title?: string; + borderColor?: string; +}>; + +export default ({ title, borderColor, children, ...props }: Props) => ( +
    + {title &&

    {title}

    } +
    + {children} +
    +
    +); diff --git a/resources/scripts/routers/AccountRouter.tsx b/resources/scripts/routers/AccountRouter.tsx index 47f07c387..d6ecd04c3 100644 --- a/resources/scripts/routers/AccountRouter.tsx +++ b/resources/scripts/routers/AccountRouter.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; import { Route, RouteComponentProps } from 'react-router-dom'; -import DesignElements from '@/components/account/DesignElements'; +import DesignElementsContainer from '@/components/account/DesignElementsContainer'; +import AccountOverviewContainer from '@/components/account/AccountOverviewContainer'; export default ({ match }: RouteComponentProps) => (
    - - + +
    ); From 403a1e79faa3602795508de8411ef3482d9f3b2d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 18:58:07 -0700 Subject: [PATCH 30/42] Fix form hover/active states --- resources/assets/styles/components/forms.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/resources/assets/styles/components/forms.css b/resources/assets/styles/components/forms.css index d734fca9d..dab0fde2a 100644 --- a/resources/assets/styles/components/forms.css +++ b/resources/assets/styles/components/forms.css @@ -50,11 +50,15 @@ input[type=number] { } .input-dark { - @apply .p-3 .bg-neutral-600 .border .border-neutral-500 .text-sm .rounded .text-neutral-200; + @apply .p-3 .bg-neutral-600 .border .border-neutral-500 .text-sm .rounded .text-neutral-200 .shadow-none; transition: border 150ms linear, box-shaodw 150ms ease-in; &:focus { - @apply .shadow; + @apply .shadow-md .border-neutral-400; + } + + &:hover { + @apply .border-neutral-400; } & + .input-help { From d43b7ea5bc17fedc0f3819637c091e12736f1642 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 23:25:38 -0700 Subject: [PATCH 31/42] Add basic Formik setup and example for update password --- package.json | 5 +- .../api/account/updateAccountPassword.ts | 21 +++ .../account/forms/UpdatePasswordForm.tsx | 100 +++++++++----- .../components/elements/ContentBox.tsx | 4 +- .../scripts/components/elements/Field.tsx | 41 ++++++ .../components/elements/SpinnerOverlay.tsx | 14 ++ yarn.lock | 130 +++++++++++++++++- 7 files changed, 275 insertions(+), 40 deletions(-) create mode 100644 resources/scripts/api/account/updateAccountPassword.ts create mode 100644 resources/scripts/components/elements/Field.tsx create mode 100644 resources/scripts/components/elements/SpinnerOverlay.tsx diff --git a/package.json b/package.json index bb3dd4aa0..d4fec3c16 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "date-fns": "^1.29.0", "easy-peasy": "^2.5.0", "feather-icons": "^4.10.0", + "formik": "^1.5.7", "jquery": "^3.3.1", "lodash": "^4.17.11", "query-string": "^6.7.0", @@ -19,7 +20,8 @@ "socket.io-client": "^2.2.0", "use-react-router": "^1.0.7", "ws-wrapper": "^2.0.0", - "xterm": "^3.5.1" + "xterm": "^3.5.1", + "yup": "^0.27.0" }, "devDependencies": { "@babel/core": "^7.2.2", @@ -35,6 +37,7 @@ "@types/react-router-dom": "^4.3.3", "@types/react-transition-group": "^2.9.2", "@types/webpack-env": "^1.13.6", + "@types/yup": "^0.26.17", "@typescript-eslint/eslint-plugin": "^1.10.1", "@typescript-eslint/parser": "^1.10.1", "babel-loader": "^8.0.5", diff --git a/resources/scripts/api/account/updateAccountPassword.ts b/resources/scripts/api/account/updateAccountPassword.ts new file mode 100644 index 000000000..b2c3f10ba --- /dev/null +++ b/resources/scripts/api/account/updateAccountPassword.ts @@ -0,0 +1,21 @@ +import http from '@/api/http'; + +interface Data { + current: string; + password: string; + confirmPassword: string; +} + +export default ({ current, password, confirmPassword }: Data): Promise => { + return new Promise((resolve, reject) => { + http.put('/account/password', { + // eslint-disable-next-line @typescript-eslint/camelcase + current_password: current, + password: password, + // eslint-disable-next-line @typescript-eslint/camelcase + password_confirmation: confirmPassword, + }) + .then(() => resolve()) + .catch(reject); + }); +}; diff --git a/resources/scripts/components/account/forms/UpdatePasswordForm.tsx b/resources/scripts/components/account/forms/UpdatePasswordForm.tsx index 1e1a720c7..d64faf4bf 100644 --- a/resources/scripts/components/account/forms/UpdatePasswordForm.tsx +++ b/resources/scripts/components/account/forms/UpdatePasswordForm.tsx @@ -1,47 +1,81 @@ import React, { useState } from 'react'; import { State, useStoreState } from 'easy-peasy'; import { ApplicationState } from '@/state/types'; +import { Form, Formik, FormikActions } from 'formik'; +import Field from '@/components/elements/Field'; +import * as Yup from 'yup'; +import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; + +interface Values { + current: string; + password: string; + confirmPassword: string; +} + +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; + }), +}); export default () => { - const [ isLoading, setIsLoading ] = useState(false); const user = useStoreState((state: State) => state.user.data); if (!user) { return null; } + const submit = (values: Values, { setSubmitting }: FormikActions) => { + setTimeout(() => setSubmitting(false), 1500); + }; + return ( -
    - - -
    - - -

    - Your new password must be at least 8 characters in length. -

    -
    -
    - - -
    -
    - -
    -
    + + + { + ({ isSubmitting, isValid }) => ( + + +
    + +
    + +
    +
    + +
    +
    + +
    + +
    + ) + } +
    +
    ); }; diff --git a/resources/scripts/components/elements/ContentBox.tsx b/resources/scripts/components/elements/ContentBox.tsx index 2862789b8..109d848a9 100644 --- a/resources/scripts/components/elements/ContentBox.tsx +++ b/resources/scripts/components/elements/ContentBox.tsx @@ -8,8 +8,8 @@ type Props = Readonly (
    - {title &&

    {title}

    } -
    {title}} +
    {children} diff --git a/resources/scripts/components/elements/Field.tsx b/resources/scripts/components/elements/Field.tsx new file mode 100644 index 000000000..f1820343e --- /dev/null +++ b/resources/scripts/components/elements/Field.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Field, FieldProps } from 'formik'; +import classNames from 'classnames'; + +interface Props { + id?: string; + type: string; + name: string; + label?: string; + description?: string; + validate?: (value: any) => undefined | string | Promise; +} + +export default ({ id, type, name, label, description, validate }: Props) => ( + + { + ({ field, form: { errors, touched } }: FieldProps) => ( + + {label && + + } + + {touched[field.name] && errors[field.name] ? +

    + {(errors[field.name] as string).charAt(0).toUpperCase() + (errors[field.name] as string).slice(1)} +

    + : + description ?

    {description}

    : null + } +
    + ) + } +
    +); diff --git a/resources/scripts/components/elements/SpinnerOverlay.tsx b/resources/scripts/components/elements/SpinnerOverlay.tsx new file mode 100644 index 000000000..fa7cb1ea4 --- /dev/null +++ b/resources/scripts/components/elements/SpinnerOverlay.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import classNames from 'classnames'; +import { CSSTransition } from 'react-transition-group'; + +export default ({ large, visible }: { visible: boolean; large?: boolean }) => ( + +
    +
    +
    +
    +); diff --git a/yarn.lock b/yarn.lock index 4d7afe1ad..2541c4cda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -605,7 +605,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" dependencies: @@ -803,6 +803,10 @@ version "1.13.6" resolved "http://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976" +"@types/yup@^0.26.17": + version "0.26.17" + resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.17.tgz#5cb7cfc211d8e985b21d88289542591c92cad9dc" + "@typescript-eslint/eslint-plugin@^1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.10.1.tgz#0a9e41f375d082363e63169049cd03ef0b6dd85e" @@ -1291,6 +1295,10 @@ arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -2113,6 +2121,10 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + core-js@^2.4.0: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" @@ -2170,6 +2182,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-context@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3" + dependencies: + fbjs "^0.8.0" + gud "^1.0.0" + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -2461,6 +2480,10 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" + default-gateway@^2.6.0: version "2.7.2" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f" @@ -2695,6 +2718,12 @@ encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" @@ -3173,6 +3202,18 @@ faye-websocket@~0.11.1: dependencies: websocket-driver ">=0.5.1" +fbjs@^0.8.0: + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + feather-icons@^4.10.0: version "4.10.0" resolved "https://registry.yarnpkg.com/feather-icons/-/feather-icons-4.10.0.tgz#5c2ba00ecf6f0529e99ab445d054fa6bd0b9c8d6" @@ -3268,6 +3309,10 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" +fn-name@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" + follow-redirects@^1.0.0: version "1.6.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.1.tgz#514973c44b5757368bad8bddfe52f81f015c94cb" @@ -3299,6 +3344,20 @@ fork-ts-checker-webpack-plugin@^0.5.2: minimatch "^3.0.4" tapable "^1.0.0" +formik@^1.5.7: + version "1.5.7" + resolved "https://registry.yarnpkg.com/formik/-/formik-1.5.7.tgz#2fc5fc2f0c693cdc4e8c9dad3a10eb4c4e131ff5" + dependencies: + create-react-context "^0.2.2" + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.11" + lodash-es "^4.17.11" + prop-types "^15.6.1" + react-fast-compare "^2.0.1" + tiny-warning "^1.0.2" + tslib "^1.9.3" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -3719,7 +3778,7 @@ iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.4.24: +iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" dependencies: @@ -4083,7 +4142,7 @@ is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" -is-stream@^1.1.0: +is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4137,6 +4196,13 @@ isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + jquery@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" @@ -4339,6 +4405,10 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +lodash-es@^4.17.11: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0" + lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -4829,6 +4899,13 @@ node-emoji@^1.8.1: dependencies: lodash.toarray "^4.4.0" +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + node-forge@0.7.5: version "0.7.5" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" @@ -5976,6 +6053,12 @@ promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + prop-types@^15.6.1, prop-types@^15.6.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -5984,6 +6067,10 @@ prop-types@^15.6.1, prop-types@^15.6.2: object-assign "^4.1.1" react-is "^16.8.1" +property-expr@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f" + proxy-addr@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" @@ -6132,6 +6219,10 @@ react-dom@^16.8.6: prop-types "^15.6.2" scheduler "^0.13.6" +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + react-hot-loader@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.9.0.tgz#498e5f76aae28bfd420efdabddf5af156745b4dd" @@ -6706,7 +6797,7 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4: +setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -7142,6 +7233,10 @@ symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" +synchronous-promise@^2.0.6: + version "2.0.9" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.9.tgz#b83db98e9e7ae826bf9c8261fd8ac859126c780a" + table@^5.2.3: version "5.4.0" resolved "https://registry.yarnpkg.com/table/-/table-5.4.0.tgz#d772a3216e68829920a41a32c18eda286c95d780" @@ -7320,6 +7415,10 @@ toposort@^1.0.0: version "1.0.7" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" @@ -7342,6 +7441,10 @@ tslib@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e" +tslib@^1.9.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + tsutils@^3.7.0: version "3.14.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.14.0.tgz#bf8d5a7bae5369331fa0f2b0a5a10bd7f7396c77" @@ -7381,6 +7484,10 @@ typescript@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" +ua-parser-js@^0.7.18: + version "0.7.20" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098" + uglify-es@^3.3.4: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" @@ -7800,6 +7907,10 @@ websocket-extensions@>=0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" +whatwg-fetch@>=0.10.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -7993,3 +8104,14 @@ yargs@~1.2.6: yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + +yup@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.27.0.tgz#f8cb198c8e7dd2124beddc2457571329096b06e7" + dependencies: + "@babel/runtime" "^7.0.0" + fn-name "~2.0.1" + lodash "^4.17.11" + property-expr "^1.5.0" + synchronous-promise "^2.0.6" + toposort "^2.0.2" From 438f1b06b996995a2c37bfff479079484e4240e7 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 22 Jun 2019 23:45:09 -0700 Subject: [PATCH 32/42] Add support for changing account password --- .../api/account/updateAccountPassword.ts | 2 +- .../scripts/components/FlashMessageRender.tsx | 12 +++++++--- .../account/AccountOverviewContainer.tsx | 2 +- .../account/forms/UpdatePasswordForm.tsx | 23 +++++++++++++++---- .../components/elements/ContentBox.tsx | 5 +++- resources/scripts/state/models/flashes.ts | 4 ++-- resources/scripts/state/types.d.ts | 3 ++- 7 files changed, 38 insertions(+), 13 deletions(-) diff --git a/resources/scripts/api/account/updateAccountPassword.ts b/resources/scripts/api/account/updateAccountPassword.ts index b2c3f10ba..c29aefd2d 100644 --- a/resources/scripts/api/account/updateAccountPassword.ts +++ b/resources/scripts/api/account/updateAccountPassword.ts @@ -8,7 +8,7 @@ interface Data { export default ({ current, password, confirmPassword }: Data): Promise => { return new Promise((resolve, reject) => { - http.put('/account/password', { + http.put('/api/client/account/password', { // eslint-disable-next-line @typescript-eslint/camelcase current_password: current, password: password, diff --git a/resources/scripts/components/FlashMessageRender.tsx b/resources/scripts/components/FlashMessageRender.tsx index ae9648784..5b9c51d75 100644 --- a/resources/scripts/components/FlashMessageRender.tsx +++ b/resources/scripts/components/FlashMessageRender.tsx @@ -4,14 +4,20 @@ import { State, useStoreState } from 'easy-peasy'; import { ApplicationState } from '@/state/types'; type Props = Readonly<{ + byKey?: string; spacerClass?: string; withBottomSpace?: boolean; }>; -export default ({ withBottomSpace, spacerClass }: Props) => { +export default ({ withBottomSpace, spacerClass, byKey }: Props) => { const flashes = useStoreState((state: State) => state.flashes.items); - if (flashes.length === 0) { + let filtered = flashes; + if (byKey) { + filtered = flashes.filter(flash => flash.key === byKey); + } + + if (filtered.length === 0) { return null; } @@ -19,7 +25,7 @@ export default ({ withBottomSpace, spacerClass }: Props) => { return (
    { - flashes.map((flash, index) => ( + filtered.map((flash, index) => ( {index > 0 &&
    } diff --git a/resources/scripts/components/account/AccountOverviewContainer.tsx b/resources/scripts/components/account/AccountOverviewContainer.tsx index 158d30ed8..61174a548 100644 --- a/resources/scripts/components/account/AccountOverviewContainer.tsx +++ b/resources/scripts/components/account/AccountOverviewContainer.tsx @@ -5,7 +5,7 @@ import UpdatePasswordForm from '@/components/account/forms/UpdatePasswordForm'; export default () => { return (
    - +
    diff --git a/resources/scripts/components/account/forms/UpdatePasswordForm.tsx b/resources/scripts/components/account/forms/UpdatePasswordForm.tsx index d64faf4bf..dce13e650 100644 --- a/resources/scripts/components/account/forms/UpdatePasswordForm.tsx +++ b/resources/scripts/components/account/forms/UpdatePasswordForm.tsx @@ -1,10 +1,12 @@ -import React, { useState } from 'react'; -import { State, useStoreState } from 'easy-peasy'; +import React from 'react'; +import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy'; import { ApplicationState } from '@/state/types'; import { Form, Formik, FormikActions } from 'formik'; import Field from '@/components/elements/Field'; import * as Yup from 'yup'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; +import updateAccountPassword from '@/api/account/updateAccountPassword'; +import { httpErrorToHuman } from '@/api/http'; interface Values { current: string; @@ -22,13 +24,26 @@ const schema = Yup.object().shape({ export default () => { const user = useStoreState((state: State) => state.user.data); + const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); if (!user) { return null; } - const submit = (values: Values, { setSubmitting }: FormikActions) => { - setTimeout(() => setSubmitting(false), 1500); + const submit = (values: Values, { resetForm, setSubmitting }: FormikActions) => { + clearFlashes('account:password'); + updateAccountPassword({ ...values }) + .then(() => { + resetForm(); + addFlash({ key: 'account:password', type: 'success', message: 'Your password has been updated.' }); + }) + .catch(error => addFlash({ + key: 'account:password', + type: 'error', + title: 'Error', + message: httpErrorToHuman(error), + })) + .then(() => setSubmitting(false)); }; return ( diff --git a/resources/scripts/components/elements/ContentBox.tsx b/resources/scripts/components/elements/ContentBox.tsx index 109d848a9..8fc74ea61 100644 --- a/resources/scripts/components/elements/ContentBox.tsx +++ b/resources/scripts/components/elements/ContentBox.tsx @@ -1,14 +1,17 @@ import * as React from 'react'; import classNames from 'classnames'; +import FlashMessageRender from '@/components/FlashMessageRender'; type Props = Readonly, HTMLDivElement> & { title?: string; borderColor?: string; + showFlashes?: string | boolean; }>; -export default ({ title, borderColor, children, ...props }: Props) => ( +export default ({ title, borderColor, showFlashes, children, ...props }: Props) => (
    {title &&

    {title}

    } + {showFlashes && }
    diff --git a/resources/scripts/state/models/flashes.ts b/resources/scripts/state/models/flashes.ts index 5c9a7f0a3..97c9f080b 100644 --- a/resources/scripts/state/models/flashes.ts +++ b/resources/scripts/state/models/flashes.ts @@ -6,8 +6,8 @@ const flashes: FlashState = { addFlash: action((state, payload) => { state.items.push(payload); }), - clearFlashes: action(state => { - state.items = []; + clearFlashes: action((state, payload) => { + state.items = payload ? state.items.filter(flashes => flashes.key !== payload) : []; }), }; diff --git a/resources/scripts/state/types.d.ts b/resources/scripts/state/types.d.ts index 7c700a692..644a4679f 100644 --- a/resources/scripts/state/types.d.ts +++ b/resources/scripts/state/types.d.ts @@ -9,7 +9,7 @@ export interface ApplicationState { export interface FlashState { items: FlashMessage[]; addFlash: Action; - clearFlashes: Action; + clearFlashes: Action; } export interface UserState { @@ -30,6 +30,7 @@ export interface UserData { export interface FlashMessage { id?: string; + key?: string; type: FlashMessageType; title?: string; message: string; From da24f665635fc0a2236e9b769c5620f45de7946d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 24 Jun 2019 21:43:46 -0700 Subject: [PATCH 33/42] Finish code for updating email --- .../assets/styles/components/typography.css | 7 +- .../scripts/api/account/updateAccountEmail.ts | 9 ++ .../account/AccountOverviewContainer.tsx | 4 +- .../account/forms/UpdateEmailAddressForm.tsx | 83 +++++++++++++++++++ resources/scripts/state/models/user.ts | 34 +++++++- resources/scripts/state/types.d.ts | 17 +--- 6 files changed, 134 insertions(+), 20 deletions(-) create mode 100644 resources/scripts/api/account/updateAccountEmail.ts create mode 100644 resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx diff --git a/resources/assets/styles/components/typography.css b/resources/assets/styles/components/typography.css index 679688f1a..301eabb2a 100644 --- a/resources/assets/styles/components/typography.css +++ b/resources/assets/styles/components/typography.css @@ -1,12 +1,17 @@ @import url('//fonts.googleapis.com/css?family=Rubik:300,400,500&display=swap'); @import url('https://fonts.googleapis.com/css?family=IBM+Plex+Sans:500&display=swap'); +body { + @apply .text-neutral-200; + letter-spacing: 0.015em; +} + h1, h2, h3, h4, h5, h6 { @apply .font-medium; + letter-spacing: 0; font-family: 'IBM Plex Sans', -apple-system, '"Roboto"', 'system-ui', 'sans-serif'; } p { @apply .text-neutral-200; - letter-spacing: 0.015em; } diff --git a/resources/scripts/api/account/updateAccountEmail.ts b/resources/scripts/api/account/updateAccountEmail.ts new file mode 100644 index 000000000..5ff230265 --- /dev/null +++ b/resources/scripts/api/account/updateAccountEmail.ts @@ -0,0 +1,9 @@ +import http from '@/api/http'; + +export default (email: string, password: string): Promise => { + return new Promise((resolve, reject) => { + http.put('/api/client/account/email', { email, password }) + .then(() => resolve()) + .catch(reject); + }); +}; diff --git a/resources/scripts/components/account/AccountOverviewContainer.tsx b/resources/scripts/components/account/AccountOverviewContainer.tsx index 61174a548..fcc208ac5 100644 --- a/resources/scripts/components/account/AccountOverviewContainer.tsx +++ b/resources/scripts/components/account/AccountOverviewContainer.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import ContentBox from '@/components/elements/ContentBox'; import UpdatePasswordForm from '@/components/account/forms/UpdatePasswordForm'; +import UpdateEmailAddressForm from '@/components/account/forms/UpdateEmailAddressForm'; export default () => { return ( @@ -9,7 +10,8 @@ export default () => {
    - + + diff --git a/resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx b/resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx new file mode 100644 index 000000000..4593908a5 --- /dev/null +++ b/resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy'; +import { ApplicationState } from '@/state/types'; +import { Form, Formik, FormikActions } from 'formik'; +import * as Yup from 'yup'; +import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; +import Field from '@/components/elements/Field'; +import { httpErrorToHuman } from '@/api/http'; + +interface Values { + email: string; + password: string; +} + +const schema = Yup.object().shape({ + email: Yup.string().email().required(), + password: Yup.string().required('You must provide your current account password.'), +}); + +export default () => { + const user = useStoreState((state: State) => state.user.data); + const updateEmail = useStoreActions((state: Actions) => state.user.updateUserEmail); + + const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); + + const submit = (values: Values, { resetForm, setSubmitting }: FormikActions) => { + 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(() => { + resetForm(); + setSubmitting(false); + }); + }; + + return ( + + { + ({ isSubmitting, isValid }) => ( + + +
    + +
    + +
    +
    + +
    + +
    + ) + } +
    + ); +}; diff --git a/resources/scripts/state/models/user.ts b/resources/scripts/state/models/user.ts index 3997d009f..c7da19fbc 100644 --- a/resources/scripts/state/models/user.ts +++ b/resources/scripts/state/models/user.ts @@ -1,11 +1,41 @@ -import { UserState } from '@/state/types'; -import { action } from 'easy-peasy'; +import { Action, action, Thunk, thunk } from 'easy-peasy'; +import updateAccountEmail from '@/api/account/updateAccountEmail'; + +export interface UserData { + uuid: string; + username: string; + email: string; + language: string; + rootAdmin: boolean; + useTotp: boolean; + createdAt: Date; + updatedAt: Date; +} + +export interface UserState { + data?: UserData; + setUserData: Action; + updateUserData: Action>; + updateUserEmail: Thunk>; +} const user: UserState = { data: undefined, setUserData: action((state, payload) => { state.data = payload; }), + + updateUserData: action((state, payload) => { + // Limitation of Typescript, can't do much about that currently unfortunately. + // @ts-ignore + state.data = { ...state.data, ...payload }; + }), + + updateUserEmail: thunk(async (actions, payload) => { + await updateAccountEmail(payload.email, payload.password); + + actions.updateUserData({ email: payload.email }); + }), }; export default user; diff --git a/resources/scripts/state/types.d.ts b/resources/scripts/state/types.d.ts index 644a4679f..9774f6f73 100644 --- a/resources/scripts/state/types.d.ts +++ b/resources/scripts/state/types.d.ts @@ -1,5 +1,6 @@ import { FlashMessageType } from '@/components/MessageBox'; import { Action } from 'easy-peasy'; +import { UserState } from '@/state/models/user'; export interface ApplicationState { flashes: FlashState; @@ -12,22 +13,6 @@ export interface FlashState { clearFlashes: Action; } -export interface UserState { - data?: UserData; - setUserData: Action; -} - -export interface UserData { - uuid: string; - username: string; - email: string; - language: string; - rootAdmin: boolean; - useTotp: boolean; - createdAt: Date; - updatedAt: Date; -} - export interface FlashMessage { id?: string; key?: string; From cbd050edda43a1bdf7c7eea679cbb97c7e72a718 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 24 Jun 2019 21:45:43 -0700 Subject: [PATCH 34/42] Only doing password and email currently --- .../components/account/AccountOverviewContainer.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/resources/scripts/components/account/AccountOverviewContainer.tsx b/resources/scripts/components/account/AccountOverviewContainer.tsx index fcc208ac5..d88134f2d 100644 --- a/resources/scripts/components/account/AccountOverviewContainer.tsx +++ b/resources/scripts/components/account/AccountOverviewContainer.tsx @@ -9,13 +9,9 @@ export default () => { -
    - - - - - -
    + + +
    ); }; From 46c338dc1bee14a6f3a04423b71377d3db5fb307 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 24 Jun 2019 22:17:58 -0700 Subject: [PATCH 35/42] Basic design concept for a nav bar --- resources/scripts/TransitionRouter.tsx | 20 +-- resources/scripts/components/App.tsx | 2 +- .../account/DesignElementsContainer.tsx | 116 +++++++++--------- resources/scripts/routers/AccountRouter.tsx | 44 ++++++- tailwind.js | 8 +- 5 files changed, 121 insertions(+), 69 deletions(-) diff --git a/resources/scripts/TransitionRouter.tsx b/resources/scripts/TransitionRouter.tsx index dc0ec7a95..cf738e17a 100644 --- a/resources/scripts/TransitionRouter.tsx +++ b/resources/scripts/TransitionRouter.tsx @@ -18,15 +18,17 @@ export default ({ basename, children }: Props) => ( {children} -

    - © 2015 - 2019  - - Pterodactyl Software - -

    +
    +

    + © 2015 - 2019  + + Pterodactyl Software + +

    +
    diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 3db5c0886..eab411e95 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -39,7 +39,7 @@ const App = () => { return ( -
    +
    diff --git a/resources/scripts/components/account/DesignElementsContainer.tsx b/resources/scripts/components/account/DesignElementsContainer.tsx index ffa9c0ac7..fc4ad61f8 100644 --- a/resources/scripts/components/account/DesignElementsContainer.tsx +++ b/resources/scripts/components/account/DesignElementsContainer.tsx @@ -5,65 +5,71 @@ import ContentBox from '@/components/elements/ContentBox'; export default class DesignElementsContainer extends React.PureComponent { render () { return ( -
    -
    - -

    - Your demands have been received: Dark Mode will be default in Pterodactyl 0.8! -

    -

    Back

    -
    -
    -

    Form Elements

    -
    - - -

    - This is some descriptive helper text to explain how things work. + +

    +
    + +

    + Your demands have been received: Dark Mode will be default in Pterodactyl 0.8!

    -
    - - -

    - This field has an error. -

    -
    - - -
    - - -
    - - - - -
    - - -
    - - +

    Back

    + +
    +

    Form Elements

    +
    + + +

    + This is some descriptive helper text to explain how things work. +

    +
    + + +

    + This field has an error. +

    +
    + + +
    + + +
    + + + + +
    + + +
    + + +
    -
    + ); } } diff --git a/resources/scripts/routers/AccountRouter.tsx b/resources/scripts/routers/AccountRouter.tsx index d6ecd04c3..7631d566f 100644 --- a/resources/scripts/routers/AccountRouter.tsx +++ b/resources/scripts/routers/AccountRouter.tsx @@ -1,11 +1,49 @@ import * as React from 'react'; -import { Route, RouteComponentProps } from 'react-router-dom'; +import { Link, NavLink, Route, RouteComponentProps } from 'react-router-dom'; import DesignElementsContainer from '@/components/account/DesignElementsContainer'; import AccountOverviewContainer from '@/components/account/AccountOverviewContainer'; export default ({ match }: RouteComponentProps) => (
    - - +
    +
    +
    + + Pterodactyl + +
    +
    + + Dashboard + + + Account + +
    +
    +
    +
    + + +
    ); diff --git a/tailwind.js b/tailwind.js index 1ca42c0cb..ec7f6c291 100644 --- a/tailwind.js +++ b/tailwind.js @@ -44,7 +44,7 @@ View the full documentation at https://tailwindcss.com. let colors = { 'transparent': 'transparent', - 'black': '#1F2933', + 'black': 'hsl(210, 27%, 10%)', 'white': '#ffffff', 'basically-white': '#fafafb', @@ -196,6 +196,12 @@ module.exports = { 'system-ui', 'sans-serif', ], + 'header': [ + '"IBM Plex Sans"', + '"Roboto"', + 'system-ui', + 'sans-serif', + ], 'serif': [ 'Constantia', '"Lucida Bright"', From c7355975ade73a1d82ac5bbdd971a882df9f2f1a Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 25 Jun 2019 20:42:18 -0700 Subject: [PATCH 36/42] Support running behind SSL --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d4fec3c16..a3103cad6 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,6 @@ "build": "NODE_ENV=development ./node_modules/.bin/webpack --progress", "build:production": "NODE_ENV=production ./node_modules/.bin/webpack", "serve": "yarn run clean && NODE_ENV=development webpack-dev-server --host 0.0.0.0 --hot", - "v:serve": "PUBLIC_PATH=http://pterodactyl.test:8080 yarn run serve" + "v:serve": "PUBLIC_PATH=https://pterodactyl.test:8080 yarn run serve --https --key /etc/ssl/private/pterodactyl.test-key.pem --cert /etc/ssl/private/pterodactyl.test.pem" } } From 9cb8020dbe0eb239f9fe1ea762d97cabbc2d994b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 25 Jun 2019 21:28:56 -0700 Subject: [PATCH 37/42] Add basic navigation bar --- package.json | 3 + .../assets/styles/components/navigation.css | 133 ++++-------------- resources/scripts/components/App.tsx | 5 +- .../scripts/components/NavigationBar.tsx | 35 +++++ .../AccountOverviewContainer.tsx | 4 +- .../dashboard/DashboardContainer.tsx | 7 + .../DesignElementsContainer.tsx | 0 .../forms/UpdateEmailAddressForm.tsx | 0 .../forms/UpdatePasswordForm.tsx | 0 resources/scripts/routers/AccountRouter.tsx | 49 ------- resources/scripts/routers/DashboardRouter.tsx | 17 +++ yarn.lock | 29 +++- 12 files changed, 121 insertions(+), 161 deletions(-) create mode 100644 resources/scripts/components/NavigationBar.tsx rename resources/scripts/components/{account => dashboard}/AccountOverviewContainer.tsx (76%) create mode 100644 resources/scripts/components/dashboard/DashboardContainer.tsx rename resources/scripts/components/{account => dashboard}/DesignElementsContainer.tsx (100%) rename resources/scripts/components/{account => dashboard}/forms/UpdateEmailAddressForm.tsx (100%) rename resources/scripts/components/{account => dashboard}/forms/UpdatePasswordForm.tsx (100%) delete mode 100644 resources/scripts/routers/AccountRouter.tsx create mode 100644 resources/scripts/routers/DashboardRouter.tsx diff --git a/package.json b/package.json index a3103cad6..75e5b87b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "pterodactyl-panel", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.19", + "@fortawesome/free-solid-svg-icons": "^5.9.0", + "@fortawesome/react-fontawesome": "^0.1.4", "@hot-loader/react-dom": "^16.8.6", "axios": "^0.18.0", "brace": "^0.11.1", diff --git a/resources/assets/styles/components/navigation.css b/resources/assets/styles/components/navigation.css index 5b9f07097..58579dda3 100644 --- a/resources/assets/styles/components/navigation.css +++ b/resources/assets/styles/components/navigation.css @@ -1,117 +1,38 @@ -.nav { - @apply .bg-primary-600 .border-b .border-t .border-primary-700; - height: 56px; +#navigation { + @apply .w-full .bg-neutral-900 .shadow-md; - & .logo { - @apply .mr-8 .font-sans .font-thin .text-3xl .text-white .inline-block; - - & a { - color: inherit; - text-decoration: none; - } - - @screen xsx { - @apply .hidden; - } + & > div { + @apply .mx-auto .w-full .flex .items-center; } - & .search-box { - @apply .mr-2; - - & > .search-input { - @apply .text-sm .p-2 .ml-8 .rounded .border .border-primary-600 .bg-white .text-neutral-900 .w-96; - transition: border 150ms ease-in; - - &:focus { - @apply .border-primary-700; - } - - &.has-search-results { - @apply .border-b-0 .rounded-b-none; - } - } - - & .search-results { - @apply .absolute .bg-white .border .border-primary-700 .border-t-0 .rounded .rounded-t-none .p-2 .ml-8 .z-50 .w-96; - - & a { - @apply .block .no-underline .p-2 .rounded; - - &:not(.no-hover):hover { - @apply .bg-neutral-50; - } - } - } - } - - & .menu { - @apply .flex .h-full .items-center; + & #logo { + @apply .flex-1; & > a { - transition: background-color 150ms linear; - @apply .block .flex .self-stretch .items-center .no-underline .text-white .font-light .text-sm .px-5; + @apply .text-2xl .font-header .px-4 .no-underline .text-neutral-200; + transition: color 150ms linear; &:hover { - @apply .bg-primary-700; + @apply .text-neutral-100; + } + } + } + + & .right-navigation { + @apply .flex .h-full .items-center .justify-center; + + & > a { + @apply .flex .items-center .h-full .no-underline .text-neutral-300 .px-6; + transition: background-color 150ms linear, color 150ms linear, box-shadow 150ms ease-in; + + &.active, &:hover { + @apply .text-neutral-100 .bg-black; + box-shadow: inset 0 -2px config('colors.cyan-700'); + } + + &.active { + box-shadow: inset 0 -2px config('colors.cyan-500'); } } } } - -.sidenav { - ul { - @apply .list-reset; - - & li { - @apply .block; - - & > a { - transition: border-left-color 250ms linear, color 250ms linear; - @apply .block .px-4 .py-3 .border-l-3 .border-neutral-100 .no-underline .text-neutral-400 .font-medium; - - &:hover, &.router-link-exact-active, &.router-link-active { - @apply .text-neutral-800; - } - - &.router-link-exact-active, &.router-link-active { - @apply .border-primary-500 .cursor-default; - } - - &::-moz-focus-inner { - @apply .border-none; - } - } - - /** - * Because of how the router works the first sidebar link is always active - * since that is the container for all of the server things. Override the - * style for active links if its the first one and not an exact route match. - */ - &:first-of-type > a { - &.router-link-active:not(.router-link-exact-active) { - @apply .border-neutral-100 .text-neutral-400 .cursor-pointer; - } - } - } - } -} -/* -.sidenav { - @apply .py-2; - - a { - @apply .block .py-3 .px-6 .text-neutral-900 .no-underline .border .border-transparent; - - &:hover, &.router-link-exact-active { - @apply .border-neutral-400 .bg-neutral-50; - - border-left: 1px solid transparent; - border-right: 1px solid transparent; - } - - &.router-link-exact-active + a:hover { - border-top: 1px solid transparent; - } - } -} -*/ diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index eab411e95..986f47d14 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -2,11 +2,11 @@ import * as React from 'react'; import { hot } from 'react-hot-loader/root'; import { BrowserRouter as Router, Route } from 'react-router-dom'; import AuthenticationRouter from '@/routers/AuthenticationRouter'; -import AccountRouter from '@/routers/AccountRouter'; import ServerOverviewContainer from '@/components/ServerOverviewContainer'; import { StoreProvider } from 'easy-peasy'; import { store } from '@/state'; import TransitionRouter from '@/TransitionRouter'; +import DashboardRouter from '@/routers/DashboardRouter'; interface WindowWithUser extends Window { PterodactylUser?: { @@ -41,9 +41,8 @@ const App = () => {
    - + -
    diff --git a/resources/scripts/components/NavigationBar.tsx b/resources/scripts/components/NavigationBar.tsx new file mode 100644 index 000000000..85b400c00 --- /dev/null +++ b/resources/scripts/components/NavigationBar.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { Link, NavLink } from 'react-router-dom'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faLayerGroup } from '@fortawesome/free-solid-svg-icons/faLayerGroup'; +import { faUserCircle } from '@fortawesome/free-solid-svg-icons/faUserCircle'; +import { faSignOutAlt } from '@fortawesome/free-solid-svg-icons/faSignOutAlt'; +import { faSwatchbook } from '@fortawesome/free-solid-svg-icons/faSwatchbook'; + +export default () => ( +
    +
    +
    + + Pterodactyl + +
    +
    + + + + + + + {process.env.NODE_ENV !== 'production' && + + + + } + + + +
    +
    +
    +); diff --git a/resources/scripts/components/account/AccountOverviewContainer.tsx b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx similarity index 76% rename from resources/scripts/components/account/AccountOverviewContainer.tsx rename to resources/scripts/components/dashboard/AccountOverviewContainer.tsx index d88134f2d..e35fb7d61 100644 --- a/resources/scripts/components/account/AccountOverviewContainer.tsx +++ b/resources/scripts/components/dashboard/AccountOverviewContainer.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import ContentBox from '@/components/elements/ContentBox'; -import UpdatePasswordForm from '@/components/account/forms/UpdatePasswordForm'; -import UpdateEmailAddressForm from '@/components/account/forms/UpdateEmailAddressForm'; +import UpdatePasswordForm from '@/components/dashboard/forms/UpdatePasswordForm'; +import UpdateEmailAddressForm from '@/components/dashboard/forms/UpdateEmailAddressForm'; export default () => { return ( diff --git a/resources/scripts/components/dashboard/DashboardContainer.tsx b/resources/scripts/components/dashboard/DashboardContainer.tsx new file mode 100644 index 000000000..4384da805 --- /dev/null +++ b/resources/scripts/components/dashboard/DashboardContainer.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export default () => ( +
    +

    Dashboard

    +
    +); diff --git a/resources/scripts/components/account/DesignElementsContainer.tsx b/resources/scripts/components/dashboard/DesignElementsContainer.tsx similarity index 100% rename from resources/scripts/components/account/DesignElementsContainer.tsx rename to resources/scripts/components/dashboard/DesignElementsContainer.tsx diff --git a/resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx b/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx similarity index 100% rename from resources/scripts/components/account/forms/UpdateEmailAddressForm.tsx rename to resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx diff --git a/resources/scripts/components/account/forms/UpdatePasswordForm.tsx b/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx similarity index 100% rename from resources/scripts/components/account/forms/UpdatePasswordForm.tsx rename to resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx diff --git a/resources/scripts/routers/AccountRouter.tsx b/resources/scripts/routers/AccountRouter.tsx deleted file mode 100644 index 7631d566f..000000000 --- a/resources/scripts/routers/AccountRouter.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react'; -import { Link, NavLink, Route, RouteComponentProps } from 'react-router-dom'; -import DesignElementsContainer from '@/components/account/DesignElementsContainer'; -import AccountOverviewContainer from '@/components/account/AccountOverviewContainer'; - -export default ({ match }: RouteComponentProps) => ( -
    -
    -
    -
    - - Pterodactyl - -
    -
    - - Dashboard - - - Account - -
    -
    -
    -
    - - -
    -
    -); diff --git a/resources/scripts/routers/DashboardRouter.tsx b/resources/scripts/routers/DashboardRouter.tsx new file mode 100644 index 000000000..62e3ee147 --- /dev/null +++ b/resources/scripts/routers/DashboardRouter.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { Route, RouteComponentProps } from 'react-router-dom'; +import DesignElementsContainer from '@/components/dashboard/DesignElementsContainer'; +import AccountOverviewContainer from '@/components/dashboard/AccountOverviewContainer'; +import NavigationBar from '@/components/NavigationBar'; +import DashboardContainer from '@/components/dashboard/DashboardContainer'; + +export default ({ match }: RouteComponentProps) => ( +
    + +
    + + + +
    +
    +); diff --git a/yarn.lock b/yarn.lock index 2541c4cda..974cee7d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -726,6 +726,29 @@ version "1.0.0" resolved "https://registry.yarnpkg.com/@csstools/sass-import-resolve/-/sass-import-resolve-1.0.0.tgz#32c3cdb2f7af3cd8f0dca357b592e7271f3831b5" +"@fortawesome/fontawesome-common-types@^0.2.19": + version "0.2.19" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.19.tgz#754a0f85e1290858152e1c05700ab502b11197f1" + +"@fortawesome/fontawesome-svg-core@^1.2.19": + version "1.2.19" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.19.tgz#0eca1ce9285c3d99e6e340633ee8f615f9d1a2e0" + dependencies: + "@fortawesome/fontawesome-common-types" "^0.2.19" + +"@fortawesome/free-solid-svg-icons@^5.9.0": + version "5.9.0" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.9.0.tgz#1c73e7bac17417d23f934d83f7fff5b100a7fda9" + dependencies: + "@fortawesome/fontawesome-common-types" "^0.2.19" + +"@fortawesome/react-fontawesome@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.4.tgz#18d61d9b583ca289a61aa7dccc05bd164d6bc9ad" + dependencies: + humps "^2.0.1" + prop-types "^15.5.10" + "@hot-loader/react-dom@^16.8.6": version "16.8.6" resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.8.6.tgz#7923ba27db1563a7cc48d4e0b2879a140df461ea" @@ -3772,6 +3795,10 @@ https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" +humps@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa" + iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" @@ -6059,7 +6086,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.6.1, prop-types@^15.6.2: +prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" dependencies: From 2f5a1984b3d6701939c89a11e97d95dce6f08723 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 25 Jun 2019 22:00:18 -0700 Subject: [PATCH 38/42] Make the heading on form elements less bold --- resources/assets/styles/components/forms.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/assets/styles/components/forms.css b/resources/assets/styles/components/forms.css index dab0fde2a..a56b86a95 100644 --- a/resources/assets/styles/components/forms.css +++ b/resources/assets/styles/components/forms.css @@ -79,7 +79,7 @@ input[type=number] { } label { - @apply .block .text-xs .font-medium .uppercase .text-neutral-700 .mb-2; + @apply .block .text-xs .uppercase .text-neutral-700 .mb-2; } select:not(.appearance-none) { From 3db76981709863cd3ee5eaf61c5ffa99edc51e3d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 25 Jun 2019 22:00:28 -0700 Subject: [PATCH 39/42] Basic design for server listing page --- .../dashboard/DashboardContainer.tsx | 93 ++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/resources/scripts/components/dashboard/DashboardContainer.tsx b/resources/scripts/components/dashboard/DashboardContainer.tsx index 4384da805..00e762f5d 100644 --- a/resources/scripts/components/dashboard/DashboardContainer.tsx +++ b/resources/scripts/components/dashboard/DashboardContainer.tsx @@ -1,7 +1,96 @@ import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faServer } from '@fortawesome/free-solid-svg-icons/faServer'; +import { faMicrochip } from '@fortawesome/free-solid-svg-icons/faMicrochip'; +import { faMemory } from '@fortawesome/free-solid-svg-icons/faMemory'; +import { faHdd } from '@fortawesome/free-solid-svg-icons/faHdd'; +import { faEthernet } from '@fortawesome/free-solid-svg-icons/faEthernet'; export default () => ( -
    -

    Dashboard

    +
    +
    +
    + +
    +
    +

    Party Parrots

    +
    +
    +
    + +

    + 192.168.100.100:25565 +

    +
    +
    + +

    + 34.6% +

    +
    +
    +
    + +

    + 2094 MB +

    +
    +

    of 4096 MB

    +
    +
    +
    + +

    + 278 MB +

    +
    +

    of 16 GB

    +
    +
    +
    +
    +
    + +
    +
    +

    My Factions Server

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. +

    +
    +
    +
    + +

    + 192.168.202.10:34556 +

    +
    +
    + +

    + 98.2 % +

    +
    +
    +
    + +

    + 376 MB +

    +
    +

    of 1024 MB

    +
    +
    +
    + +

    + 187 MB +

    +
    +

    of 32 GB

    +
    +
    +
    ); From 8ac8a370f87ae34ea0d18c626922eb7363e3061b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 28 Jun 2019 22:17:29 -0700 Subject: [PATCH 40/42] Fix some issues with navigating in the router and bad animations --- .../assets/styles/components/animations.css | 4 +- resources/scripts/TransitionRouter.tsx | 52 ++++++++----------- resources/scripts/components/App.tsx | 18 ++++--- .../dashboard/DashboardContainer.tsx | 5 +- .../components/server/ServerConsole.tsx | 5 ++ resources/scripts/routers/DashboardRouter.tsx | 19 ++++--- resources/scripts/routers/ServerRouter.tsx | 18 +++++++ 7 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 resources/scripts/components/server/ServerConsole.tsx create mode 100644 resources/scripts/routers/ServerRouter.tsx diff --git a/resources/assets/styles/components/animations.css b/resources/assets/styles/components/animations.css index 97c1ced7c..d472d1267 100644 --- a/resources/assets/styles/components/animations.css +++ b/resources/assets/styles/components/animations.css @@ -5,7 +5,7 @@ .fade-enter-active { @apply .opacity-100; - transition: opacity 150ms; + transition: opacity 250ms; } .fade-exit { @@ -14,7 +14,7 @@ .fade-exit-active { @apply .opacity-0; - transition: opacity 150ms; + transition: opacity 250ms; } /** @todo fix this, hides footer stuff */ diff --git a/resources/scripts/TransitionRouter.tsx b/resources/scripts/TransitionRouter.tsx index cf738e17a..630e44023 100644 --- a/resources/scripts/TransitionRouter.tsx +++ b/resources/scripts/TransitionRouter.tsx @@ -1,38 +1,32 @@ import React from 'react'; import { Route, Switch } from 'react-router'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; -import { BrowserRouter } from 'react-router-dom'; type Props = Readonly<{ - basename: string; children: React.ReactNode; }>; -export default ({ basename, children }: Props) => ( - - ( - - -
    - - {children} - -
    -

    - © 2015 - 2019  - - Pterodactyl Software - -

    -
    -
    -
    -
    - )} - /> -
    +export default ({ children }: Props) => ( + ( + + +
    + {children} +
    +

    + © 2015 - 2019  + + Pterodactyl Software + +

    +
    +
    +
    +
    + )} + /> ); diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 986f47d14..d0935f3b4 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; import { hot } from 'react-hot-loader/root'; -import { BrowserRouter as Router, Route } from 'react-router-dom'; -import AuthenticationRouter from '@/routers/AuthenticationRouter'; -import ServerOverviewContainer from '@/components/ServerOverviewContainer'; +import { BrowserRouter, BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import { StoreProvider } from 'easy-peasy'; import { store } from '@/state'; -import TransitionRouter from '@/TransitionRouter'; import DashboardRouter from '@/routers/DashboardRouter'; +import ServerRouter from '@/routers/ServerRouter'; +import AuthenticationRouter from '@/routers/AuthenticationRouter'; interface WindowWithUser extends Window { PterodactylUser?: { @@ -40,10 +39,13 @@ const App = () => {
    - - - - + + + + + + +
    diff --git a/resources/scripts/components/dashboard/DashboardContainer.tsx b/resources/scripts/components/dashboard/DashboardContainer.tsx index 00e762f5d..4aecf9ea8 100644 --- a/resources/scripts/components/dashboard/DashboardContainer.tsx +++ b/resources/scripts/components/dashboard/DashboardContainer.tsx @@ -5,10 +5,11 @@ import { faMicrochip } from '@fortawesome/free-solid-svg-icons/faMicrochip'; import { faMemory } from '@fortawesome/free-solid-svg-icons/faMemory'; import { faHdd } from '@fortawesome/free-solid-svg-icons/faHdd'; import { faEthernet } from '@fortawesome/free-solid-svg-icons/faEthernet'; +import { Link } from 'react-router-dom'; export default () => (
    -
    +
    @@ -47,7 +48,7 @@ export default () => (

    of 16 GB

    -
    +
    diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx new file mode 100644 index 000000000..ae409fd00 --- /dev/null +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default () => ( + null +); diff --git a/resources/scripts/routers/DashboardRouter.tsx b/resources/scripts/routers/DashboardRouter.tsx index 62e3ee147..699521157 100644 --- a/resources/scripts/routers/DashboardRouter.tsx +++ b/resources/scripts/routers/DashboardRouter.tsx @@ -1,17 +1,22 @@ import * as React from 'react'; -import { Route, RouteComponentProps } from 'react-router-dom'; +import { Route, RouteComponentProps, Switch } from 'react-router-dom'; import DesignElementsContainer from '@/components/dashboard/DesignElementsContainer'; import AccountOverviewContainer from '@/components/dashboard/AccountOverviewContainer'; import NavigationBar from '@/components/NavigationBar'; import DashboardContainer from '@/components/dashboard/DashboardContainer'; +import TransitionRouter from '@/TransitionRouter'; -export default ({ match }: RouteComponentProps) => ( +export default ({ location }: RouteComponentProps) => (
    -
    - - - -
    + +
    + + + + + +
    +
    ); diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx new file mode 100644 index 000000000..d7124c176 --- /dev/null +++ b/resources/scripts/routers/ServerRouter.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { Route, RouteComponentProps, Switch } from 'react-router-dom'; +import NavigationBar from '@/components/NavigationBar'; +import ServerConsole from '@/components/server/ServerConsole'; +import TransitionRouter from '@/TransitionRouter'; + +export default ({ location }: RouteComponentProps) => ( +
    + + +
    + + + +
    +
    +
    +); From 109bed4f7d59a2edc5b0c7bf8f56ac1228649a47 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 28 Jun 2019 22:49:08 -0700 Subject: [PATCH 41/42] Add basic navigation bar to server view --- .../assets/styles/components/navigation.css | 26 +++++++++++++++++++ resources/scripts/TransitionRouter.tsx | 2 +- resources/scripts/components/App.tsx | 4 +-- .../components/server/ServerConsole.tsx | 4 ++- resources/scripts/routers/DashboardRouter.tsx | 4 +-- resources/scripts/routers/ServerRouter.tsx | 20 ++++++++++---- 6 files changed, 49 insertions(+), 11 deletions(-) diff --git a/resources/assets/styles/components/navigation.css b/resources/assets/styles/components/navigation.css index 58579dda3..5db937ed8 100644 --- a/resources/assets/styles/components/navigation.css +++ b/resources/assets/styles/components/navigation.css @@ -36,3 +36,29 @@ } } } + +#sub-navigation { + @apply .w-full .bg-neutral-700 .shadow; + + .items { + @apply .flex .items-center .text-sm .mx-2; + + & > a, & > div { + @apply .inline-block .py-3 .px-4 .text-neutral-300 .no-underline; + transition: color 150ms linear, box-shadow 150ms ease-in; + + &:not(:first-of-type) { + @apply .ml-2; + } + + &.active, &:hover { + @apply .text-neutral-100; + box-shadow: inset 0 -2px config('colors.cyan-700'); + } + + &.active { + box-shadow: inset 0 -2px config('colors.cyan-500'); + } + } + } +} diff --git a/resources/scripts/TransitionRouter.tsx b/resources/scripts/TransitionRouter.tsx index 630e44023..cdb7a1d80 100644 --- a/resources/scripts/TransitionRouter.tsx +++ b/resources/scripts/TransitionRouter.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Route, Switch } from 'react-router'; +import { Route } from 'react-router'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; type Props = Readonly<{ diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index d0935f3b4..880bca8a0 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -41,9 +41,9 @@ const App = () => {
    - + - +
    diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx index ae409fd00..aae89742c 100644 --- a/resources/scripts/components/server/ServerConsole.tsx +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -1,5 +1,7 @@ import React from 'react'; export default () => ( - null +
    + Test +
    ); diff --git a/resources/scripts/routers/DashboardRouter.tsx b/resources/scripts/routers/DashboardRouter.tsx index 699521157..d9312fd8c 100644 --- a/resources/scripts/routers/DashboardRouter.tsx +++ b/resources/scripts/routers/DashboardRouter.tsx @@ -7,7 +7,7 @@ import DashboardContainer from '@/components/dashboard/DashboardContainer'; import TransitionRouter from '@/TransitionRouter'; export default ({ location }: RouteComponentProps) => ( -
    +
    @@ -18,5 +18,5 @@ export default ({ location }: RouteComponentProps) => (
    -
    + ); diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx index d7124c176..0e8543690 100644 --- a/resources/scripts/routers/ServerRouter.tsx +++ b/resources/scripts/routers/ServerRouter.tsx @@ -1,18 +1,28 @@ import * as React from 'react'; -import { Route, RouteComponentProps, Switch } from 'react-router-dom'; +import { NavLink, Route, RouteComponentProps, Switch } from 'react-router-dom'; import NavigationBar from '@/components/NavigationBar'; import ServerConsole from '@/components/server/ServerConsole'; import TransitionRouter from '@/TransitionRouter'; -export default ({ location }: RouteComponentProps) => ( -
    +export default ({ match, location }: RouteComponentProps) => ( + +
    +
    +
    + Console + File Manager + Databases + User Management +
    +
    +
    - +
    -
    + ); From e69d55ec6f5e5e70f3d7673b7521238a370fcc54 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 28 Jun 2019 22:52:57 -0700 Subject: [PATCH 42/42] Nyoom, bye bye Vue --- resources/assets/scripts/api/http.ts | 55 ---- .../scripts/api/server/createDatabase.ts | 30 -- .../scripts/api/server/files/copyFile.ts | 13 - .../scripts/api/server/files/createFolder.ts | 14 - .../scripts/api/server/files/deleteFile.ts | 13 - .../api/server/files/getDownloadToken.ts | 14 - .../api/server/files/getFileContents.ts | 20 -- .../scripts/api/server/files/renameFile.ts | 12 - .../api/server/files/writeFileContents.ts | 14 - .../api/server/getDirectoryContents.ts | 40 --- resources/assets/scripts/api/server/types.ts | 30 -- resources/assets/scripts/app.ts | 41 --- resources/assets/scripts/bootstrap.ts | 33 -- resources/assets/scripts/components/Flash.vue | 103 ------ .../assets/scripts/components/MessageBox.vue | 18 -- .../components/auth/ForgotPassword.vue | 98 ------ .../assets/scripts/components/auth/Login.vue | 18 -- .../scripts/components/auth/LoginForm.vue | 104 ------ .../scripts/components/auth/ResetPassword.vue | 128 -------- .../scripts/components/auth/TwoFactorForm.vue | 87 ----- .../assets/scripts/components/core/Icon.vue | 18 -- .../assets/scripts/components/core/Modal.vue | 54 ---- .../scripts/components/core/Navigation.vue | 142 --------- .../scripts/components/core/SpinnerModal.vue | 24 -- .../scripts/components/dashboard/Account.vue | 62 ---- .../components/dashboard/Dashboard.vue | 128 -------- .../components/dashboard/ServerBox.vue | 200 ------------ .../dashboard/account/ChangePassword.vue | 94 ------ .../account/TwoFactorAuthentication.vue | 193 ------------ .../dashboard/account/UpdateEmail.vue | 80 ----- .../assets/scripts/components/forms/CSRF.vue | 16 - .../scripts/components/server/Server.vue | 121 ------- .../server/components/PowerButtons.vue | 52 --- .../database/CreateDatabaseModal.vue | 105 ------ .../components/database/DatabaseRow.vue | 70 ---- .../database/DeleteDatabaseModal.vue | 99 ------ .../filemanager/FileContextMenu.vue | 86 ----- .../server/components/filemanager/FileRow.vue | 193 ------------ .../filemanager/modals/CopyFileModal.vue | 58 ---- .../filemanager/modals/CreateFolderModal.vue | 103 ------ .../filemanager/modals/DeleteFileModal.vue | 88 ------ .../filemanager/modals/DownloadFileModal.vue | 50 --- .../filemanager/modals/EditFileModal.vue | 298 ------------------ .../filemanager/modals/MoveFileModal.vue | 125 -------- .../filemanager/modals/RenameModal.vue | 132 -------- .../components/server/subpages/Console.vue | 181 ----------- .../components/server/subpages/Databases.vue | 112 ------- .../server/subpages/FileManager.vue | 200 ------------ resources/assets/scripts/helpers/.gitignore | 1 - resources/assets/scripts/helpers/axios.ts | 22 -- resources/assets/scripts/helpers/index.ts | 28 -- resources/assets/scripts/helpers/statuses.ts | 6 - resources/assets/scripts/helpers/ziggy.js | 11 - resources/assets/scripts/mixins/flash.ts | 58 ---- .../scripts/mixins/socketio/connector.ts | 228 -------------- .../assets/scripts/mixins/socketio/emitter.ts | 60 ---- .../assets/scripts/mixins/socketio/index.ts | 57 ---- resources/assets/scripts/models/server.ts | 87 ----- resources/assets/scripts/models/user.ts | 53 ---- .../assets/scripts/pterodactyl-shims.d.ts | 44 --- resources/assets/scripts/router.ts | 80 ----- resources/assets/scripts/store/index.ts | 34 -- .../assets/scripts/store/modules/auth.ts | 106 ------- .../assets/scripts/store/modules/dashboard.ts | 66 ---- .../assets/scripts/store/modules/server.ts | 93 ------ .../assets/scripts/store/modules/socket.ts | 47 --- resources/assets/scripts/store/types.ts | 42 --- resources/assets/scripts/vue-shims.d.ts | 4 - .../styles/components/animations.css | 0 .../styles/components/authentication.css | 0 .../styles/components/filemanager.css | 0 .../{assets => }/styles/components/forms.css | 0 .../styles/components/miscellaneous.css | 0 .../{assets => }/styles/components/modal.css | 0 .../styles/components/navigation.css | 0 .../styles/components/notifications.css | 0 .../styles/components/spinners.css | 0 .../styles/components/typography.css | 0 resources/{assets => }/styles/main.css | 0 webpack.config.js | 3 +- 80 files changed, 2 insertions(+), 5097 deletions(-) delete mode 100644 resources/assets/scripts/api/http.ts delete mode 100644 resources/assets/scripts/api/server/createDatabase.ts delete mode 100644 resources/assets/scripts/api/server/files/copyFile.ts delete mode 100644 resources/assets/scripts/api/server/files/createFolder.ts delete mode 100644 resources/assets/scripts/api/server/files/deleteFile.ts delete mode 100644 resources/assets/scripts/api/server/files/getDownloadToken.ts delete mode 100644 resources/assets/scripts/api/server/files/getFileContents.ts delete mode 100644 resources/assets/scripts/api/server/files/renameFile.ts delete mode 100644 resources/assets/scripts/api/server/files/writeFileContents.ts delete mode 100644 resources/assets/scripts/api/server/getDirectoryContents.ts delete mode 100644 resources/assets/scripts/api/server/types.ts delete mode 100644 resources/assets/scripts/app.ts delete mode 100644 resources/assets/scripts/bootstrap.ts delete mode 100644 resources/assets/scripts/components/Flash.vue delete mode 100644 resources/assets/scripts/components/MessageBox.vue delete mode 100644 resources/assets/scripts/components/auth/ForgotPassword.vue delete mode 100644 resources/assets/scripts/components/auth/Login.vue delete mode 100644 resources/assets/scripts/components/auth/LoginForm.vue delete mode 100644 resources/assets/scripts/components/auth/ResetPassword.vue delete mode 100644 resources/assets/scripts/components/auth/TwoFactorForm.vue delete mode 100644 resources/assets/scripts/components/core/Icon.vue delete mode 100644 resources/assets/scripts/components/core/Modal.vue delete mode 100644 resources/assets/scripts/components/core/Navigation.vue delete mode 100644 resources/assets/scripts/components/core/SpinnerModal.vue delete mode 100644 resources/assets/scripts/components/dashboard/Account.vue delete mode 100644 resources/assets/scripts/components/dashboard/Dashboard.vue delete mode 100644 resources/assets/scripts/components/dashboard/ServerBox.vue delete mode 100644 resources/assets/scripts/components/dashboard/account/ChangePassword.vue delete mode 100644 resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue delete mode 100644 resources/assets/scripts/components/dashboard/account/UpdateEmail.vue delete mode 100644 resources/assets/scripts/components/forms/CSRF.vue delete mode 100644 resources/assets/scripts/components/server/Server.vue delete mode 100644 resources/assets/scripts/components/server/components/PowerButtons.vue delete mode 100644 resources/assets/scripts/components/server/components/database/CreateDatabaseModal.vue delete mode 100644 resources/assets/scripts/components/server/components/database/DatabaseRow.vue delete mode 100644 resources/assets/scripts/components/server/components/database/DeleteDatabaseModal.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/FileRow.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/modals/CopyFileModal.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/modals/CreateFolderModal.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/modals/DeleteFileModal.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/modals/DownloadFileModal.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/modals/EditFileModal.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/modals/MoveFileModal.vue delete mode 100644 resources/assets/scripts/components/server/components/filemanager/modals/RenameModal.vue delete mode 100644 resources/assets/scripts/components/server/subpages/Console.vue delete mode 100644 resources/assets/scripts/components/server/subpages/Databases.vue delete mode 100644 resources/assets/scripts/components/server/subpages/FileManager.vue delete mode 100644 resources/assets/scripts/helpers/.gitignore delete mode 100644 resources/assets/scripts/helpers/axios.ts delete mode 100644 resources/assets/scripts/helpers/index.ts delete mode 100644 resources/assets/scripts/helpers/statuses.ts delete mode 100644 resources/assets/scripts/helpers/ziggy.js delete mode 100644 resources/assets/scripts/mixins/flash.ts delete mode 100644 resources/assets/scripts/mixins/socketio/connector.ts delete mode 100644 resources/assets/scripts/mixins/socketio/emitter.ts delete mode 100644 resources/assets/scripts/mixins/socketio/index.ts delete mode 100644 resources/assets/scripts/models/server.ts delete mode 100644 resources/assets/scripts/models/user.ts delete mode 100644 resources/assets/scripts/pterodactyl-shims.d.ts delete mode 100644 resources/assets/scripts/router.ts delete mode 100644 resources/assets/scripts/store/index.ts delete mode 100644 resources/assets/scripts/store/modules/auth.ts delete mode 100644 resources/assets/scripts/store/modules/dashboard.ts delete mode 100644 resources/assets/scripts/store/modules/server.ts delete mode 100644 resources/assets/scripts/store/modules/socket.ts delete mode 100644 resources/assets/scripts/store/types.ts delete mode 100644 resources/assets/scripts/vue-shims.d.ts rename resources/{assets => }/styles/components/animations.css (100%) rename resources/{assets => }/styles/components/authentication.css (100%) rename resources/{assets => }/styles/components/filemanager.css (100%) rename resources/{assets => }/styles/components/forms.css (100%) rename resources/{assets => }/styles/components/miscellaneous.css (100%) rename resources/{assets => }/styles/components/modal.css (100%) rename resources/{assets => }/styles/components/navigation.css (100%) rename resources/{assets => }/styles/components/notifications.css (100%) rename resources/{assets => }/styles/components/spinners.css (100%) rename resources/{assets => }/styles/components/typography.css (100%) rename resources/{assets => }/styles/main.css (100%) diff --git a/resources/assets/scripts/api/http.ts b/resources/assets/scripts/api/http.ts deleted file mode 100644 index 6648a63db..000000000 --- a/resources/assets/scripts/api/http.ts +++ /dev/null @@ -1,55 +0,0 @@ -import axios, {AxiosError, AxiosInstance} from 'axios'; -import {ServerApplicationCredentials} from "@/store/types"; - -// This token is set in the bootstrap.js file at the beginning of the request -// and is carried through from there. -// const token: string = ''; - -const http: AxiosInstance = axios.create({ - headers: { - 'X-Requested-With': 'XMLHttpRequest', - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, -}); - -// If we have a phpdebugbar instance registered at this point in time go -// ahead and route the response data through to it so things show up. -// @ts-ignore -if (typeof window.phpdebugbar !== 'undefined') { - http.interceptors.response.use(response => { - // @ts-ignore - window.phpdebugbar.ajaxHandler.handle(response.request); - - return response; - }); -} - -export default http; - -/** - * Creates a request object for the node that uses the server UUID and connection - * credentials. Basically just a tiny wrapper to set this quickly. - */ -export function withCredentials(server: string, credentials: ServerApplicationCredentials): AxiosInstance { - http.defaults.baseURL = credentials.node; - http.defaults.headers['X-Access-Server'] = server; - http.defaults.headers['X-Access-Token'] = credentials.key; - - return 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 { - if (error.response && error.response.data) { - const { data } = error.response; - if (data.errors && data.errors[0] && data.errors[0].detail) { - return data.errors[0].detail; - } - } - - return error.message; -} diff --git a/resources/assets/scripts/api/server/createDatabase.ts b/resources/assets/scripts/api/server/createDatabase.ts deleted file mode 100644 index 927a328b8..000000000 --- a/resources/assets/scripts/api/server/createDatabase.ts +++ /dev/null @@ -1,30 +0,0 @@ -import http from '@/api/http'; -// @ts-ignore -import route from '../../../../../vendor/tightenco/ziggy/src/js/route'; -import {AxiosError} from "axios"; -import {ServerDatabase} from "@/api/server/types"; - -/** - * Creates a new database on the system for the currently active server. - */ -export function createDatabase(server: string, database: string, remote: string): Promise { - return new Promise((resolve, reject) => { - http.post(route('api.client.servers.databases', {server}), {database, remote}) - .then(response => { - const copy: any = response.data.attributes; - copy.password = copy.relationships.password.attributes.password; - copy.showPassword = false; - - delete copy.relationships; - - resolve(copy); - }) - .catch((err: AxiosError) => { - if (err.response && err.response.data && Array.isArray(err.response.data.errors)) { - return reject(err.response.data.errors[0].detail); - } - - return reject(err); - }); - }); -} diff --git a/resources/assets/scripts/api/server/files/copyFile.ts b/resources/assets/scripts/api/server/files/copyFile.ts deleted file mode 100644 index ab13d53b0..000000000 --- a/resources/assets/scripts/api/server/files/copyFile.ts +++ /dev/null @@ -1,13 +0,0 @@ -import http from "@/api/http"; - -/** - * Creates a copy of the given file or directory on the Daemon. Expects a fully resolved path - * to be passed through for both data arguments. - */ -export function copyFile(server: string, location: string): Promise { - return new Promise((resolve, reject) => { - http.post(`/api/client/servers/${server}/files/copy`, {location}) - .then(() => resolve()) - .catch(reject); - }); -} diff --git a/resources/assets/scripts/api/server/files/createFolder.ts b/resources/assets/scripts/api/server/files/createFolder.ts deleted file mode 100644 index 91c85e12c..000000000 --- a/resources/assets/scripts/api/server/files/createFolder.ts +++ /dev/null @@ -1,14 +0,0 @@ -import http from "@/api/http"; - -/** - * Connects to the remote daemon and creates a new folder on the server. - */ -export function createFolder(server: string, directory: string, name: string): Promise { - return new Promise((resolve, reject) => { - http.post(`/api/client/servers/${server}/files/create-folder`, { - directory, name, - }) - .then(() => resolve()) - .catch(reject); - }); -} diff --git a/resources/assets/scripts/api/server/files/deleteFile.ts b/resources/assets/scripts/api/server/files/deleteFile.ts deleted file mode 100644 index f7e9ef927..000000000 --- a/resources/assets/scripts/api/server/files/deleteFile.ts +++ /dev/null @@ -1,13 +0,0 @@ -import http from "@/api/http"; - -/** - * Deletes files and/or folders from the server. You should pass through an array of - * file or folder paths to be deleted. - */ -export function deleteFile(server: string, location: string): Promise { - return new Promise((resolve, reject) => { - http.post(`/api/client/servers/${server}/files/delete`, {location}) - .then(() => resolve()) - .catch(reject); - }) -} diff --git a/resources/assets/scripts/api/server/files/getDownloadToken.ts b/resources/assets/scripts/api/server/files/getDownloadToken.ts deleted file mode 100644 index 4e784195d..000000000 --- a/resources/assets/scripts/api/server/files/getDownloadToken.ts +++ /dev/null @@ -1,14 +0,0 @@ -import http from "@/api/http"; -// @ts-ignore -import route from '../../../../../../vendor/tightenco/ziggy/src/js/route'; - -/** - * Gets a download token for a file on the server. - */ -export function getDownloadToken(server: string, file: string): Promise { - return new Promise((resolve, reject) => { - http.post(route('api.client.servers.files.download', { server, file })) - .then(response => resolve(response.data ? response.data.token || null : null)) - .catch(reject); - }); -} diff --git a/resources/assets/scripts/api/server/files/getFileContents.ts b/resources/assets/scripts/api/server/files/getFileContents.ts deleted file mode 100644 index f3a1f3d36..000000000 --- a/resources/assets/scripts/api/server/files/getFileContents.ts +++ /dev/null @@ -1,20 +0,0 @@ -import http from "@/api/http"; -import {AxiosError} from "axios"; - -export default (server: string, file: string): Promise => { - return new Promise((resolve, reject) => { - http.get(`/api/client/servers/${server}/files/contents`, { - params: { file }, - responseType: 'text', - transformResponse: res => res, - }) - .then(response => resolve(response.data || '')) - .catch((error: AxiosError) => { - if (error.response && error.response.data) { - error.response.data = JSON.parse(error.response.data); - } - - reject(error); - }); - }); -} diff --git a/resources/assets/scripts/api/server/files/renameFile.ts b/resources/assets/scripts/api/server/files/renameFile.ts deleted file mode 100644 index 75c9c3026..000000000 --- a/resources/assets/scripts/api/server/files/renameFile.ts +++ /dev/null @@ -1,12 +0,0 @@ -import http from "@/api/http"; - -export function renameFile(server: string, renameFrom: string, renameTo: string): Promise { - return new Promise((resolve, reject) => { - http.put(`/api/client/servers/${server}/files/rename`, { - rename_from: renameFrom, - rename_to: renameTo, - }) - .then(() => resolve()) - .catch(reject); - }); -} diff --git a/resources/assets/scripts/api/server/files/writeFileContents.ts b/resources/assets/scripts/api/server/files/writeFileContents.ts deleted file mode 100644 index 065c4a8db..000000000 --- a/resources/assets/scripts/api/server/files/writeFileContents.ts +++ /dev/null @@ -1,14 +0,0 @@ -import http from "@/api/http"; - -export default (server: string, file: string, content: string): Promise => { - return new Promise((resolve, reject) => { - http.post(`/api/client/servers/${server}/files/write`, content, { - params: { file }, - headers: { - 'Content-Type': 'text/plain; charset=utf-8', - }, - }) - .then(() => resolve()) - .catch(reject); - }); -} diff --git a/resources/assets/scripts/api/server/getDirectoryContents.ts b/resources/assets/scripts/api/server/getDirectoryContents.ts deleted file mode 100644 index 6138fd62f..000000000 --- a/resources/assets/scripts/api/server/getDirectoryContents.ts +++ /dev/null @@ -1,40 +0,0 @@ -import http from '../http'; -import {filter, isObject} from 'lodash'; -import {DirectoryContentObject, DirectoryContents} from "./types"; - -/** - * Get the contents of a specific directory for a given server. - */ -export function getDirectoryContents(server: string, directory: string): Promise { - return new Promise((resolve, reject) => { - http.get(`/api/client/servers/${server}/files/list`, { - params: {directory} - }) - .then((response) => { - return resolve({ - files: filter(response.data.contents, function (o: DirectoryContentObject) { - return o.file; - }), - directories: filter(response.data.contents, function (o: DirectoryContentObject) { - return o.directory; - }), - editable: response.data.editable, - }); - }) - .catch(err => { - if (err.response && err.response.status === 404) { - return reject('The directory you requested could not be located on the server'); - } - - if (err.response.data && isObject(err.response.data.errors)) { - err.response.data.errors.forEach((error: any) => { - return reject(error.detail); - }); - } - - return reject(err); - }); - }); -} - -export default getDirectoryContents; diff --git a/resources/assets/scripts/api/server/types.ts b/resources/assets/scripts/api/server/types.ts deleted file mode 100644 index 300153897..000000000 --- a/resources/assets/scripts/api/server/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -export type DirectoryContents = { - files: Array, - directories: Array, - editable: Array -} - -export type DirectoryContentObject = { - name: string, - created: string, - modified: string, - mode: string, - size: number, - directory: boolean, - file: boolean, - symlink: boolean, - mime: string, -} - -export type ServerDatabase = { - id: string, - name: string, - connections_from: string, - username: string, - host: { - address: string, - port: number, - }, - password: string, - showPassword: boolean, -} diff --git a/resources/assets/scripts/app.ts b/resources/assets/scripts/app.ts deleted file mode 100644 index 147e312d5..000000000 --- a/resources/assets/scripts/app.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import VueI18n from 'vue-i18n'; -import VueRouter from 'vue-router'; -import VeeValidate from 'vee-validate'; -// Helpers -// @ts-ignore -import {Ziggy} from './helpers/ziggy'; -// @ts-ignore -import Locales from './../../../resources/lang/locales'; - -import {FlashMixin} from './mixins/flash'; -import store from './store/index'; -import router from './router'; - -Vue.config.productionTip = false; -require('./bootstrap'); - -window.events = new Vue(); -window.Ziggy = Ziggy; - -Vue.use(Vuex); -Vue.use(VueRouter); -Vue.use(VeeValidate); -Vue.use(VueI18n); - -const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default; - -Vue.mixin({methods: {route}}); -Vue.mixin(FlashMixin); - -const i18n = new VueI18n({ - locale: 'en', - messages: {...Locales}, -}); - -if (module.hot) { - module.hot.accept(); -} - -new Vue({store, router, i18n}).$mount('#pterodactyl'); diff --git a/resources/assets/scripts/bootstrap.ts b/resources/assets/scripts/bootstrap.ts deleted file mode 100644 index 905ad5faf..000000000 --- a/resources/assets/scripts/bootstrap.ts +++ /dev/null @@ -1,33 +0,0 @@ -import axios from './api/http'; - -window._ = require('lodash'); - -/** - * We'll load jQuery and the Bootstrap jQuery plugin which provides support - * for JavaScript based Bootstrap features such as modals and tabs. This - * code may be modified to fit the specific needs of your application. - */ - -try { - window.$ = window.jQuery = require('jquery'); -} catch (e) { -} - -window.axios = axios; - -/** - * Next we will register the CSRF Token as a common header with Axios so that - * all outgoing HTTP requests automatically have it attached. This is just - * a simple convenience so we don't have to attach every token manually. - */ - -let token = document.head.querySelector('meta[name="csrf-token"]'); - -if (token) { - // @ts-ignore - window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; - // @ts-ignore - window.X_CSRF_TOKEN = token.content; -} else { - console.error('CSRF token not found in document.'); -} diff --git a/resources/assets/scripts/components/Flash.vue b/resources/assets/scripts/components/Flash.vue deleted file mode 100644 index fad2821e6..000000000 --- a/resources/assets/scripts/components/Flash.vue +++ /dev/null @@ -1,103 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/MessageBox.vue b/resources/assets/scripts/components/MessageBox.vue deleted file mode 100644 index 02a07aa22..000000000 --- a/resources/assets/scripts/components/MessageBox.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/auth/ForgotPassword.vue b/resources/assets/scripts/components/auth/ForgotPassword.vue deleted file mode 100644 index 8489c0ec1..000000000 --- a/resources/assets/scripts/components/auth/ForgotPassword.vue +++ /dev/null @@ -1,98 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/auth/Login.vue b/resources/assets/scripts/components/auth/Login.vue deleted file mode 100644 index 9ce85b1ee..000000000 --- a/resources/assets/scripts/components/auth/Login.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/auth/LoginForm.vue b/resources/assets/scripts/components/auth/LoginForm.vue deleted file mode 100644 index b021a3915..000000000 --- a/resources/assets/scripts/components/auth/LoginForm.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/auth/ResetPassword.vue b/resources/assets/scripts/components/auth/ResetPassword.vue deleted file mode 100644 index 4a41e3650..000000000 --- a/resources/assets/scripts/components/auth/ResetPassword.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/auth/TwoFactorForm.vue b/resources/assets/scripts/components/auth/TwoFactorForm.vue deleted file mode 100644 index 248235298..000000000 --- a/resources/assets/scripts/components/auth/TwoFactorForm.vue +++ /dev/null @@ -1,87 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/core/Icon.vue b/resources/assets/scripts/components/core/Icon.vue deleted file mode 100644 index 6b928d065..000000000 --- a/resources/assets/scripts/components/core/Icon.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/core/Modal.vue b/resources/assets/scripts/components/core/Modal.vue deleted file mode 100644 index eb1f5b283..000000000 --- a/resources/assets/scripts/components/core/Modal.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/core/Navigation.vue b/resources/assets/scripts/components/core/Navigation.vue deleted file mode 100644 index 0942afc98..000000000 --- a/resources/assets/scripts/components/core/Navigation.vue +++ /dev/null @@ -1,142 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/core/SpinnerModal.vue b/resources/assets/scripts/components/core/SpinnerModal.vue deleted file mode 100644 index 5c19115da..000000000 --- a/resources/assets/scripts/components/core/SpinnerModal.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/Account.vue b/resources/assets/scripts/components/dashboard/Account.vue deleted file mode 100644 index ed7486540..000000000 --- a/resources/assets/scripts/components/dashboard/Account.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/Dashboard.vue b/resources/assets/scripts/components/dashboard/Dashboard.vue deleted file mode 100644 index 3c1c174cb..000000000 --- a/resources/assets/scripts/components/dashboard/Dashboard.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/ServerBox.vue b/resources/assets/scripts/components/dashboard/ServerBox.vue deleted file mode 100644 index 5aeca1f42..000000000 --- a/resources/assets/scripts/components/dashboard/ServerBox.vue +++ /dev/null @@ -1,200 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/account/ChangePassword.vue b/resources/assets/scripts/components/dashboard/account/ChangePassword.vue deleted file mode 100644 index a544749a2..000000000 --- a/resources/assets/scripts/components/dashboard/account/ChangePassword.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue b/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue deleted file mode 100644 index c70868038..000000000 --- a/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue +++ /dev/null @@ -1,193 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/account/UpdateEmail.vue b/resources/assets/scripts/components/dashboard/account/UpdateEmail.vue deleted file mode 100644 index 815a09697..000000000 --- a/resources/assets/scripts/components/dashboard/account/UpdateEmail.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/forms/CSRF.vue b/resources/assets/scripts/components/forms/CSRF.vue deleted file mode 100644 index f0e830f03..000000000 --- a/resources/assets/scripts/components/forms/CSRF.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/Server.vue b/resources/assets/scripts/components/server/Server.vue deleted file mode 100644 index 12243a8f0..000000000 --- a/resources/assets/scripts/components/server/Server.vue +++ /dev/null @@ -1,121 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/PowerButtons.vue b/resources/assets/scripts/components/server/components/PowerButtons.vue deleted file mode 100644 index 131c332e4..000000000 --- a/resources/assets/scripts/components/server/components/PowerButtons.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/database/CreateDatabaseModal.vue b/resources/assets/scripts/components/server/components/database/CreateDatabaseModal.vue deleted file mode 100644 index 9636565ef..000000000 --- a/resources/assets/scripts/components/server/components/database/CreateDatabaseModal.vue +++ /dev/null @@ -1,105 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/database/DatabaseRow.vue b/resources/assets/scripts/components/server/components/database/DatabaseRow.vue deleted file mode 100644 index 35c34573d..000000000 --- a/resources/assets/scripts/components/server/components/database/DatabaseRow.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/database/DeleteDatabaseModal.vue b/resources/assets/scripts/components/server/components/database/DeleteDatabaseModal.vue deleted file mode 100644 index 12a135b33..000000000 --- a/resources/assets/scripts/components/server/components/database/DeleteDatabaseModal.vue +++ /dev/null @@ -1,99 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue b/resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue deleted file mode 100644 index af317622b..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/FileRow.vue b/resources/assets/scripts/components/server/components/filemanager/FileRow.vue deleted file mode 100644 index e447483c4..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/FileRow.vue +++ /dev/null @@ -1,193 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/modals/CopyFileModal.vue b/resources/assets/scripts/components/server/components/filemanager/modals/CopyFileModal.vue deleted file mode 100644 index c60690d75..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/modals/CopyFileModal.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/modals/CreateFolderModal.vue b/resources/assets/scripts/components/server/components/filemanager/modals/CreateFolderModal.vue deleted file mode 100644 index 912b3b5bd..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/modals/CreateFolderModal.vue +++ /dev/null @@ -1,103 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/modals/DeleteFileModal.vue b/resources/assets/scripts/components/server/components/filemanager/modals/DeleteFileModal.vue deleted file mode 100644 index bfe08f4b7..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/modals/DeleteFileModal.vue +++ /dev/null @@ -1,88 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/modals/DownloadFileModal.vue b/resources/assets/scripts/components/server/components/filemanager/modals/DownloadFileModal.vue deleted file mode 100644 index a669f45a8..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/modals/DownloadFileModal.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/modals/EditFileModal.vue b/resources/assets/scripts/components/server/components/filemanager/modals/EditFileModal.vue deleted file mode 100644 index 1a01912fc..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/modals/EditFileModal.vue +++ /dev/null @@ -1,298 +0,0 @@ - - - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/modals/MoveFileModal.vue b/resources/assets/scripts/components/server/components/filemanager/modals/MoveFileModal.vue deleted file mode 100644 index 7166046ce..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/modals/MoveFileModal.vue +++ /dev/null @@ -1,125 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/components/filemanager/modals/RenameModal.vue b/resources/assets/scripts/components/server/components/filemanager/modals/RenameModal.vue deleted file mode 100644 index c87b0dcfd..000000000 --- a/resources/assets/scripts/components/server/components/filemanager/modals/RenameModal.vue +++ /dev/null @@ -1,132 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/subpages/Console.vue b/resources/assets/scripts/components/server/subpages/Console.vue deleted file mode 100644 index b4adad664..000000000 --- a/resources/assets/scripts/components/server/subpages/Console.vue +++ /dev/null @@ -1,181 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/subpages/Databases.vue b/resources/assets/scripts/components/server/subpages/Databases.vue deleted file mode 100644 index 95372ccf8..000000000 --- a/resources/assets/scripts/components/server/subpages/Databases.vue +++ /dev/null @@ -1,112 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/server/subpages/FileManager.vue b/resources/assets/scripts/components/server/subpages/FileManager.vue deleted file mode 100644 index 0b89c4f7e..000000000 --- a/resources/assets/scripts/components/server/subpages/FileManager.vue +++ /dev/null @@ -1,200 +0,0 @@ - - - diff --git a/resources/assets/scripts/helpers/.gitignore b/resources/assets/scripts/helpers/.gitignore deleted file mode 100644 index 232ae1dd0..000000000 --- a/resources/assets/scripts/helpers/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ziggy.js diff --git a/resources/assets/scripts/helpers/axios.ts b/resources/assets/scripts/helpers/axios.ts deleted file mode 100644 index 5a0c76f06..000000000 --- a/resources/assets/scripts/helpers/axios.ts +++ /dev/null @@ -1,22 +0,0 @@ -import axios, {AxiosResponse} from 'axios'; - -/** - * We'll load the axios HTTP library which allows us to easily issue requests - * to our Laravel back-end. This library automatically handles sending the - * CSRF token as a header based on the value of the "XSRF" token cookie. - */ -axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; -axios.defaults.headers.common['Accept'] = 'application/json'; - -// Attach the response data to phpdebugbar so that we can see everything happening. -// @ts-ignore -if (typeof phpdebugbar !== 'undefined') { - axios.interceptors.response.use(function (response: AxiosResponse) { - // @ts-ignore - phpdebugbar.ajaxHandler.handle(response.request); - - return response; - }); -} - -export default axios; diff --git a/resources/assets/scripts/helpers/index.ts b/resources/assets/scripts/helpers/index.ts deleted file mode 100644 index 4768c4da8..000000000 --- a/resources/assets/scripts/helpers/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {format} from 'date-fns'; - -/** - * Return the human readable filesize for a given number of bytes. This - * uses 1024 as the base, so the response is denoted accordingly. - */ -export function readableSize(bytes: number): string { - if (Math.abs(bytes) < 1024) { - return `${bytes} Bytes`; - } - - let u: number = -1; - const units: Array = ['KiB', 'MiB', 'GiB', 'TiB']; - - do { - bytes /= 1024; - u++; - } while (Math.abs(bytes) >= 1024 && u < units.length - 1); - - return `${bytes.toFixed(1)} ${units[u]}`; -} - -/** - * Format the given date as a human readable string. - */ -export function formatDate(date: string): string { - return format(date, 'MMM D, YYYY [at] HH:MM'); -} diff --git a/resources/assets/scripts/helpers/statuses.ts b/resources/assets/scripts/helpers/statuses.ts deleted file mode 100644 index 662a3a6fb..000000000 --- a/resources/assets/scripts/helpers/statuses.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default { - STATUS_OFF: 'offline', - STATUS_ON: 'running', - STATUS_STARTING: 'starting', - STATUS_STOPPING: 'stopping', -}; diff --git a/resources/assets/scripts/helpers/ziggy.js b/resources/assets/scripts/helpers/ziggy.js deleted file mode 100644 index b1a521065..000000000 --- a/resources/assets/scripts/helpers/ziggy.js +++ /dev/null @@ -1,11 +0,0 @@ - var Ziggy = { - namedRoutes: JSON.parse('{"debugbar.openhandler":{"uri":"_debugbar\/open","methods":["GET","HEAD"],"domain":null},"debugbar.clockwork":{"uri":"_debugbar\/clockwork\/{id}","methods":["GET","HEAD"],"domain":null},"debugbar.assets.css":{"uri":"_debugbar\/assets\/stylesheets","methods":["GET","HEAD"],"domain":null},"debugbar.assets.js":{"uri":"_debugbar\/assets\/javascript","methods":["GET","HEAD"],"domain":null},"debugbar.cache.delete":{"uri":"_debugbar\/cache\/{key}\/{tags?}","methods":["DELETE"],"domain":null},"index":{"uri":"\/","methods":["GET","HEAD"],"domain":null},"account":{"uri":"account","methods":["GET","HEAD"],"domain":null},"account.api":{"uri":"account\/api","methods":["GET","HEAD"],"domain":null},"account.api.new":{"uri":"account\/api\/new","methods":["GET","HEAD"],"domain":null},"account.api.revoke":{"uri":"account\/api\/revoke\/{identifier}","methods":["DELETE"],"domain":null},"account.two_factor":{"uri":"account\/two_factor","methods":["GET","HEAD"],"domain":null},"account.two_factor.enable":{"uri":"account\/two_factor\/totp","methods":["POST"],"domain":null},"account.two_factor.disable":{"uri":"account\/two_factor\/totp\/disable","methods":["POST"],"domain":null},"admin.index":{"uri":"admin","methods":["GET","HEAD"],"domain":null},"admin.statistics":{"uri":"admin\/statistics","methods":["GET","HEAD"],"domain":null},"admin.api.index":{"uri":"admin\/api","methods":["GET","HEAD"],"domain":null},"admin.api.new":{"uri":"admin\/api\/new","methods":["GET","HEAD"],"domain":null},"admin.api.delete":{"uri":"admin\/api\/revoke\/{identifier}","methods":["DELETE"],"domain":null},"admin.locations":{"uri":"admin\/locations","methods":["GET","HEAD"],"domain":null},"admin.locations.view":{"uri":"admin\/locations\/view\/{location}","methods":["GET","HEAD"],"domain":null},"admin.databases":{"uri":"admin\/databases","methods":["GET","HEAD"],"domain":null},"admin.databases.view":{"uri":"admin\/databases\/view\/{host}","methods":["GET","HEAD"],"domain":null},"admin.settings":{"uri":"admin\/settings","methods":["GET","HEAD"],"domain":null},"admin.settings.mail":{"uri":"admin\/settings\/mail","methods":["GET","HEAD"],"domain":null},"admin.settings.mail.test":{"uri":"admin\/settings\/mail\/test","methods":["GET","HEAD"],"domain":null},"admin.settings.advanced":{"uri":"admin\/settings\/advanced","methods":["GET","HEAD"],"domain":null},"admin.users":{"uri":"admin\/users","methods":["GET","HEAD"],"domain":null},"admin.users.json":{"uri":"admin\/users\/accounts.json","methods":["GET","HEAD"],"domain":null},"admin.users.new":{"uri":"admin\/users\/new","methods":["GET","HEAD"],"domain":null},"admin.users.view":{"uri":"admin\/users\/view\/{user}","methods":["GET","HEAD"],"domain":null},"admin.servers":{"uri":"admin\/servers","methods":["GET","HEAD"],"domain":null},"admin.servers.new":{"uri":"admin\/servers\/new","methods":["GET","HEAD"],"domain":null},"admin.servers.view":{"uri":"admin\/servers\/view\/{server}","methods":["GET","HEAD"],"domain":null},"admin.servers.view.details":{"uri":"admin\/servers\/view\/{server}\/details","methods":["GET","HEAD"],"domain":null},"admin.servers.view.build":{"uri":"admin\/servers\/view\/{server}\/build","methods":["GET","HEAD"],"domain":null},"admin.servers.view.startup":{"uri":"admin\/servers\/view\/{server}\/startup","methods":["GET","HEAD"],"domain":null},"admin.servers.view.database":{"uri":"admin\/servers\/view\/{server}\/database","methods":["GET","HEAD"],"domain":null},"admin.servers.view.manage":{"uri":"admin\/servers\/view\/{server}\/manage","methods":["GET","HEAD"],"domain":null},"admin.servers.view.delete":{"uri":"admin\/servers\/view\/{server}\/delete","methods":["GET","HEAD"],"domain":null},"admin.servers.view.manage.toggle":{"uri":"admin\/servers\/view\/{server}\/manage\/toggle","methods":["POST"],"domain":null},"admin.servers.view.manage.rebuild":{"uri":"admin\/servers\/view\/{server}\/manage\/rebuild","methods":["POST"],"domain":null},"admin.servers.view.manage.suspension":{"uri":"admin\/servers\/view\/{server}\/manage\/suspension","methods":["POST"],"domain":null},"admin.servers.view.manage.reinstall":{"uri":"admin\/servers\/view\/{server}\/manage\/reinstall","methods":["POST"],"domain":null},"admin.servers.view.database.delete":{"uri":"admin\/servers\/view\/{server}\/database\/{database}\/delete","methods":["DELETE"],"domain":null},"admin.nodes":{"uri":"admin\/nodes","methods":["GET","HEAD"],"domain":null},"admin.nodes.new":{"uri":"admin\/nodes\/new","methods":["GET","HEAD"],"domain":null},"admin.nodes.view":{"uri":"admin\/nodes\/view\/{node}","methods":["GET","HEAD"],"domain":null},"admin.nodes.view.settings":{"uri":"admin\/nodes\/view\/{node}\/settings","methods":["GET","HEAD"],"domain":null},"admin.nodes.view.configuration":{"uri":"admin\/nodes\/view\/{node}\/configuration","methods":["GET","HEAD"],"domain":null},"admin.nodes.view.allocation":{"uri":"admin\/nodes\/view\/{node}\/allocation","methods":["GET","HEAD"],"domain":null},"admin.nodes.view.servers":{"uri":"admin\/nodes\/view\/{node}\/servers","methods":["GET","HEAD"],"domain":null},"admin.nodes.view.configuration.token":{"uri":"admin\/nodes\/view\/{node}\/settings\/token","methods":["GET","HEAD"],"domain":null},"admin.nodes.view.allocation.removeBlock":{"uri":"admin\/nodes\/view\/{node}\/allocation\/remove","methods":["POST"],"domain":null},"admin.nodes.view.allocation.setAlias":{"uri":"admin\/nodes\/view\/{node}\/allocation\/alias","methods":["POST"],"domain":null},"admin.nodes.view.delete":{"uri":"admin\/nodes\/view\/{node}\/delete","methods":["DELETE"],"domain":null},"admin.nodes.view.allocation.removeSingle":{"uri":"admin\/nodes\/view\/{node}\/allocation\/remove\/{allocation}","methods":["DELETE"],"domain":null},"admin.nodes.view.allocation.removeMultiple":{"uri":"admin\/nodes\/view\/{node}\/allocations","methods":["DELETE"],"domain":null},"admin.nests":{"uri":"admin\/nests","methods":["GET","HEAD"],"domain":null},"admin.nests.new":{"uri":"admin\/nests\/new","methods":["GET","HEAD"],"domain":null},"admin.nests.view":{"uri":"admin\/nests\/view\/{nest}","methods":["GET","HEAD"],"domain":null},"admin.nests.egg.new":{"uri":"admin\/nests\/egg\/new","methods":["GET","HEAD"],"domain":null},"admin.nests.egg.view":{"uri":"admin\/nests\/egg\/{egg}","methods":["GET","HEAD"],"domain":null},"admin.nests.egg.export":{"uri":"admin\/nests\/egg\/{egg}\/export","methods":["GET","HEAD"],"domain":null},"admin.nests.egg.variables":{"uri":"admin\/nests\/egg\/{egg}\/variables","methods":["GET","HEAD"],"domain":null},"admin.nests.egg.scripts":{"uri":"admin\/nests\/egg\/{egg}\/scripts","methods":["GET","HEAD"],"domain":null},"admin.nests.egg.import":{"uri":"admin\/nests\/import","methods":["POST"],"domain":null},"admin.nests.egg.variables.edit":{"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","methods":["PATCH"],"domain":null},"admin.packs":{"uri":"admin\/packs","methods":["GET","HEAD"],"domain":null},"admin.packs.new":{"uri":"admin\/packs\/new","methods":["GET","HEAD"],"domain":null},"admin.packs.new.template":{"uri":"admin\/packs\/new\/template","methods":["GET","HEAD"],"domain":null},"admin.packs.view":{"uri":"admin\/packs\/view\/{pack}","methods":["GET","HEAD"],"domain":null},"admin.packs.view.export":{"uri":"admin\/packs\/view\/{pack}\/export\/{files?}","methods":["POST"],"domain":null},"auth.login":{"uri":"auth\/login","methods":["GET","HEAD"],"domain":null},"auth.forgot-password":{"uri":"auth\/password","methods":["GET","HEAD"],"domain":null},"auth.reset":{"uri":"auth\/password\/reset\/{token}","methods":["GET","HEAD"],"domain":null},"auth.login-checkpoint":{"uri":"auth\/login\/checkpoint","methods":["POST"],"domain":null},"auth.reset-password":{"uri":"auth\/password\/reset","methods":["POST"],"domain":null},"auth.logout":{"uri":"auth\/logout","methods":["GET","HEAD"],"domain":null},"server.credentials":{"uri":"api\/server\/{server}\/credentials","methods":["GET","HEAD"],"domain":null},"server.files":{"uri":"api\/server\/{server}\/files\/{directory?}","methods":["GET","HEAD"],"domain":null},"server.index":{"uri":"api\/server\/{server}","methods":["GET","HEAD"],"domain":null},"server.console":{"uri":"api\/server\/{server}\/console","methods":["GET","HEAD"],"domain":null},"api.application.users":{"uri":"api\/application\/users","methods":["GET","HEAD"],"domain":null},"api.application.users.view":{"uri":"api\/application\/users\/{user}","methods":["GET","HEAD"],"domain":null},"api.application.users.external":{"uri":"api\/application\/users\/external\/{external_id}","methods":["GET","HEAD"],"domain":null},"api.application.nodes":{"uri":"api\/application\/nodes","methods":["GET","HEAD"],"domain":null},"api.application.nodes.view":{"uri":"api\/application\/nodes\/{node}","methods":["GET","HEAD"],"domain":null},"api.application.allocations":{"uri":"api\/application\/nodes\/{node}\/allocations","methods":["GET","HEAD"],"domain":null},"api.application.allocations.view":{"uri":"api\/application\/nodes\/{node}\/allocations\/{allocation}","methods":["DELETE"],"domain":null},"api.applications.locations":{"uri":"api\/application\/locations","methods":["GET","HEAD"],"domain":null},"api.application.locations.view":{"uri":"api\/application\/locations\/{location}","methods":["GET","HEAD"],"domain":null},"api.application.servers":{"uri":"api\/application\/servers","methods":["GET","HEAD"],"domain":null},"api.application.servers.view":{"uri":"api\/application\/servers\/{server}","methods":["GET","HEAD"],"domain":null},"api.application.servers.external":{"uri":"api\/application\/servers\/external\/{external_id}","methods":["GET","HEAD"],"domain":null},"api.application.servers.details":{"uri":"api\/application\/servers\/{server}\/details","methods":["PATCH"],"domain":null},"api.application.servers.build":{"uri":"api\/application\/servers\/{server}\/build","methods":["PATCH"],"domain":null},"api.application.servers.startup":{"uri":"api\/application\/servers\/{server}\/startup","methods":["PATCH"],"domain":null},"api.application.servers.suspend":{"uri":"api\/application\/servers\/{server}\/suspend","methods":["POST"],"domain":null},"api.application.servers.unsuspend":{"uri":"api\/application\/servers\/{server}\/unsuspend","methods":["POST"],"domain":null},"api.application.servers.reinstall":{"uri":"api\/application\/servers\/{server}\/reinstall","methods":["POST"],"domain":null},"api.application.servers.rebuild":{"uri":"api\/application\/servers\/{server}\/rebuild","methods":["POST"],"domain":null},"api.application.servers.databases":{"uri":"api\/application\/servers\/{server}\/databases","methods":["GET","HEAD"],"domain":null},"api.application.servers.databases.view":{"uri":"api\/application\/servers\/{server}\/databases\/{database}","methods":["GET","HEAD"],"domain":null},"api.application.nests":{"uri":"api\/application\/nests","methods":["GET","HEAD"],"domain":null},"api.application.nests.view":{"uri":"api\/application\/nests\/{nest}","methods":["GET","HEAD"],"domain":null},"api.application.nests.eggs":{"uri":"api\/application\/nests\/{nest}\/eggs","methods":["GET","HEAD"],"domain":null},"api.application.nests.eggs.view":{"uri":"api\/application\/nests\/{nest}\/eggs\/{egg}","methods":["GET","HEAD"],"domain":null},"api.client.index":{"uri":"api\/client","methods":["GET","HEAD"],"domain":null},"api.client.account":{"uri":"api\/client\/account","methods":["GET","HEAD"],"domain":null},"api.client.account.update-email":{"uri":"api\/client\/account\/email","methods":["PUT"],"domain":null},"api.client.account.update-password":{"uri":"api\/client\/account\/password","methods":["PUT"],"domain":null},"api.client.servers.view":{"uri":"api\/client\/servers\/{server}","methods":["GET","HEAD"],"domain":null},"api.client.servers.resources":{"uri":"api\/client\/servers\/{server}\/utilization","methods":["GET","HEAD"],"domain":null},"api.client.servers.command":{"uri":"api\/client\/servers\/{server}\/command","methods":["POST"],"domain":null},"api.client.servers.power":{"uri":"api\/client\/servers\/{server}\/power","methods":["POST"],"domain":null},"api.client.servers.databases":{"uri":"api\/client\/servers\/{server}\/databases","methods":["GET","HEAD"],"domain":null},"api.client.servers.databases.delete":{"uri":"api\/client\/servers\/{server}\/databases\/{database}","methods":["DELETE"],"domain":null},"api.client.servers.files.list":{"uri":"api\/client\/servers\/{server}\/files\/list","methods":["GET","HEAD"],"domain":null},"api.client.servers.files.contents":{"uri":"api\/client\/servers\/{server}\/files\/contents","methods":["GET","HEAD"],"domain":null},"api.client.servers.files.rename":{"uri":"api\/client\/servers\/{server}\/files\/rename","methods":["PUT"],"domain":null},"api.client.servers.files.copy":{"uri":"api\/client\/servers\/{server}\/files\/copy","methods":["POST"],"domain":null},"api.client.servers.files.write":{"uri":"api\/client\/servers\/{server}\/files\/write","methods":["POST"],"domain":null},"api.client.servers.files.delete":{"uri":"api\/client\/servers\/{server}\/files\/delete","methods":["POST"],"domain":null},"api.client.servers.files.create-folder":{"uri":"api\/client\/servers\/{server}\/files\/create-folder","methods":["POST"],"domain":null},"api.client.servers.files.download":{"uri":"api\/client\/servers\/{server}\/files\/download\/{file}","methods":["POST"],"domain":null},"api.client.servers.network":{"uri":"api\/client\/servers\/{server}\/network","methods":["GET","HEAD"],"domain":null},"api.remote.authenticate":{"uri":"api\/remote\/authenticate\/{token}","methods":["GET","HEAD"],"domain":null},"api.remote.download_file":{"uri":"api\/remote\/download-file","methods":["POST"],"domain":null},"api.remote.eggs":{"uri":"api\/remote\/eggs","methods":["GET","HEAD"],"domain":null},"api.remote.eggs.download":{"uri":"api\/remote\/eggs\/{uuid}","methods":["GET","HEAD"],"domain":null},"api.remote.scripts":{"uri":"api\/remote\/scripts\/{uuid}","methods":["GET","HEAD"],"domain":null},"api.remote.sftp":{"uri":"api\/remote\/sftp","methods":["POST"],"domain":null},"daemon.pack.pull":{"uri":"daemon\/packs\/pull\/{uuid}","methods":["GET","HEAD"],"domain":null},"daemon.pack.hash":{"uri":"daemon\/packs\/pull\/{uuid}\/hash","methods":["GET","HEAD"],"domain":null},"daemon.configuration":{"uri":"daemon\/configure\/{token}","methods":["GET","HEAD"],"domain":null},"daemon.install":{"uri":"daemon\/install","methods":["POST"],"domain":null}}'), - baseUrl: 'http://pterodactyl.test/', - baseProtocol: 'http', - baseDomain: 'pterodactyl.test', - basePort: false - }; - - export { - Ziggy - } diff --git a/resources/assets/scripts/mixins/flash.ts b/resources/assets/scripts/mixins/flash.ts deleted file mode 100644 index c90b98641..000000000 --- a/resources/assets/scripts/mixins/flash.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {ComponentOptions} from "vue"; -import {Vue} from "vue/types/vue"; -import {TranslateResult} from "vue-i18n"; - -export interface FlashInterface { - flash(message: string | TranslateResult, title: string, severity: string): void; - - clear(): void, - - success(message: string | TranslateResult): void, - - info(message: string | TranslateResult): void, - - warning(message: string | TranslateResult): void, - - error(message: string | TranslateResult): void, -} - -class Flash implements FlashInterface { - flash(message: string, title: string, severity: string = 'info'): void { - severity = severity || 'info'; - if (['danger', 'fatal', 'error'].includes(severity)) { - severity = 'error'; - } - - // @ts-ignore - window.events.$emit('flash', {message, title, severity}); - } - - clear(): void { - // @ts-ignore - window.events.$emit('clear-flashes'); - } - - success(message: string): void { - this.flash(message, 'Success', 'success'); - } - - info(message: string): void { - this.flash(message, 'Info', 'info'); - } - - warning(message: string): void { - this.flash(message, 'Warning', 'warning'); - } - - error(message: string): void { - this.flash(message, 'Error', 'error'); - } -} - -export const FlashMixin: ComponentOptions = { - computed: { - '$flash': function () { - return new Flash(); - } - }, -}; diff --git a/resources/assets/scripts/mixins/socketio/connector.ts b/resources/assets/scripts/mixins/socketio/connector.ts deleted file mode 100644 index 84804836c..000000000 --- a/resources/assets/scripts/mixins/socketio/connector.ts +++ /dev/null @@ -1,228 +0,0 @@ -import {camelCase} from 'lodash'; -import SocketEmitter from './emitter'; -import {Store} from "vuex"; - -const SOCKET_CONNECT = 'connect'; -const SOCKET_ERROR = 'error'; -const SOCKET_DISCONNECT = 'disconnect'; - -// This is defined in the wings daemon code and referenced here so that it is obvious -// where we are pulling these random data objects from. -type WingsWebsocketResponse = { - event: string, - args: Array -} - -export default class SocketioConnector { - /** - * The socket instance. - */ - socket: null | WebSocket; - - /** - * The vuex store being used to persist data and socket state. - */ - store: Store | undefined; - - /** - * Tracks a reconnect attempt for the websocket. Will gradually back off on attempts - * after a certain period of time has elapsed. - */ - private reconnectTimeout: any; - - /** - * Tracks the number of reconnect attempts which is used to determine the backoff - * throttle for connections. - */ - private reconnectAttempts: number = 0; - - private socketProtocol?: string; - private socketUrl?: string; - - constructor(store: Store | undefined) { - this.socket = null; - this.store = store; - } - - /** - * Initialize a new Socket connection. - */ - public connect(url: string, protocol?: string): void { - this.socketUrl = url; - this.socketProtocol = protocol; - - this.connectToSocket() - .then(socket => { - this.socket = socket; - this.emitAndPassToStore(SOCKET_CONNECT); - this.registerEventListeners(); - }) - .catch(() => this.reconnectToSocket()); - } - - /** - * Return the socket instance we are working with. - */ - public instance(): WebSocket | null { - return this.socket; - } - - /** - * Sends an event along to the websocket. If there is no active connection, a void - * result is returned. - */ - public emit(event: string, payload?: string | Array): void | false { - if (!this.socket) { - return false - } - - this.socket.send(JSON.stringify({ - event, args: typeof payload === 'string' ? [payload] : payload - })); - } - - /** - * Register the event listeners for this socket including user-defined ones in the store as - * well as global system events from Socekt.io. - */ - protected registerEventListeners() { - if (!this.socket) { - return; - } - - this.socket.onclose = () => { - this.reconnectToSocket(); - this.emitAndPassToStore(SOCKET_DISCONNECT); - }; - - this.socket.onerror = () => { - if (this.socket && this.socket.readyState !== WebSocket.OPEN) { - this.emitAndPassToStore(SOCKET_ERROR, ['Failed to connect to websocket.']); - } - }; - - this.socket.onmessage = (wse): void => { - try { - let {event, args}: WingsWebsocketResponse = JSON.parse(wse.data); - - this.emitAndPassToStore(event, args); - } catch (ex) { - // do nothing, bad JSON response - console.error(ex); - return - } - }; - } - - /** - * Performs an actual socket connection, wrapped as a Promise for an easier interface. - */ - protected connectToSocket(): Promise { - return new Promise((resolve, reject) => { - let hasReturned = false; - const socket = new WebSocket(this.socketUrl!, this.socketProtocol); - - socket.onopen = () => { - if (hasReturned) { - socket && socket.close(); - } - - hasReturned = true; - this.resetConnectionAttempts(); - resolve(socket); - }; - - const rejectFunc = () => { - if (!hasReturned) { - hasReturned = true; - this.emitAndPassToStore(SOCKET_ERROR, ['Failed to connect to websocket.']); - reject(); - } - }; - - socket.onerror = rejectFunc; - socket.onclose = rejectFunc; - }); - } - - - /** - * Attempts to reconnect to the socket instance if it becomes disconnected. - */ - private reconnectToSocket() { - const { socket } = this; - if (!socket) { - return; - } - - // Clear the existing timeout if one exists for some reason. - this.reconnectTimeout && clearTimeout(this.reconnectTimeout); - - this.reconnectTimeout = setTimeout(() => { - console.warn(`Attempting to reconnect to websocket [${this.reconnectAttempts}]...`); - - this.reconnectAttempts++; - this.connect(this.socketUrl!, this.socketProtocol); - }, this.getIntervalTimeout()); - } - - private resetConnectionAttempts() { - this.reconnectTimeout && clearTimeout(this.reconnectTimeout); - this.reconnectAttempts = 0; - } - - /** - * Determine the amount of time we should wait before attempting to reconnect to the socket. - */ - private getIntervalTimeout(): number { - if (this.reconnectAttempts < 10) { - return 50; - } else if (this.reconnectAttempts < 25) { - return 500; - } else if (this.reconnectAttempts < 50) { - return 1000; - } - - return 2500; - } - - - /** - * Emits the event over the event emitter and also passes it along to the vuex store. - */ - private emitAndPassToStore(event: string, payload?: Array) { - payload ? SocketEmitter.emit(event, ...payload) : SocketEmitter.emit(event); - this.passToStore(event, payload); - } - - /** - * Pass event calls off to the Vuex store if there is a corresponding function. - */ - private passToStore(event: string, payload?: Array) { - if (!this.store) { - return; - } - - const s: Store = this.store; - const mutation = `SOCKET_${event.toUpperCase()}`; - const action = `socket_${camelCase(event)}`; - - // @ts-ignore - Object.keys(this.store._mutations).filter((namespaced: string): boolean => { - return namespaced.split('/').pop() === mutation; - }).forEach((namespaced: string): void => { - s.commit(namespaced, payload ? this.unwrap(payload) : null); - }); - - // @ts-ignore - Object.keys(this.store._actions).filter((namespaced: string): boolean => { - return namespaced.split('/').pop() === action; - }).forEach((namespaced: string): void => { - s.dispatch(namespaced, payload ? this.unwrap(payload) : null).catch(console.error); - }); - } - - private unwrap(args: Array) { - return (args && args.length <= 1) ? args[0] : args; - } -} diff --git a/resources/assets/scripts/mixins/socketio/emitter.ts b/resources/assets/scripts/mixins/socketio/emitter.ts deleted file mode 100644 index 8a97772e8..000000000 --- a/resources/assets/scripts/mixins/socketio/emitter.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {isFunction} from 'lodash'; -import {ComponentOptions} from "vue"; -import {Vue} from "vue/types/vue"; - -export default new class SocketEmitter { - listeners: Map) => void, - vm: ComponentOptions, - }>>; - - constructor() { - this.listeners = new Map(); - } - - /** - * Add an event listener for socket events. - */ - addListener(event: string | number, callback: (...data: any[]) => void, vm: ComponentOptions) { - if (!isFunction(callback)) { - return; - } - - if (!this.listeners.has(event)) { - this.listeners.set(event, []); - } - - // @ts-ignore - this.listeners.get(event).push({callback, vm}); - } - - /** - * Remove an event listener for socket events based on the context passed through. - */ - removeListener(event: string | number, callback: (...data: any[]) => void, vm: ComponentOptions) { - if (!isFunction(callback) || !this.listeners.has(event)) { - return; - } - - // @ts-ignore - const filtered = this.listeners.get(event).filter((listener) => { - return listener.callback !== callback || listener.vm !== vm; - }); - - if (filtered.length > 0) { - this.listeners.set(event, filtered); - } else { - this.listeners.delete(event); - } - } - - /** - * Emit a socket event. - */ - emit(event: string | number, ...args: any) { - (this.listeners.get(event) || []).forEach((listener) => { - // @ts-ignore - listener.callback.call(listener.vm, ...args); - }); - } -} diff --git a/resources/assets/scripts/mixins/socketio/index.ts b/resources/assets/scripts/mixins/socketio/index.ts deleted file mode 100644 index 118bccf95..000000000 --- a/resources/assets/scripts/mixins/socketio/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import SocketEmitter from './emitter'; -import SocketioConnector from './connector'; -import {ComponentOptions} from 'vue'; -import {Vue} from "vue/types/vue"; - -let connector: SocketioConnector | null = null; - -export const Socketio: ComponentOptions = { - /** - * Setup the socket when we create the first component using the mixin. This is the Server.vue - * file, unless you mess up all of this code. Subsequent components to use this mixin will - * receive the existing connector instance, so it is very important that the top-most component - * calls the connectors destroy function when it is destroyed. - */ - created: function () { - if (!connector) { - connector = new SocketioConnector(this.$store); - } - - const sockets = (this.$options || {}).sockets || {}; - Object.keys(sockets).forEach((event) => { - SocketEmitter.addListener(event, sockets[event], this); - }); - }, - - /** - * Before destroying the component we need to remove any event listeners registered for it. - */ - beforeDestroy: function () { - const sockets = (this.$options || {}).sockets || {}; - Object.keys(sockets).forEach((event) => { - SocketEmitter.removeListener(event, sockets[event], this); - }); - }, - - methods: { - '$socket': function (): SocketioConnector | null { - return connector; - }, - - /** - * Disconnects from the active socket and sets the connector to null. - */ - removeSocket: function () { - if (!connector) { - return; - } - - const instance = connector.instance(); - if (instance) { - instance.close(); - } - - connector = null; - }, - }, -}; diff --git a/resources/assets/scripts/models/server.ts b/resources/assets/scripts/models/server.ts deleted file mode 100644 index f3b85d0f4..000000000 --- a/resources/assets/scripts/models/server.ts +++ /dev/null @@ -1,87 +0,0 @@ -type ServerAllocation = { - ip: string, - port: number, -}; - -type ServerLimits = { - memory: number, - swap: number, - disk: number, - io: number, - cpu: number, -} - -type ServerFeatureLimits = { - databases: number, - allocations: number, -}; - -export type ServerData = { - identifier: string, - uuid: string, - name: string, - node: string, - description: string, - allocation: ServerAllocation, - limits: ServerLimits, - feature_limits: ServerFeatureLimits, -}; - -/** - * A model representing a server returned by the client API. - */ -export default class Server { - /** - * The server identifier, generally the 8-character representation of the server UUID. - */ - identifier: string; - - /** - * The long form identifier for this server. - */ - uuid: string; - - /** - * The human friendy name for this server. - */ - name: string; - - /** - * The name of the node that this server belongs to. - */ - node: string; - - /** - * A description of this server. - */ - description: string; - - /** - * The primary allocation details for this server. - */ - allocation: ServerAllocation; - - /** - * The base limits for this server when it comes to the actual docker container. - */ - limits: ServerLimits; - - /** - * The feature limits for this server, database & allocations currently. - */ - featureLimits: ServerFeatureLimits; - - /** - * Construct a new server model instance. - */ - constructor(data: ServerData) { - this.identifier = data.identifier; - this.uuid = data.uuid; - this.name = data.name; - this.node = data.node; - this.description = data.description; - this.allocation = data.allocation; - this.limits = data.limits; - this.featureLimits = data.feature_limits; - } -} diff --git a/resources/assets/scripts/models/user.ts b/resources/assets/scripts/models/user.ts deleted file mode 100644 index 930536817..000000000 --- a/resources/assets/scripts/models/user.ts +++ /dev/null @@ -1,53 +0,0 @@ -export type UserData = { - root_admin: boolean, - username: string, - email: string, - first_name: string, - last_name: string, - language: string, -}; - -/** - * A user model that represents an user in Pterodactyl. - */ -export default class User { - /** - * Determines wether or not the user is an admin. - */ - admin: boolean; - - /** - * The username for the currently authenticated user. - */ - username: string; - - /** - * The currently authenticated users email address. - */ - email: string; - - /** - * The full name of the logged in user. - */ - name: string; - first_name: string; - last_name: string; - - /** - * The language the user has selected to use. - */ - language: string; - - /** - * Create a new user model. - */ - constructor(data: UserData) { - this.admin = data.root_admin; - this.username = data.username; - this.email = data.email; - this.name = `${data.first_name} ${data.last_name}`; - this.first_name = data.first_name; - this.last_name = data.last_name; - this.language = data.language; - } -} diff --git a/resources/assets/scripts/pterodactyl-shims.d.ts b/resources/assets/scripts/pterodactyl-shims.d.ts deleted file mode 100644 index ea89db124..000000000 --- a/resources/assets/scripts/pterodactyl-shims.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -import Vue from "vue"; -import {Store} from "vuex"; -import {FlashInterface} from "./mixins/flash"; -import {AxiosInstance} from "axios"; -import {Vue as VueType} from "vue/types/vue"; -import {ApplicationState} from "./store/types"; -import {Route} from "vue-router"; -// @ts-ignore -import {Ziggy} from './helpers/ziggy'; - -declare global { - interface Window { - X_CSRF_TOKEN: string, - _: any, - $: any, - jQuery: any, - axios: AxiosInstance, - events: VueType, - Ziggy: Ziggy, - } -} - -declare module 'vue/types/options' { - interface ComponentOptions { - $store?: Store, - $options?: { - sockets?: { - [s: string]: (data: any) => void, - } - }, - sockets?: { - [s: string]: (data: any) => void, - } - } -} - -declare module 'vue/types/vue' { - interface Vue { - $route: Route, - $store: Store, - $flash: FlashInterface, - route: (name: string, params?: object, absolute?: boolean) => string, - } -} diff --git a/resources/assets/scripts/router.ts b/resources/assets/scripts/router.ts deleted file mode 100644 index e5dedd594..000000000 --- a/resources/assets/scripts/router.ts +++ /dev/null @@ -1,80 +0,0 @@ -import VueRouter, {Route} from 'vue-router'; -import store from './store/index'; -import User from './models/user'; -// Base Vuejs Templates -import Login from './components/auth/Login.vue'; -import Dashboard from './components/dashboard/Dashboard.vue'; -import Account from './components/dashboard/Account.vue'; -import ResetPassword from './components/auth/ResetPassword.vue'; -import LoginForm from "@/components/auth/LoginForm.vue"; -import ForgotPassword from "@/components/auth/ForgotPassword.vue"; -import TwoFactorForm from "@/components/auth/TwoFactorForm.vue"; -import Server from "@/components/server/Server.vue"; -import ConsolePage from "@/components/server/subpages/Console.vue"; -import FileManagerPage from "@/components/server/subpages/FileManager.vue"; -import DatabasesPage from "@/components/server/subpages/Databases.vue"; - -const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default; - -const routes = [ - { - path: '/auth', component: Login, - children: [ - {name: 'login', path: 'login', component: LoginForm}, - {name: 'forgot-password', path: 'password', component: ForgotPassword}, - {name: 'checkpoint', path: 'checkpoint', component: TwoFactorForm}, - ] - }, - - { - name: 'reset-password', - path: '/auth/password/reset/:token', - component: ResetPassword, - props: function (route: Route) { - return {token: route.params.token, email: route.query.email || ''}; - }, - }, - - {name: 'dashboard', path: '/', component: Dashboard}, - {name: 'account', path: '/account', component: Account}, - {name: 'account.api', path: '/account/api', component: Account}, - {name: 'account.security', path: '/account/security', component: Account}, - - { - path: '/server/:id', component: Server, - children: [ - {name: 'server', path: '', component: ConsolePage}, - {name: 'server-files', path: 'files/:path(.*)?', component: FileManagerPage}, - // {name: 'server-subusers', path: 'subusers', component: ServerSubusers}, - // {name: 'server-schedules', path: 'schedules', component: ServerSchedules}, - {name: 'server-databases', path: 'databases', component: DatabasesPage}, - // {name: 'server-allocations', path: 'allocations', component: ServerAllocations}, - // {name: 'server-settings', path: 'settings', component: ServerSettings}, - ], - }, -]; - -const router = new VueRouter({ - mode: 'history', routes, -}); - -// Redirect the user to the login page if they try to access a protected route and -// have no JWT or the JWT is expired and wouldn't be accepted by the Panel. -router.beforeEach((to, from, next) => { - if (to.path === route('auth.logout')) { - return window.location = route('auth.logout'); - } - - const user = store.getters['auth/getUser']; - - // Check that if we're accessing a non-auth route that a user exists on the page. - if (!to.path.startsWith('/auth') && !(user instanceof User)) { - store.commit('auth/logout'); - return window.location = route('auth.logout'); - } - - // Continue on through the pipeline. - return next(); -}); - -export default router; diff --git a/resources/assets/scripts/store/index.ts b/resources/assets/scripts/store/index.ts deleted file mode 100644 index 045785fd4..000000000 --- a/resources/assets/scripts/store/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import auth from './modules/auth'; -import dashboard from './modules/dashboard'; -import server from './modules/server'; -import socket from './modules/socket'; -import {ApplicationState} from "./types"; - -Vue.use(Vuex); - -const store = new Vuex.Store({ - strict: process.env.NODE_ENV !== 'production', - modules: {auth, dashboard, server, socket}, -}); - -if (module.hot) { - module.hot.accept(['./modules/auth'], () => { - const newAuthModule = require('./modules/auth').default; - const newDashboardModule = require('./modules/dashboard').default; - const newServerModule = require('./modules/server').default; - const newSocketModule = require('./modules/socket').default; - - store.hotUpdate({ - modules: { - auth: newAuthModule, - dashboard: newDashboardModule, - server: newServerModule, - socket: newSocketModule - }, - }); - }); -} - -export default store; diff --git a/resources/assets/scripts/store/modules/auth.ts b/resources/assets/scripts/store/modules/auth.ts deleted file mode 100644 index 6fba4ec93..000000000 --- a/resources/assets/scripts/store/modules/auth.ts +++ /dev/null @@ -1,106 +0,0 @@ -import User, {UserData} from '../../models/user'; -import {ActionContext} from "vuex"; -import {AuthenticationState} from "../types"; - -const route = require('./../../../../../vendor/tightenco/ziggy/src/js/route').default; - -type LoginAction = { - user: string, - password: string, -} - -type UpdateEmailAction = { - email: string, - password: string, -} - -export default { - namespaced: true, - state: { - // @ts-ignore - user: typeof window.PterodactylUser === 'object' ? new User(window.PterodactylUser) : null, - }, - getters: { - /** - * Return the currently authenticated user. - */ - getUser: function (state: AuthenticationState): null | User { - return state.user; - }, - }, - setters: {}, - actions: { - /** - * Log a user into the Panel. - */ - login: ({commit}: ActionContext, {user, password}: LoginAction): Promise<{ - complete: boolean, - intended?: string, - token?: string, - }> => { - return new Promise((resolve, reject) => { - // @ts-ignore - window.axios.post(route('auth.login'), {user, password}) - // @ts-ignore - .then(response => { - commit('logout'); - - // If there is a 302 redirect or some other odd behavior (basically, response that isnt - // in JSON format) throw an error and don't try to continue with the login. - if (!(response.data instanceof Object)) { - return reject(new Error('An error was encountered while processing this request.')); - } - - if (response.data.complete) { - commit('login', response.data.user); - return resolve({ - complete: true, - intended: response.data.intended, - }); - } - - return resolve({ - complete: false, - token: response.data.login_token, - }); - }) - .catch(reject); - }); - }, - - /** - * Update a user's email address on the Panel and store the updated result in Vuex. - */ - updateEmail: function ({commit}: ActionContext, {email, password}: UpdateEmailAction): Promise { - return new Promise((resolve, reject) => { - // @ts-ignore - window.axios.put(route('api.client.account.update-email'), {email, password}) - // @ts-ignore - .then(response => { - // If there is a 302 redirect or some other odd behavior (basically, response that isnt - // in JSON format) throw an error and don't try to continue with the login. - if (!(response.data instanceof Object) && response.status !== 201) { - return reject(new Error('An error was encountered while processing this request.')); - } - - commit('setEmail', email); - return resolve(); - }) - .catch(reject); - }); - }, - }, - mutations: { - setEmail: function (state: AuthenticationState, email: string) { - if (state.user) { - state.user.email = email; - } - }, - login: function (state: AuthenticationState, data: UserData) { - state.user = new User(data); - }, - logout: function (state: AuthenticationState) { - state.user = null; - }, - }, -}; diff --git a/resources/assets/scripts/store/modules/dashboard.ts b/resources/assets/scripts/store/modules/dashboard.ts deleted file mode 100644 index 1ea907ea5..000000000 --- a/resources/assets/scripts/store/modules/dashboard.ts +++ /dev/null @@ -1,66 +0,0 @@ -import Server, {ServerData} from '../../models/server'; -import {ActionContext} from "vuex"; -import {DashboardState} from "../types"; - -const route = require('./../../../../../vendor/tightenco/ziggy/src/js/route').default; - -export default { - namespaced: true, - state: { - servers: [], - searchTerm: '', - }, - getters: { - getSearchTerm: function (state: DashboardState): string { - return state.searchTerm; - }, - }, - actions: { - /** - * Retrieve all of the servers for a user matching the query. - */ - loadServers: ({commit, state}: ActionContext): Promise => { - return new Promise((resolve, reject) => { - // @ts-ignore - window.axios.get(route('api.client.index'), { - params: {query: state.searchTerm}, - }) - // @ts-ignore - .then(response => { - // If there is a 302 redirect or some other odd behavior (basically, response that isnt - // in JSON format) throw an error and don't try to continue with the request processing. - if (!(response.data instanceof Object)) { - return reject(new Error('An error was encountered while processing this request.')); - } - - // Remove all of the existing servers. - commit('clearServers'); - - response.data.data.forEach((obj: { attributes: ServerData }) => { - commit('addServer', obj.attributes); - }); - - resolve(); - }) - .catch(reject); - }); - }, - - setSearchTerm: ({commit}: ActionContext, term: string) => { - commit('setSearchTerm', term); - }, - }, - mutations: { - addServer: function (state: DashboardState, data: ServerData) { - state.servers.push( - new Server(data) - ); - }, - clearServers: function (state: DashboardState) { - state.servers = []; - }, - setSearchTerm: function (state: DashboardState, term: string) { - state.searchTerm = term; - }, - }, -}; diff --git a/resources/assets/scripts/store/modules/server.ts b/resources/assets/scripts/store/modules/server.ts deleted file mode 100644 index abe435056..000000000 --- a/resources/assets/scripts/store/modules/server.ts +++ /dev/null @@ -1,93 +0,0 @@ -// @ts-ignore -import route from '../../../../../vendor/tightenco/ziggy/src/js/route'; -import {ActionContext} from "vuex"; -import {ServerData} from "@/models/server"; -import {ServerApplicationCredentials, ServerState} from "../types"; - -export default { - namespaced: true, - state: { - server: {}, - credentials: {node: '', key: ''}, - console: [], - fm: { - currentDirectory: '/', - }, - }, - getters: {}, - actions: { - /** - * Fetches the active server from the API and stores it in vuex. - */ - getServer: ({commit}: ActionContext, {server}: { server: string }): Promise => { - return new Promise((resolve, reject) => { - // @ts-ignore - window.axios.get(route('api.client.servers.view', {server})) - // @ts-ignore - .then(response => { - // If there is a 302 redirect or some other odd behavior (basically, response that isnt - // in JSON format) throw an error and don't try to continue with the login. - if (!(response.data instanceof Object)) { - return reject(new Error('An error was encountered while processing this request.')); - } - - if (response.data.object === 'server' && response.data.attributes) { - commit('SERVER_DATA', response.data.attributes) - } - - return resolve(); - }) - .catch(reject); - }); - }, - - /** - * Get authentication credentials that the client should use when connecting to the daemon to - * retrieve server information. - */ - getCredentials: ({commit}: ActionContext, {server}: { server: string }) => { - return new Promise((resolve, reject) => { - // @ts-ignore - window.axios.get(route('server.credentials', {server})) - // @ts-ignore - .then(response => { - // If there is a 302 redirect or some other odd behavior (basically, response that isnt - // in JSON format) throw an error and don't try to continue with the login. - if (!(response.data instanceof Object)) { - return reject(new Error('An error was encountered while processing this request.')); - } - - if (response.data.key) { - commit('SERVER_CREDENTIALS', response.data) - } - - return resolve(); - }) - .catch(reject); - }); - }, - - /** - * Update the last viewed directory for the server the user is currently viewing. This allows - * us to quickly navigate back to that directory, as well as ensure that actions taken are - * performed aganist the correct directory without having to pass that mess everywhere. - */ - updateCurrentDirectory: ({commit}: ActionContext, directory: string) => { - commit('SET_CURRENT_DIRECTORY', directory); - }, - }, - mutations: { - SET_CURRENT_DIRECTORY: function (state: ServerState, directory: string) { - state.fm.currentDirectory = directory; - }, - SERVER_DATA: function (state: ServerState, data: ServerData) { - state.server = data; - }, - SERVER_CREDENTIALS: function (state: ServerState, credentials: ServerApplicationCredentials) { - state.credentials = credentials; - }, - CONSOLE_DATA: function (state: ServerState, data: string) { - state.console.push(data); - }, - }, -} diff --git a/resources/assets/scripts/store/modules/socket.ts b/resources/assets/scripts/store/modules/socket.ts deleted file mode 100644 index a12e83d92..000000000 --- a/resources/assets/scripts/store/modules/socket.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Status from '../../helpers/statuses'; -import {SocketState} from "../types"; - -export default { - namespaced: true, - state: { - connected: false, - connectionError: false, - status: Status.STATUS_OFF, - outputBuffer: [], - }, - mutations: { - SOCKET_CONNECT: (state: SocketState) => { - state.connected = true; - state.connectionError = false; - }, - - SOCKET_ERROR: (state: SocketState, err: Error) => { - state.connected = false; - state.connectionError = err; - }, - - 'SOCKET_INITIAL STATUS': (state: SocketState, data: string) => { - state.status = data; - }, - - SOCKET_STATUS: (state: SocketState, data: string) => { - state.status = data; - }, - - 'SOCKET_CONSOLE OUTPUT': (state: SocketState, data: string) => { - const { outputBuffer } = state; - - if (outputBuffer.length >= 500) { - // Pop all of the output buffer items off the front until we only have 499 - // items in the array. - for (let i = 0; i <= (outputBuffer.length - 500); i++) { - outputBuffer.shift(); - i++; - } - } - - outputBuffer.push(data); - state.outputBuffer = outputBuffer; - }, - }, -}; diff --git a/resources/assets/scripts/store/types.ts b/resources/assets/scripts/store/types.ts deleted file mode 100644 index 505c68b51..000000000 --- a/resources/assets/scripts/store/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Server, {ServerData} from "../models/server"; -import User from "../models/user"; - -export type ApplicationState = { - socket: SocketState, - server: ServerState, - auth: AuthenticationState, - dashboard: DashboardState, -} - -export type SocketState = { - connected: boolean, - connectionError: boolean | Error, - status: string, - outputBuffer: string[], -} - -export type ServerApplicationCredentials = { - node: string, - key: string, -}; - -export type FileManagerState = { - currentDirectory: string, -} - -export type ServerState = { - server: ServerData, - credentials: ServerApplicationCredentials, - console: Array, - fm: FileManagerState, -}; - -export type DashboardState = { - searchTerm: string, - servers: Array, -}; - - -export type AuthenticationState = { - user: null | User, -} diff --git a/resources/assets/scripts/vue-shims.d.ts b/resources/assets/scripts/vue-shims.d.ts deleted file mode 100644 index b93ce2227..000000000 --- a/resources/assets/scripts/vue-shims.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.vue' { - import Vue from 'vue' - export default Vue -} diff --git a/resources/assets/styles/components/animations.css b/resources/styles/components/animations.css similarity index 100% rename from resources/assets/styles/components/animations.css rename to resources/styles/components/animations.css diff --git a/resources/assets/styles/components/authentication.css b/resources/styles/components/authentication.css similarity index 100% rename from resources/assets/styles/components/authentication.css rename to resources/styles/components/authentication.css diff --git a/resources/assets/styles/components/filemanager.css b/resources/styles/components/filemanager.css similarity index 100% rename from resources/assets/styles/components/filemanager.css rename to resources/styles/components/filemanager.css diff --git a/resources/assets/styles/components/forms.css b/resources/styles/components/forms.css similarity index 100% rename from resources/assets/styles/components/forms.css rename to resources/styles/components/forms.css diff --git a/resources/assets/styles/components/miscellaneous.css b/resources/styles/components/miscellaneous.css similarity index 100% rename from resources/assets/styles/components/miscellaneous.css rename to resources/styles/components/miscellaneous.css diff --git a/resources/assets/styles/components/modal.css b/resources/styles/components/modal.css similarity index 100% rename from resources/assets/styles/components/modal.css rename to resources/styles/components/modal.css diff --git a/resources/assets/styles/components/navigation.css b/resources/styles/components/navigation.css similarity index 100% rename from resources/assets/styles/components/navigation.css rename to resources/styles/components/navigation.css diff --git a/resources/assets/styles/components/notifications.css b/resources/styles/components/notifications.css similarity index 100% rename from resources/assets/styles/components/notifications.css rename to resources/styles/components/notifications.css diff --git a/resources/assets/styles/components/spinners.css b/resources/styles/components/spinners.css similarity index 100% rename from resources/assets/styles/components/spinners.css rename to resources/styles/components/spinners.css diff --git a/resources/assets/styles/components/typography.css b/resources/styles/components/typography.css similarity index 100% rename from resources/assets/styles/components/typography.css rename to resources/styles/components/typography.css diff --git a/resources/assets/styles/main.css b/resources/styles/main.css similarity index 100% rename from resources/assets/styles/main.css rename to resources/styles/main.css diff --git a/webpack.config.js b/webpack.config.js index 95138b14d..bd0774b65 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ const _ = require('lodash'); const path = require('path'); const tailwind = require('tailwindcss'); @@ -52,7 +53,7 @@ module.exports = { performance: { hints: false, }, - entry: ['./resources/assets/styles/main.css', './resources/scripts/index.tsx'], + entry: ['./resources/styles/main.css', './resources/scripts/index.tsx'], output: { path: path.resolve(__dirname, 'public/assets'), filename: isProduction ? 'bundle.[chunkhash:8].js' : 'bundle.[hash:8].js',