More socket and console improvements for server
This commit is contained in:
parent
f866ad5b34
commit
38d7985e66
7 changed files with 169 additions and 76 deletions
|
@ -8,6 +8,7 @@
|
||||||
"vue": "^2.5.7",
|
"vue": "^2.5.7",
|
||||||
"vue-axios": "^2.1.1",
|
"vue-axios": "^2.1.1",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
|
"vue-socket.io-extended": "^3.1.0",
|
||||||
"vuex": "^3.0.1",
|
"vuex": "^3.0.1",
|
||||||
"vuex-i18n": "^1.10.5",
|
"vuex-i18n": "^1.10.5",
|
||||||
"vuex-router-sync": "^5.0.0",
|
"vuex-router-sync": "^5.0.0",
|
||||||
|
|
|
@ -67,34 +67,30 @@
|
||||||
import Navigation from '../core/Navigation';
|
import Navigation from '../core/Navigation';
|
||||||
import ProgressBar from './components/ProgressBar';
|
import ProgressBar from './components/ProgressBar';
|
||||||
import {mapState} from 'vuex';
|
import {mapState} from 'vuex';
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import VueSocketio from 'vue-socket.io-extended';
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
import PowerButtons from './components/PowerButtons';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ProgressBar, Navigation, TerminalIcon, FolderIcon, UsersIcon,
|
PowerButtons, ProgressBar, Navigation,
|
||||||
CalendarIcon, DatabaseIcon, GlobeIcon, SettingsIcon
|
TerminalIcon, FolderIcon, UsersIcon, CalendarIcon, DatabaseIcon, GlobeIcon, SettingsIcon
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState('server', ['server', 'credentials']),
|
...mapState('server', ['server', 'credentials']),
|
||||||
|
...mapState('socket', ['connected', 'connectionError']),
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
this.loadServer();
|
this.loadServer();
|
||||||
|
|
||||||
this.$on('send-command', data => {
|
|
||||||
this.socket.emit('send command', data);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$on('send-initial-log', () => {
|
|
||||||
this.socket.emit('send server log');
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
socket: null,
|
|
||||||
loadingServerData: true,
|
loadingServerData: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -109,50 +105,17 @@
|
||||||
this.$store.dispatch('server/getCredentials', {server: this.$route.params.id})
|
this.$store.dispatch('server/getCredentials', {server: this.$route.params.id})
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
// Configure the socket.io implementation. This is a really ghetto way of handling things
|
||||||
|
// but all of these plugins assume you have some constant connection, which we don't.
|
||||||
|
const socket = io(`${this.credentials.node}/v1/ws/${this.server.uuid}`, {
|
||||||
|
query: `token=${this.credentials.key}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
Vue.use(VueSocketio, socket, { store: this.$store });
|
||||||
this.loadingServerData = false;
|
this.loadingServerData = false;
|
||||||
this.initalizeWebsocket();
|
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
},
|
},
|
||||||
|
|
||||||
initalizeWebsocket: function () {
|
|
||||||
this.socket = io(this.credentials.node + '/v1/ws/' + this.server.uuid, {
|
|
||||||
query: 'token=' + this.credentials.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on('error', this._socket_error);
|
|
||||||
this.socket.on('connect', this._socket_connect);
|
|
||||||
this.socket.on('status', this._socket_status);
|
|
||||||
this.socket.on('initial status', this._socket_status);
|
|
||||||
this.socket.on('server log', this._socket_serverLog);
|
|
||||||
this.socket.on('console', this._socket_consoleLine);
|
|
||||||
},
|
|
||||||
|
|
||||||
_socket_error: function (err) {
|
|
||||||
this.$emit('socket::error', {err});
|
|
||||||
},
|
|
||||||
|
|
||||||
_socket_connect: function () {
|
|
||||||
this.$emit('socket::connected');
|
|
||||||
},
|
|
||||||
|
|
||||||
_socket_status: function (data) {
|
|
||||||
this.$emit('socket::status', {data});
|
|
||||||
},
|
|
||||||
|
|
||||||
_socket_serverLog: function (data) {
|
|
||||||
data.split(/\n/g).forEach(item => {
|
|
||||||
this.$emit('console', item);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_socket_consoleLine: function (data) {
|
|
||||||
if(data.line) {
|
|
||||||
data.line.split(/\n/g).forEach(item => {
|
|
||||||
this.$emit('console', item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="connected">
|
||||||
|
<transition name="slide-fade" mode="out-in">
|
||||||
|
<button class="btn btn-green uppercase text-xs px-4 py-2"
|
||||||
|
v-if="status === statuses.STATUS_OFF"
|
||||||
|
v-on:click.prevent="sendPowerAction('start')"
|
||||||
|
>Start</button>
|
||||||
|
<div v-else>
|
||||||
|
<button class="btn btn-red uppercase text-xs px-4 py-2" v-on:click.prevent="sendPowerAction('stop')">Stop</button>
|
||||||
|
<button class="btn btn-secondary uppercase text-xs px-4 py-2" v-on:click.prevent="sendPowerAction('restart')">Restart</button>
|
||||||
|
<button class="btn btn-secondary uppercase text-xs px-4 py-2" v-on:click.prevent="sendPowerAction('kill')">Kill</button>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<div class="pt-2 text-xs text-grey-light">Connecting to node</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Status from './../../../helpers/statuses';
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'power-buttons',
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState('socket', ['connected', 'status']),
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
statuses: Status,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
sendPowerAction: function (action) {
|
||||||
|
this.$socket.emit('set status', action)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs font-mono">
|
<div class="text-xs font-mono">
|
||||||
<div class="rounded-t p-2 bg-black overflow-scroll w-full" style="min-height: 16rem;max-height:64rem;">
|
<div class="rounded-t p-2 bg-black overflow-scroll w-full" style="min-height: 16rem;max-height:64rem;">
|
||||||
<div v-if="loadingConsole">
|
<div v-if="!connected">
|
||||||
<div class="spinner spinner-xl mt-24"></div>
|
<div class="spinner spinner-xl mt-24"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2 text-grey-light" ref="terminal"></div>
|
<div class="mb-2 text-grey-light" ref="terminal"></div>
|
||||||
|
@ -27,36 +27,59 @@
|
||||||
<script>
|
<script>
|
||||||
import { Terminal } from 'xterm';
|
import { Terminal } from 'xterm';
|
||||||
import * as TerminalFit from 'xterm/lib/addons/fit/fit';
|
import * as TerminalFit from 'xterm/lib/addons/fit/fit';
|
||||||
import Status from './../../../helpers/statuses';
|
import {mapState} from 'vuex';
|
||||||
|
|
||||||
Terminal.applyAddon(TerminalFit);
|
Terminal.applyAddon(TerminalFit);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'console-page',
|
name: 'console-page',
|
||||||
|
computed: {
|
||||||
|
...mapState('socket', ['connected']),
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* Watch the connected variable and when it becomes true request the server logs.
|
||||||
|
*
|
||||||
|
* @param {Boolean} state
|
||||||
|
*/
|
||||||
|
connected: function (state) {
|
||||||
|
if (state) {
|
||||||
|
this.$socket.emit('send server log');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for specific socket.io emits from the server.
|
||||||
|
*/
|
||||||
|
sockets: {
|
||||||
|
'server log': function (data) {
|
||||||
|
data.split(/\n/g).forEach(line => {
|
||||||
|
this.terminal.writeln(line);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'console': function (data) {
|
||||||
|
data.line.split(/\n/g).forEach(line => {
|
||||||
|
this.terminal.writeln(line);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mount the component and setup all of the terminal actions. Also fetches the initial
|
* Mount the component and setup all of the terminal actions. Also fetches the initial
|
||||||
* logs from the server to populate into the terminal.
|
* logs from the server to populate into the terminal if the socket is connected. If the
|
||||||
|
* socket is not connected this will occur automatically when it connects.
|
||||||
*/
|
*/
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
this.$parent.$on('socket::connected', () => {
|
|
||||||
this.terminal.open(this.$refs.terminal);
|
this.terminal.open(this.$refs.terminal);
|
||||||
this.terminal.fit();
|
this.terminal.fit();
|
||||||
this.terminal.clear();
|
this.terminal.clear();
|
||||||
|
|
||||||
this.$parent.$emit('send-initial-log');
|
if (this.connected) {
|
||||||
});
|
this.$socket.emit('send server log');
|
||||||
|
|
||||||
this.$parent.$on('console', data => {
|
|
||||||
this.loadingConsole = false;
|
|
||||||
this.terminal.writeln(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$parent.$on('socket::status', s => {
|
|
||||||
if (s === Status.STATUS_OFF) {
|
|
||||||
this.loadingConsole = false;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function () {
|
data: function () {
|
||||||
|
@ -76,7 +99,6 @@
|
||||||
command: '',
|
command: '',
|
||||||
commandHistory: [],
|
commandHistory: [],
|
||||||
commandHistoryIndex: -1,
|
commandHistoryIndex: -1,
|
||||||
loadingConsole: true,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -87,7 +109,7 @@
|
||||||
sendCommand: function () {
|
sendCommand: function () {
|
||||||
this.commandHistoryIndex = -1;
|
this.commandHistoryIndex = -1;
|
||||||
this.commandHistory.unshift(this.command);
|
this.commandHistory.unshift(this.command);
|
||||||
this.$parent.$emit('send-command', this.command);
|
this.$socket.emit('send command', this.command);
|
||||||
this.command = '';
|
this.command = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,13 @@ import Vuex from 'vuex';
|
||||||
import auth from './modules/auth';
|
import auth from './modules/auth';
|
||||||
import dashboard from './modules/dashboard';
|
import dashboard from './modules/dashboard';
|
||||||
import server from './modules/server';
|
import server from './modules/server';
|
||||||
|
import socket from './modules/socket';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
strict: process.env.NODE_ENV !== 'production',
|
strict: process.env.NODE_ENV !== 'production',
|
||||||
modules: {auth, dashboard, server},
|
modules: {auth, dashboard, server, socket},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
|
@ -16,9 +17,15 @@ if (module.hot) {
|
||||||
const newAuthModule = require('./modules/auth').default;
|
const newAuthModule = require('./modules/auth').default;
|
||||||
const newDashboardModule = require('./modules/dashboard').default;
|
const newDashboardModule = require('./modules/dashboard').default;
|
||||||
const newServerModule = require('./modules/server').default;
|
const newServerModule = require('./modules/server').default;
|
||||||
|
const newSocketModule = require('./modules/socket').default;
|
||||||
|
|
||||||
store.hotUpdate({
|
store.hotUpdate({
|
||||||
modules: {newAuthModule, newDashboardModule, newServerModule},
|
modules: {
|
||||||
|
auth: newAuthModule,
|
||||||
|
dashboard: newDashboardModule,
|
||||||
|
server: newServerModule,
|
||||||
|
socket: newSocketModule
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
29
resources/assets/scripts/store/modules/socket.js
Normal file
29
resources/assets/scripts/store/modules/socket.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import Status from './../../helpers/statuses';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
connected: false,
|
||||||
|
connectionError: null,
|
||||||
|
status: Status.STATUS_OFF,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SOCKET_CONNECT: (state) => {
|
||||||
|
state.connected = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
SOCKET_ERROR: (state, err) => {
|
||||||
|
state.connectionError = err;
|
||||||
|
},
|
||||||
|
|
||||||
|
'SOCKET_INITIAL STATUS': (state, data) => {
|
||||||
|
state.status = data.status;
|
||||||
|
},
|
||||||
|
|
||||||
|
SOCKET_STATUS: (state, data) => {
|
||||||
|
state.status = data.status;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
10
yarn.lock
10
yarn.lock
|
@ -1490,6 +1490,10 @@ camelcase@^4.0.0, camelcase@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||||
|
|
||||||
|
camelcase@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
|
||||||
|
|
||||||
caniuse-api@^1.5.2:
|
caniuse-api@^1.5.2:
|
||||||
version "1.6.1"
|
version "1.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
|
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
|
||||||
|
@ -6876,6 +6880,12 @@ vue-router@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
|
||||||
|
|
||||||
|
vue-socket.io-extended@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-socket.io-extended/-/vue-socket.io-extended-3.1.0.tgz#0c1833377f902381c861c43a403a476eee542bc9"
|
||||||
|
dependencies:
|
||||||
|
camelcase "^5.0.0"
|
||||||
|
|
||||||
vue-style-loader@^4.0.1:
|
vue-style-loader@^4.0.1:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.0.tgz#7588bd778e2c9f8d87bfc3c5a4a039638da7a863"
|
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.0.tgz#7588bd778e2c9f8d87bfc3c5a4a039638da7a863"
|
||||||
|
|
Loading…
Reference in a new issue