Implement basic support for connecting to wings console via websocket rather than socketio
This commit is contained in:
parent
0757d8856b
commit
e87c5f6657
12 changed files with 122 additions and 117 deletions
|
@ -34,6 +34,11 @@
|
|||
Databases
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: 'server-network' }">
|
||||
Networking
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -54,7 +59,6 @@
|
|||
import Vue from 'vue';
|
||||
import Navigation from '@/components/core/Navigation.vue';
|
||||
import {mapState} from 'vuex';
|
||||
import * as io from 'socket.io-client';
|
||||
import {Socketio} from "@/mixins/socketio";
|
||||
import PowerButtons from "@/components/server/components/PowerButtons.vue";
|
||||
import Flash from "@/components/Flash.vue";
|
||||
|
@ -105,13 +109,12 @@
|
|||
this.$store.dispatch('server/getCredentials', {server: this.$route.params.id})
|
||||
])
|
||||
.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}`,
|
||||
});
|
||||
// Configure the websocket implementation and assign it to the mixin.
|
||||
this.$socket().connect(
|
||||
`ws://192.168.50.3:8080/api/servers/${this.server.uuid}/ws`,
|
||||
'CC8kHCuMkXPosgzGO6d37wvhNcksWxG6kTrA',
|
||||
);
|
||||
|
||||
this.$socket().connect(socket);
|
||||
this.loadingServerData = false;
|
||||
})
|
||||
.catch(err => {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<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-if="status === 'offline'"
|
||||
v-on:click.prevent="sendPowerAction('start')"
|
||||
>Start
|
||||
</button>
|
||||
|
@ -45,7 +45,7 @@
|
|||
|
||||
methods: {
|
||||
sendPowerAction: function (action: string) {
|
||||
this.$socket().instance().emit('set status', action)
|
||||
this.$socket().emit('set state', action)
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -64,20 +64,12 @@
|
|||
* Listen for specific socket.io emits from the server.
|
||||
*/
|
||||
sockets: {
|
||||
'server log': function (data: string) {
|
||||
data.split(/\n/g).forEach((line: string): void => {
|
||||
if (this.terminal) {
|
||||
this.terminal.writeln(line + '\u001b[0m');
|
||||
}
|
||||
});
|
||||
'server log': function (lines: Array<string>) {
|
||||
lines.forEach(data => data.split(/\n/g).forEach(line => this.terminal && this.terminal.writeln(line + '\u001b[0m')));
|
||||
},
|
||||
|
||||
'console': function (data: { line: string }) {
|
||||
data.line.split(/\n/g).forEach((line: string): void => {
|
||||
if (this.terminal) {
|
||||
this.terminal.writeln(line + '\u001b[0m');
|
||||
}
|
||||
});
|
||||
'console output': function (line: string) {
|
||||
this.terminal && this.terminal.writeln(line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m');
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -114,7 +106,7 @@
|
|||
this.terminal.fit();
|
||||
this.terminal.clear();
|
||||
|
||||
this.$socket().instance().emit('send server log');
|
||||
this.$socket().emit('send logs');
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -123,7 +115,7 @@
|
|||
sendCommand: function () {
|
||||
this.commandHistoryIndex = -1;
|
||||
this.commandHistory.unshift(this.command);
|
||||
this.$socket().instance().emit('send command', this.command);
|
||||
this.$socket().emit('send command', this.command);
|
||||
this.command = '';
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export default {
|
||||
STATUS_OFF: 0,
|
||||
STATUS_ON: 1,
|
||||
STATUS_STARTING: 2,
|
||||
STATUS_STOPPING: 3,
|
||||
STATUS_OFF: 'offline',
|
||||
STATUS_ON: 'running',
|
||||
STATUS_STARTING: 'starting',
|
||||
STATUS_STOPPING: 'stopping',
|
||||
};
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,29 +1,23 @@
|
|||
import * as io from 'socket.io-client';
|
||||
import {camelCase} from 'lodash';
|
||||
import SocketEmitter from './emitter';
|
||||
import {Store} from "vuex";
|
||||
|
||||
const SYSTEM_EVENTS: Array<string> = [
|
||||
'connect',
|
||||
'error',
|
||||
'disconnect',
|
||||
'reconnect',
|
||||
'reconnect_attempt',
|
||||
'reconnecting',
|
||||
'reconnect_error',
|
||||
'reconnect_failed',
|
||||
'connect_error',
|
||||
'connect_timeout',
|
||||
'connecting',
|
||||
'ping',
|
||||
'pong',
|
||||
];
|
||||
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<string>
|
||||
}
|
||||
|
||||
export default class SocketioConnector {
|
||||
/**
|
||||
* The socket instance.
|
||||
*/
|
||||
socket: null | SocketIOClient.Socket;
|
||||
socket: null | WebSocket;
|
||||
|
||||
/**
|
||||
* The vuex store being used to persist data and socket state.
|
||||
|
@ -37,21 +31,33 @@ export default class SocketioConnector {
|
|||
|
||||
/**
|
||||
* Initialize a new Socket connection.
|
||||
*
|
||||
* @param {io} socket
|
||||
*/
|
||||
connect(socket: SocketIOClient.Socket) {
|
||||
this.socket = socket;
|
||||
connect(url: string, protocols?: string | string[]): void {
|
||||
this.socket = new WebSocket(url, protocols);
|
||||
this.registerEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the socket instance we are working with.
|
||||
*/
|
||||
instance(): SocketIOClient.Socket | null {
|
||||
instance(): WebSocket | null {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an event along to the websocket. If there is no active connection, a void
|
||||
* result is returned.
|
||||
*/
|
||||
emit(event: string, payload?: string | Array<string>): 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.
|
||||
|
@ -61,55 +67,66 @@ export default class SocketioConnector {
|
|||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
this.socket['onevent'] = (packet: { data: Array<any> }): void => {
|
||||
const [event, ...args] = packet.data;
|
||||
SocketEmitter.emit(event, ...args);
|
||||
|
||||
this.passToStore(event, args);
|
||||
this.socket.onopen = () => this.emitAndPassToStore(SOCKET_CONNECT);
|
||||
this.socket.onclose = () => this.emitAndPassToStore(SOCKET_DISCONNECT);
|
||||
this.socket.onerror = () => {
|
||||
// @todo reconnect?
|
||||
if (this.socket && this.socket.readyState !== 1) {
|
||||
this.emitAndPassToStore(SOCKET_ERROR, ['Failed to connect to websocket.']);
|
||||
}
|
||||
};
|
||||
|
||||
SYSTEM_EVENTS.forEach((event: string): void => {
|
||||
if (!this.socket) {
|
||||
return;
|
||||
this.socket.onmessage = (wse): void => {
|
||||
console.log('Socket message:', wse.data);
|
||||
|
||||
try {
|
||||
let {event, args}: WingsWebsocketResponse = JSON.parse(wse.data);
|
||||
|
||||
this.emitAndPassToStore(event, args);
|
||||
} catch (ex) {
|
||||
// do nothing, bad JSON response
|
||||
console.error(ex);
|
||||
return
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.socket.on(event, (payload: any) => {
|
||||
SocketEmitter.emit(event, payload);
|
||||
|
||||
this.passToStore(event, payload);
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Emits the event over the event emitter and also passes it along to the vuex store.
|
||||
*/
|
||||
emitAndPassToStore(event: string, payload?: Array<string>) {
|
||||
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.
|
||||
*/
|
||||
passToStore(event: string | number, payload: Array<any>) {
|
||||
passToStore(event: string, payload?: Array<string>) {
|
||||
if (!this.store) {
|
||||
return;
|
||||
}
|
||||
|
||||
const s: Store<any> = this.store;
|
||||
const mutation = `SOCKET_${String(event).toUpperCase()}`;
|
||||
const action = `socket_${camelCase(String(event))}`;
|
||||
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, this.unwrap(payload));
|
||||
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, this.unwrap(payload)).catch(console.error);
|
||||
s.dispatch(namespaced, payload ? this.unwrap(payload) : null).catch(console.error);
|
||||
});
|
||||
}
|
||||
|
||||
unwrap(args: Array<any>) {
|
||||
unwrap(args: Array<string>) {
|
||||
return (args && args.length <= 1) ? args[0] : args;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,10 +34,7 @@ export const Socketio: ComponentOptions<Vue> = {
|
|||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* @return {SocketioConnector}
|
||||
*/
|
||||
'$socket': function () {
|
||||
'$socket': function (): SocketioConnector | null {
|
||||
return connector;
|
||||
},
|
||||
|
||||
|
@ -49,7 +46,7 @@ export const Socketio: ComponentOptions<Vue> = {
|
|||
return;
|
||||
}
|
||||
|
||||
const instance: SocketIOClient.Socket | null = connector.instance();
|
||||
const instance = connector.instance();
|
||||
if (instance) {
|
||||
instance.close();
|
||||
}
|
||||
|
|
|
@ -19,17 +19,12 @@ export default {
|
|||
state.connectionError = err;
|
||||
},
|
||||
|
||||
SOCKET_CONNECT_ERROR: (state: SocketState, err: Error) => {
|
||||
state.connected = false;
|
||||
state.connectionError = err;
|
||||
'SOCKET_INITIAL STATUS': (state: SocketState, data: string) => {
|
||||
state.status = data;
|
||||
},
|
||||
|
||||
'SOCKET_INITIAL STATUS': (state: SocketState, data: { status: number }) => {
|
||||
state.status = data.status;
|
||||
},
|
||||
|
||||
SOCKET_STATUS: (state: SocketState, data: { status: number }) => {
|
||||
state.status = data.status;
|
||||
SOCKET_STATUS: (state: SocketState, data: string) => {
|
||||
state.status = data;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ export type ApplicationState = {
|
|||
export type SocketState = {
|
||||
connected: boolean,
|
||||
connectionError: boolean | Error,
|
||||
status: number,
|
||||
status: string,
|
||||
}
|
||||
|
||||
export type ServerApplicationCredentials = {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue