Change socket implementation for servers
This commit is contained in:
parent
e0fda5865d
commit
dc52e238ac
9 changed files with 242 additions and 17 deletions
|
@ -70,18 +70,19 @@
|
|||
import Navigation from '../core/Navigation';
|
||||
import ProgressBar from './components/ProgressBar';
|
||||
import { mapState } from 'vuex';
|
||||
import VueSocketio from 'vue-socket.io-extended';
|
||||
import io from 'socket.io-client';
|
||||
import Vue from 'vue';
|
||||
import { Socketio } from './../../mixins/socketio';
|
||||
|
||||
import PowerButtons from './components/PowerButtons';
|
||||
import Flash from '../Flash';
|
||||
|
||||
export default {
|
||||
name: 'server',
|
||||
mixins: [Socketio],
|
||||
components: {
|
||||
Flash,
|
||||
PowerButtons, ProgressBar, Navigation,
|
||||
TerminalIcon, FolderIcon, UsersIcon, CalendarIcon, DatabaseIcon, GlobeIcon, SettingsIcon
|
||||
TerminalIcon, FolderIcon, UsersIcon, CalendarIcon, DatabaseIcon, GlobeIcon, SettingsIcon,
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -95,10 +96,20 @@
|
|||
};
|
||||
},
|
||||
|
||||
sockets: {
|
||||
'console': function () {
|
||||
console.log('server CONSOLE');
|
||||
},
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
this.loadServer();
|
||||
},
|
||||
|
||||
beforeDestroy: function () {
|
||||
this.removeSocket();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Load the core server information needed for these pages to be functional.
|
||||
|
@ -115,7 +126,7 @@
|
|||
query: `token=${this.credentials.key}`,
|
||||
});
|
||||
|
||||
Vue.use(VueSocketio, socket, { store: this.$store });
|
||||
this.$socket().connect(socket);
|
||||
this.loadingServerData = false;
|
||||
})
|
||||
.catch(console.error);
|
||||
|
|
|
@ -24,11 +24,12 @@
|
|||
|
||||
<script>
|
||||
import Status from './../../../helpers/statuses';
|
||||
import { Socketio } from './../../../mixins/socketio';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'power-buttons',
|
||||
|
||||
mixins: [Socketio],
|
||||
computed: {
|
||||
...mapState('socket', ['connected', 'status']),
|
||||
},
|
||||
|
@ -41,7 +42,7 @@
|
|||
|
||||
methods: {
|
||||
sendPowerAction: function (action) {
|
||||
this.$socket.emit('set status', action)
|
||||
this.$socket().instance().emit('set status', action)
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -28,10 +28,12 @@
|
|||
import { Terminal } from 'xterm';
|
||||
import * as TerminalFit from 'xterm/lib/addons/fit/fit';
|
||||
import {mapState} from 'vuex';
|
||||
import {Socketio} from './../../../mixins/socketio';
|
||||
|
||||
Terminal.applyAddon(TerminalFit);
|
||||
|
||||
export default {
|
||||
mixins: [Socketio],
|
||||
name: 'console-page',
|
||||
computed: {
|
||||
...mapState('socket', ['connected']),
|
||||
|
@ -103,7 +105,7 @@
|
|||
this.terminal.fit();
|
||||
this.terminal.clear();
|
||||
|
||||
this.$socket.emit('send server log');
|
||||
this.$socket().instance().emit('send server log');
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -112,7 +114,7 @@
|
|||
sendCommand: function () {
|
||||
this.commandHistoryIndex = -1;
|
||||
this.commandHistory.unshift(this.command);
|
||||
this.$socket.emit('send command', this.command);
|
||||
this.$socket().instance().emit('send command', this.command);
|
||||
this.command = '';
|
||||
},
|
||||
|
||||
|
|
103
resources/assets/scripts/mixins/socketio/connector.js
Normal file
103
resources/assets/scripts/mixins/socketio/connector.js
Normal file
|
@ -0,0 +1,103 @@
|
|||
import io from 'socket.io-client';
|
||||
import camelCase from 'camelcase';
|
||||
import SocketEmitter from './emitter';
|
||||
|
||||
const SYSTEM_EVENTS = [
|
||||
'connect',
|
||||
'error',
|
||||
'disconnect',
|
||||
'reconnect',
|
||||
'reconnect_attempt',
|
||||
'reconnecting',
|
||||
'reconnect_error',
|
||||
'reconnect_failed',
|
||||
'connect_error',
|
||||
'connect_timeout',
|
||||
'connecting',
|
||||
'ping',
|
||||
'pong',
|
||||
];
|
||||
|
||||
export default class SocketioConnector {
|
||||
constructor (store = null) {
|
||||
this.socket = null;
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new Socket connection.
|
||||
*
|
||||
* @param {io} socket
|
||||
*/
|
||||
connect (socket) {
|
||||
if (!socket instanceof io) {
|
||||
throw new Error('First argument passed to connect() should be an instance of socket.io-client.');
|
||||
}
|
||||
|
||||
this.socket = socket;
|
||||
this.registerEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the socket instance we are working with.
|
||||
*
|
||||
* @return {io|null}
|
||||
*/
|
||||
instance () {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the event listeners for this socket including user-defined ones in the store as
|
||||
* well as global system events from Socekt.io.
|
||||
*/
|
||||
registerEventListeners () {
|
||||
this.socket['onevent'] = (packet) => {
|
||||
const [event, ...args] = packet.data;
|
||||
SocketEmitter.emit(event, ...args);
|
||||
this.passToStore(event, args);
|
||||
};
|
||||
|
||||
SYSTEM_EVENTS.forEach((event) => {
|
||||
this.socket.on(event, (payload) => {
|
||||
SocketEmitter.emit(event, payload);
|
||||
this.passToStore(event, payload);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass event calls off to the Vuex store if there is a corresponding function.
|
||||
*
|
||||
* @param {String|Number|Symbol} event
|
||||
* @param {Array} payload
|
||||
*/
|
||||
passToStore (event, payload) {
|
||||
if (!this.store) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mutation = `SOCKET_${event.toUpperCase()}`;
|
||||
const action = `socket_${camelCase(event)}`;
|
||||
|
||||
Object.keys(this.store._mutations).filter((namespaced) => {
|
||||
return namespaced.split('/').pop() === mutation;
|
||||
}).forEach((namespaced) => {
|
||||
this.store.commit(namespaced, this.unwrap(payload));
|
||||
});
|
||||
|
||||
Object.keys(this.store._actions).filter((namespaced) => {
|
||||
return namespaced.split('/').pop() === action;
|
||||
}).forEach((namespaced) => {
|
||||
this.store.dispatch(namespaced, this.unwrap(payload));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} args
|
||||
* @return {Array<Object>|Object}
|
||||
*/
|
||||
unwrap (args) {
|
||||
return (args && args.length <= 1) ? args[0] : args;
|
||||
}
|
||||
}
|
61
resources/assets/scripts/mixins/socketio/emitter.js
Normal file
61
resources/assets/scripts/mixins/socketio/emitter.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import isFunction from 'lodash/isFunction';
|
||||
|
||||
export default new class SocketEmitter {
|
||||
constructor () {
|
||||
this.listeners = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event listener for socket events.
|
||||
*
|
||||
* @param {String|Number|Symbol} event
|
||||
* @param {Function} callback
|
||||
* @param {*} vm
|
||||
*/
|
||||
addListener (event, callback, vm) {
|
||||
if (!isFunction(callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.listeners.has(event)) {
|
||||
this.listeners.set(event, []);
|
||||
}
|
||||
|
||||
this.listeners.get(event).push({callback, vm});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event listener for socket events based on the context passed through.
|
||||
*
|
||||
* @param {String|Number|Symbol} event
|
||||
* @param {Function} callback
|
||||
* @param {*} vm
|
||||
*/
|
||||
removeListener (event, callback, vm) {
|
||||
if (!isFunction(callback) || !this.listeners.has(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param {String|Number|Symbol} event
|
||||
* @param {Array} args
|
||||
*/
|
||||
emit (event, ...args) {
|
||||
(this.listeners.get(event) || []).forEach((listener) => {
|
||||
listener.callback.call(listener.vm, ...args);
|
||||
});
|
||||
}
|
||||
}
|
53
resources/assets/scripts/mixins/socketio/index.js
Normal file
53
resources/assets/scripts/mixins/socketio/index.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import SocketEmitter from './emitter';
|
||||
import SocketioConnector from './connector';
|
||||
|
||||
let connector = null;
|
||||
|
||||
export const Socketio = {
|
||||
/**
|
||||
* 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: {
|
||||
/**
|
||||
* @return {SocketioConnector}
|
||||
*/
|
||||
'$socket': function () {
|
||||
return connector;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnects from the active socket and sets the connector to null.
|
||||
*/
|
||||
removeSocket: function () {
|
||||
if (connector !== null && connector.instance() !== null) {
|
||||
connector.instance().close();
|
||||
}
|
||||
|
||||
connector = null;
|
||||
},
|
||||
},
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue