mirror of
https://github.com/pterodactyl/panel.git
synced 2024-11-22 17:12:30 +01:00
Merge branch 'feature/vuejs-serverlist' into feature/vue-serverview
This commit is contained in:
commit
378a1859cf
@ -35,7 +35,9 @@ class ClientController extends ClientApiController
|
||||
*/
|
||||
public function index(GetServersRequest $request): array
|
||||
{
|
||||
$servers = $this->repository->filterUserAccessServers($request->user(), User::FILTER_LEVEL_SUBUSER);
|
||||
$servers = $this->repository
|
||||
->setSearchTerm($request->input('query'))
|
||||
->filterUserAccessServers($request->user(), User::FILTER_LEVEL_ALL);
|
||||
|
||||
return $this->fractal->collection($servers)
|
||||
->transformWith($this->getTransformer(ServerTransformer::class))
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Lcobucci\JWT\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
|
||||
class LoginController extends AbstractLoginController
|
||||
@ -63,11 +65,26 @@ class LoginController extends AbstractLoginController
|
||||
'request_ip' => $request->ip(),
|
||||
], 5);
|
||||
|
||||
return response()->json(['complete' => false, 'token' => $token]);
|
||||
return response()->json(['complete' => false, 'login_token' => $token]);
|
||||
}
|
||||
|
||||
$signer = new Sha256();
|
||||
$token = (new Builder)->setIssuer('http://pterodactyl.local')
|
||||
->setAudience('http://pterodactyl.local')
|
||||
->setId(str_random(12), true)
|
||||
->setIssuedAt(time())
|
||||
->setNotBefore(time())
|
||||
->setExpiration(time() + 3600)
|
||||
->set('uid', $user->id)
|
||||
->sign($signer, env('APP_JWT_KEY'))
|
||||
->getToken();
|
||||
|
||||
$this->auth->guard()->login($user, true);
|
||||
|
||||
return response()->json(['complete' => true]);
|
||||
return response()->json([
|
||||
'complete' => true,
|
||||
'intended' => $this->redirectPath(),
|
||||
'token' => $token->__toString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Pterodactyl\Http\Middleware\Api;
|
||||
|
||||
use Closure;
|
||||
use Lcobucci\JWT\Parser;
|
||||
use Cake\Chronos\Chronos;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\ApiKey;
|
||||
@ -63,6 +64,23 @@ class AuthenticateKey
|
||||
}
|
||||
|
||||
$raw = $request->bearerToken();
|
||||
|
||||
// This is an internal JWT, treat it differently to get the correct user
|
||||
// before passing it along.
|
||||
if (strlen($raw) > ApiKey::IDENTIFIER_LENGTH + ApiKey::KEY_LENGTH) {
|
||||
$token = (new Parser)->parse($raw);
|
||||
|
||||
$model = (new ApiKey)->fill([
|
||||
'user_id' => $token->getClaim('uid'),
|
||||
'key_type' => ApiKey::TYPE_ACCOUNT,
|
||||
]);
|
||||
|
||||
$this->auth->guard()->loginUsingId($token->getClaim('uid'));
|
||||
$request->attributes->set('api_key', $model);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$identifier = substr($raw, 0, ApiKey::IDENTIFIER_LENGTH);
|
||||
$token = substr($raw, ApiKey::IDENTIFIER_LENGTH);
|
||||
|
||||
|
@ -4,7 +4,6 @@ namespace Pterodactyl\Http\Middleware\Api;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Barryvdh\Debugbar\LaravelDebugbar;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
|
||||
@ -41,10 +40,6 @@ class SetSessionDriver
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if ($this->config->get('app.debug')) {
|
||||
$this->app->make(LaravelDebugbar::class)->disable();
|
||||
}
|
||||
|
||||
$this->config->set('session.driver', 'array');
|
||||
|
||||
return $next($request);
|
||||
|
@ -28,7 +28,12 @@ class ServerTransformer extends BaseClientTransformer
|
||||
'identifier' => $server->uuidShort,
|
||||
'uuid' => $server->uuid,
|
||||
'name' => $server->name,
|
||||
'node' => $server->node->name,
|
||||
'description' => $server->description,
|
||||
'allocation' => [
|
||||
'ip' => $server->allocation->alias,
|
||||
'port' => $server->allocation->port,
|
||||
],
|
||||
'limits' => [
|
||||
'memory' => $server->memory,
|
||||
'swap' => $server->swap,
|
||||
|
@ -26,6 +26,7 @@
|
||||
"laracasts/utilities": "^3.0",
|
||||
"laravel/framework": "5.6.*",
|
||||
"laravel/tinker": "^1.0",
|
||||
"lcobucci/jwt": "^3.2",
|
||||
"lord/laroute": "^2.4",
|
||||
"matriphe/iso-639": "^1.2",
|
||||
"nesbot/carbon": "^1.22",
|
||||
|
60
composer.lock
generated
60
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9232ff40da15c9430731254edc662eb7",
|
||||
"content-hash": "f84af54d009a128472ca7e19a50fccf8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "appstract/laravel-blade-directives",
|
||||
@ -1569,6 +1569,64 @@
|
||||
],
|
||||
"time": "2018-05-17T13:42:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/jwt",
|
||||
"version": "3.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lcobucci/jwt.git",
|
||||
"reference": "0b5930be73582369e10c4d4bb7a12bac927a203c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/0b5930be73582369e10c4d4bb7a12bac927a203c",
|
||||
"reference": "0b5930be73582369e10c4d4bb7a12bac927a203c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"mdanter/ecc": "~0.3.1",
|
||||
"mikey179/vfsstream": "~1.5",
|
||||
"phpmd/phpmd": "~2.2",
|
||||
"phpunit/php-invoker": "~1.1",
|
||||
"phpunit/phpunit": "~4.5",
|
||||
"squizlabs/php_codesniffer": "~2.3"
|
||||
},
|
||||
"suggest": {
|
||||
"mdanter/ecc": "Required to use Elliptic Curves based algorithms."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lcobucci\\JWT\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Luís Otávio Cobucci Oblonczyk",
|
||||
"email": "lcobucci@gmail.com",
|
||||
"role": "developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
|
||||
"keywords": [
|
||||
"JWS",
|
||||
"jwt"
|
||||
],
|
||||
"time": "2017-09-01T08:23:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "1.0.45",
|
||||
|
49
package-lock.json
generated
49
package-lock.json
generated
@ -6122,6 +6122,12 @@
|
||||
"integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=",
|
||||
"dev": true
|
||||
},
|
||||
"jwt-decode": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
|
||||
"integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
@ -7006,6 +7012,12 @@
|
||||
"integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=",
|
||||
"dev": true
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.22.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz",
|
||||
"integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==",
|
||||
"dev": true
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
@ -11075,12 +11087,24 @@
|
||||
"spdx-expression-parse": "3.0.0"
|
||||
}
|
||||
},
|
||||
"validator": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz",
|
||||
"integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==",
|
||||
"dev": true
|
||||
},
|
||||
"value-or-function": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
|
||||
"integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
|
||||
"dev": true
|
||||
},
|
||||
"vee-validate": {
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-2.0.9.tgz",
|
||||
"integrity": "sha512-0qA3hrpF2jIBoEReWF8YkvG1ukJVS56+oyPTxOtb2OfB5d7iUuQiyboOOpXOvOViREHNXTsIcQ5XIQOMBff/wg==",
|
||||
"dev": true
|
||||
},
|
||||
"vendors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
|
||||
@ -11241,6 +11265,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-mc": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-mc/-/vue-mc-0.2.4.tgz",
|
||||
"integrity": "sha1-k1actuCOLRxSlop0zOimssm9pmo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"axios": "0.16.2",
|
||||
"lodash": "4.17.5",
|
||||
"moment": "2.22.1",
|
||||
"validator": "8.2.0",
|
||||
"vue": "2.5.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz",
|
||||
"integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.0",
|
||||
"is-buffer": "1.1.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.1.tgz",
|
||||
|
@ -24,16 +24,19 @@
|
||||
"gulp-rev": "^8.1.1",
|
||||
"gulp-uglify-es": "^1.0.1",
|
||||
"jquery": "^3.3.1",
|
||||
"jwt-decode": "^2.2.0",
|
||||
"lodash": "^4.17.5",
|
||||
"postcss": "^6.0.21",
|
||||
"postcss-import": "^11.1.0",
|
||||
"postcss-preset-env": "^3.4.0",
|
||||
"postcss-scss": "^1.0.4",
|
||||
"tailwindcss": "^0.5.1",
|
||||
"vee-validate": "^2.0.9",
|
||||
"vue": "^2.5.7",
|
||||
"vue-axios": "^2.1.1",
|
||||
"vue-devtools": "^3.1.9",
|
||||
"vue-loader": "^14.2.2",
|
||||
"vue-mc": "^0.2.4",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-template-compiler": "^2.5.16",
|
||||
"vueify-insert-css": "^1.0.0",
|
||||
|
@ -13,19 +13,14 @@ import faSolid from '@fortawesome/fontawesome-free-solid';
|
||||
import FontAwesomeIcon from '@fortawesome/vue-fontawesome';
|
||||
fontawesome.library.add(faSolid);
|
||||
|
||||
// Base Vuejs Templates
|
||||
import Login from './components/auth/Login';
|
||||
import Dashboard from './components/dashboard/Dashboard';
|
||||
import Account from './components/dashboard/Account';
|
||||
import ResetPassword from './components/auth/ResetPassword';
|
||||
import { Server, ServerConsole, ServerAllocations, ServerDatabases, ServerFiles, ServerSchedules, ServerSettings, ServerSubusers } from './components/server';
|
||||
import { routes } from './routes';
|
||||
import { storeData } from './store';
|
||||
|
||||
window.events = new Vue;
|
||||
window.Ziggy = Ziggy;
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const store = new Vuex.Store();
|
||||
const store = new Vuex.Store(storeData);
|
||||
const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default;
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
@ -41,35 +36,7 @@ Vue.i18n.set('en');
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes: [
|
||||
{ name: 'login', path: '/auth/login', component: Login },
|
||||
{ name: 'forgot-password', path: '/auth/password', component: Login },
|
||||
{ name: 'checkpoint', path: '/checkpoint', component: Login },
|
||||
{
|
||||
name: 'reset-password',
|
||||
path: '/auth/password/reset/:token',
|
||||
component: ResetPassword,
|
||||
props: function (route) {
|
||||
return { token: route.params.token, email: route.query.email || '' };
|
||||
}
|
||||
},
|
||||
{ name : 'index', 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: ServerConsole },
|
||||
{ name: 'server-files', path: 'files', component: ServerFiles },
|
||||
{ name: 'server-subusers', path: 'subusers', component: ServerSubusers },
|
||||
{ name: 'server-schedules', path: 'schedules', component: ServerSchedules },
|
||||
{ name: 'server-databases', path: 'databases', component: ServerDatabases },
|
||||
{ name: 'server-allocations', path: 'allocations', component: ServerAllocations },
|
||||
{ name: 'server-settings', path: 'settings', component: ServerSettings },
|
||||
]
|
||||
}
|
||||
]
|
||||
mode: 'history', routes
|
||||
});
|
||||
|
||||
require('./bootstrap');
|
||||
|
9
resources/assets/scripts/bootstrap.js
vendored
9
resources/assets/scripts/bootstrap.js
vendored
@ -19,6 +19,15 @@ try {
|
||||
window.axios = require('axios');
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.token || '';
|
||||
|
||||
if (typeof phpdebugbar !== 'undefined') {
|
||||
window.axios.interceptors.response.use(function (response) {
|
||||
phpdebugbar.ajaxHandler.handle(response.request);
|
||||
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Next we will register the CSRF Token as a common header with Axios so that
|
||||
|
@ -82,16 +82,20 @@
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.data.complete) {
|
||||
return window.location = '/';
|
||||
localStorage.setItem('token', response.data.token);
|
||||
self.$store.dispatch('login');
|
||||
return window.location = response.data.intended;
|
||||
}
|
||||
|
||||
self.$props.user.password = '';
|
||||
self.$data.showSpinner = false;
|
||||
self.$router.push({name: 'checkpoint', query: {token: response.data.token}});
|
||||
self.$router.push({name: 'checkpoint', query: {token: response.data.login_token}});
|
||||
})
|
||||
.catch(function (err) {
|
||||
self.$props.user.password = '';
|
||||
self.$data.showSpinner = false;
|
||||
self.$store.dispatch('logout');
|
||||
|
||||
if (!err.response) {
|
||||
return console.error(err);
|
||||
}
|
||||
|
@ -49,9 +49,12 @@
|
||||
authentication_code: this.$data.code,
|
||||
})
|
||||
.then(function (response) {
|
||||
localStorage.setItem('token', response.data.token);
|
||||
self.$store.dispatch('login');
|
||||
window.location = response.data.intended;
|
||||
})
|
||||
.catch(function (err) {
|
||||
self.$store.dispatch('logout');
|
||||
if (!err.response) {
|
||||
return console.error(err);
|
||||
}
|
||||
|
@ -7,11 +7,19 @@
|
||||
ref="search"
|
||||
/>
|
||||
</div>
|
||||
<transition-group class="w-full m-auto mt-4 animate fadein sm:flex flex-wrap content-start">
|
||||
<router-link class="server-box" :to="{name: 'server', params: { id: server.uuidShort }}" :key="index" v-for="(server, index) in servers">
|
||||
<div class="content">
|
||||
<div class="float-right">
|
||||
<div class="indicator online"></div>
|
||||
<transition-group class="w-full m-auto mt-4 animate fadein sm:flex flex-wrap content-start"><div class="server-box" :key="index" v-for="(server, index) in servers.models">
|
||||
<router-link :to="{ name: 'server', params: { id: server.identifier }}" class="content">
|
||||
<div class="float-right">
|
||||
<div class="indicator"></div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-black font-bold text-xl">
|
||||
{{ server.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-0 flex">
|
||||
<div class="usage">
|
||||
<div class="indicator-title">CPU</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-black font-bold text-xl">{{ server.name }}</div>
|
||||
@ -34,19 +42,21 @@
|
||||
<span class="font-light text-sm">Mb</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="text-sm">
|
||||
<p class="text-grey">{{ server.node_name }}</p>
|
||||
<p class="text-grey-dark">{{ server.allocation.ip }}:{{ server.allocation.port }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="text-sm">
|
||||
<p class="text-grey">{{ server.node }}</p>
|
||||
<p class="text-grey-dark">{{ server.allocation.ip }}:{{ server.allocation.port }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</router-link>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ServerCollection } from '../../models/server';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
@ -54,7 +64,7 @@
|
||||
data: function () {
|
||||
return {
|
||||
search: '',
|
||||
servers: [],
|
||||
servers: new ServerCollection,
|
||||
}
|
||||
},
|
||||
|
||||
@ -69,15 +79,16 @@
|
||||
* @param {string} query
|
||||
*/
|
||||
loadServers: function (query = '') {
|
||||
const self = this;
|
||||
|
||||
window.axios.get(this.route('dashboard.servers'), {
|
||||
window.axios.get(this.route('api.client.index'), {
|
||||
params: { query },
|
||||
})
|
||||
.then(function (response) {
|
||||
self.servers = response.data;
|
||||
.then(response => {
|
||||
this.servers = new ServerCollection;
|
||||
response.data.data.forEach(obj => {
|
||||
this.servers.add(obj.attributes);
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
|
19
resources/assets/scripts/models/allocation.js
Normal file
19
resources/assets/scripts/models/allocation.js
Normal file
@ -0,0 +1,19 @@
|
||||
const Allocation = function () {
|
||||
this.ip = null;
|
||||
this.port = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a new allocation model.
|
||||
*
|
||||
* @param obj
|
||||
* @returns {Allocation}
|
||||
*/
|
||||
Allocation.prototype.fill = function (obj) {
|
||||
this.ip = obj.ip || null;
|
||||
this.port = obj.port || null;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
export default Allocation;
|
114
resources/assets/scripts/models/server.js
Normal file
114
resources/assets/scripts/models/server.js
Normal file
@ -0,0 +1,114 @@
|
||||
import { Collection, Model } from 'vue-mc';
|
||||
|
||||
/**
|
||||
* A generic server model used throughout the code base.
|
||||
*/
|
||||
export class Server extends Model {
|
||||
/**
|
||||
* Identifier the primary identifier for this model.
|
||||
*
|
||||
* @returns {{identifier: string}}
|
||||
*/
|
||||
static options() {
|
||||
return {
|
||||
identifier: 'identifier',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the defaults for this model.
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static defaults() {
|
||||
return {
|
||||
uuid: null,
|
||||
identifier: null,
|
||||
name: '',
|
||||
description: '',
|
||||
node: '',
|
||||
limits: {
|
||||
memory: 0,
|
||||
swap: 0,
|
||||
disk: 0,
|
||||
io: 0,
|
||||
cpu: 0,
|
||||
},
|
||||
allocation: {
|
||||
ip: null,
|
||||
port: null,
|
||||
},
|
||||
feature_limits: {
|
||||
databases: 0,
|
||||
allocations: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutations to apply to items in this model.
|
||||
*
|
||||
* @returns {{name: StringConstructor, description: StringConstructor}}
|
||||
*/
|
||||
static mutations() {
|
||||
return {
|
||||
uuid: String,
|
||||
identifier: String,
|
||||
name: String,
|
||||
description: String,
|
||||
node: String,
|
||||
limits: {
|
||||
memory: Number,
|
||||
swap: Number,
|
||||
disk: Number,
|
||||
io: Number,
|
||||
cpu: Number,
|
||||
},
|
||||
allocation: {
|
||||
ip: String,
|
||||
port: Number,
|
||||
},
|
||||
feature_limits: {
|
||||
databases: Number,
|
||||
allocations: Number,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes to use when building models.
|
||||
*
|
||||
* @returns {{fetch: string}}
|
||||
*/
|
||||
static routes() {
|
||||
return {
|
||||
fetch: '/api/client/servers/{identifier}',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerCollection extends Collection {
|
||||
static model() {
|
||||
return Server;
|
||||
}
|
||||
|
||||
static defaults() {
|
||||
return {
|
||||
orderBy: identifier,
|
||||
};
|
||||
}
|
||||
|
||||
static routes() {
|
||||
return {
|
||||
fetch: '/api/client',
|
||||
};
|
||||
}
|
||||
|
||||
get todo() {
|
||||
return this.sum('done');
|
||||
}
|
||||
|
||||
get done() {
|
||||
return this.todo === 0;
|
||||
}
|
||||
}
|
33
resources/assets/scripts/models/user.js
Normal file
33
resources/assets/scripts/models/user.js
Normal file
@ -0,0 +1,33 @@
|
||||
import JwtDecode from 'jwt-decode';
|
||||
|
||||
const User = function () {
|
||||
this.id = 0;
|
||||
this.admin = false;
|
||||
this.email = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a new instance of the user model using a JWT.
|
||||
*
|
||||
* @param {string} token
|
||||
* @returns {User}
|
||||
*/
|
||||
User.prototype.fromJwt = function (token) {
|
||||
return this.newModel(JwtDecode(token));
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an instance of this user model with the properties set on it.
|
||||
*
|
||||
* @param {object} obj
|
||||
* @returns {User}
|
||||
*/
|
||||
User.prototype.newModel = function (obj) {
|
||||
this.id = obj.id;
|
||||
this.admin = obj.admin;
|
||||
this.email = obj.email;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
export default User;
|
46
resources/assets/scripts/routes.js
Normal file
46
resources/assets/scripts/routes.js
Normal file
@ -0,0 +1,46 @@
|
||||
// Base Vuejs Templates
|
||||
import Login from './components/auth/Login';
|
||||
import Dashboard from './components/dashboard/Dashboard';
|
||||
import Account from './components/dashboard/Account';
|
||||
import ResetPassword from './components/auth/ResetPassword';
|
||||
import { Server, ServerConsole, ServerAllocations, ServerDatabases, ServerFiles, ServerSchedules, ServerSettings, ServerSubusers } from './components/server';
|
||||
|
||||
import {
|
||||
Server, ServerAllocations, ServerConsole,
|
||||
ServerDatabases,
|
||||
ServerFiles,
|
||||
ServerSchedules,
|
||||
ServerSettings,
|
||||
ServerSubusers
|
||||
} from "./components/server";
|
||||
|
||||
export const routes = [
|
||||
{ name: 'login', path: '/auth/login', component: Login },
|
||||
{ name: 'forgot-password', path: '/auth/password', component: Login },
|
||||
{ name: 'checkpoint', path: '/checkpoint', component: Login },
|
||||
{
|
||||
name: 'reset-password',
|
||||
path: '/auth/password/reset/:token',
|
||||
component: ResetPassword,
|
||||
props: function (route) {
|
||||
return { token: route.params.token, email: route.query.email || '' };
|
||||
}
|
||||
},
|
||||
|
||||
{ name : 'index', 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: ServerConsole },
|
||||
{ name: 'server-files', path: 'files', component: ServerFiles },
|
||||
{ name: 'server-subusers', path: 'subusers', component: ServerSubusers },
|
||||
{ name: 'server-schedules', path: 'schedules', component: ServerSchedules },
|
||||
{ name: 'server-databases', path: 'databases', component: ServerDatabases },
|
||||
{ name: 'server-allocations', path: 'allocations', component: ServerAllocations },
|
||||
{ name: 'server-settings', path: 'settings', component: ServerSettings },
|
||||
]
|
||||
}
|
||||
];
|
28
resources/assets/scripts/store.js
Normal file
28
resources/assets/scripts/store.js
Normal file
@ -0,0 +1,28 @@
|
||||
import User from './models/user';
|
||||
|
||||
export const storeData = {
|
||||
state: {
|
||||
user: null,
|
||||
},
|
||||
actions: {
|
||||
login: function ({ commit }) {
|
||||
commit('login');
|
||||
},
|
||||
logout: function ({ commit }) {
|
||||
commit('logout');
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
user: function (state) {
|
||||
return state.user;
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
login: function (state) {
|
||||
state.user = new User().fromJwt(localStorage.getItem('token'));
|
||||
},
|
||||
logout: function (state) {
|
||||
state.user = null;
|
||||
}
|
||||
}
|
||||
};
|
@ -65,7 +65,11 @@ code {
|
||||
}
|
||||
|
||||
& > .content {
|
||||
@apply .border .border-grey-light .bg-white .rounded .p-4 .justify-between .leading-normal;
|
||||
@apply .border .border-grey-light .bg-white .rounded .p-4 .justify-between .leading-normal .no-underline .block .text-black;
|
||||
|
||||
&:visited {
|
||||
@apply .text-black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
45
yarn.lock
45
yarn.lock
@ -357,6 +357,13 @@ aws4@^1.2.1:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
|
||||
|
||||
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"
|
||||
@ -2152,6 +2159,12 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "^2.0.4"
|
||||
|
||||
follow-redirects@^1.2.3:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.0.tgz#234f49cf770b7f35b40e790f636ceba0c3a0ab77"
|
||||
dependencies:
|
||||
debug "^3.1.0"
|
||||
|
||||
follow-redirects@^1.3.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.4.1.tgz#d8120f4518190f55aac65bb6fc7b85fcd666d6aa"
|
||||
@ -3141,6 +3154,10 @@ just-debounce@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea"
|
||||
|
||||
jwt-decode@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79"
|
||||
|
||||
kind-of@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44"
|
||||
@ -3379,6 +3396,10 @@ lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0:
|
||||
version "4.17.5"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
|
||||
|
||||
lodash@^4.17:
|
||||
version "4.17.10"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
||||
|
||||
longest@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
@ -3610,6 +3631,10 @@ modify-filename@^1.0.0, modify-filename@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1"
|
||||
|
||||
moment@^2.18.1:
|
||||
version "2.22.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad"
|
||||
|
||||
move-concurrently@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||
@ -5855,10 +5880,18 @@ 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"
|
||||
|
||||
value-or-function@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813"
|
||||
|
||||
vee-validate@^2.0.9:
|
||||
version "2.0.9"
|
||||
resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.0.9.tgz#948a96572d9e2369d5cb217a84269ecf6c1f9a11"
|
||||
|
||||
vendors@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
|
||||
@ -5974,6 +6007,16 @@ vue-loader@^14.2.2:
|
||||
vue-style-loader "^4.0.1"
|
||||
vue-template-es2015-compiler "^1.6.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"
|
||||
@ -5996,7 +6039,7 @@ vue-template-es2015-compiler@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
|
||||
|
||||
vue@^2.5.7:
|
||||
vue@^2.2, vue@^2.5.7:
|
||||
version "2.5.16"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user