prettier reformat for merge (and remove jpeg-turbo Again)
This commit is contained in:
@@ -26,13 +26,11 @@ export default class AuthManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Make sure the fetch returned okay
|
// Make sure the fetch returned okay
|
||||||
if(!response.ok)
|
if (!response.ok) throw new Error(`Failed to query quth server: ${response.statusText}`);
|
||||||
throw new Error(`Failed to query quth server: ${response.statusText}`)
|
|
||||||
|
|
||||||
let json = (await response.json()) as JoinResponse;
|
let json = (await response.json()) as JoinResponse;
|
||||||
|
|
||||||
if (!json.success)
|
if (!json.success) throw new Error(json.error);
|
||||||
throw new Error(json.error);
|
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ const kCVMTSAssetsRoot = path.resolve(__dirname, '../../assets');
|
|||||||
|
|
||||||
const kRestartTimeout = 5000;
|
const kRestartTimeout = 5000;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type ChatHistory = {
|
type ChatHistory = {
|
||||||
user: string;
|
user: string;
|
||||||
msg: string;
|
msg: string;
|
||||||
@@ -115,14 +113,14 @@ export default class CollabVMServer {
|
|||||||
this.VM = vm;
|
this.VM = vm;
|
||||||
|
|
||||||
// hack but whatever (TODO: less rickity)
|
// hack but whatever (TODO: less rickity)
|
||||||
if(config.vm.type == "qemu") {
|
if (config.vm.type == 'qemu') {
|
||||||
(vm as QemuVM).on('statechange', (newState: VMState) => {
|
(vm as QemuVM).on('statechange', (newState: VMState) => {
|
||||||
if(newState == VMState.Stopped) {
|
if (newState == VMState.Stopped) {
|
||||||
this.logger.Info("stopped ?");
|
this.logger.Info('stopped ?');
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
this.logger.Info("restarting VM");
|
this.logger.Info('restarting VM');
|
||||||
await this.VM.Start();
|
await this.VM.Start();
|
||||||
}, kRestartTimeout)
|
}, kRestartTimeout);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -642,7 +640,7 @@ export default class CollabVMServer {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// No
|
// No
|
||||||
this.logger.Error(`User ${user?.IP.address} ${user?.username ? `with username ${user?.username}` : ''} sent broken Guacamole: ${(err as Error)}`);
|
this.logger.Error(`User ${user?.IP.address} ${user?.username ? `with username ${user?.username}` : ''} sent broken Guacamole: ${err as Error}`);
|
||||||
user?.kick();
|
user?.kick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import VNCVMDef from "./VNCVM/VNCVMDef";
|
import VNCVMDef from './VNCVM/VNCVMDef';
|
||||||
|
|
||||||
export default interface IConfig {
|
export default interface IConfig {
|
||||||
http: {
|
http: {
|
||||||
@@ -13,7 +13,7 @@ export default interface IConfig {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
}
|
};
|
||||||
auth: {
|
auth: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
apiEndpoint: string;
|
apiEndpoint: string;
|
||||||
@@ -26,7 +26,7 @@ export default interface IConfig {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
vm: {
|
vm: {
|
||||||
type: "qemu" | "vncvm";
|
type: 'qemu' | 'vncvm';
|
||||||
};
|
};
|
||||||
qemu: {
|
qemu: {
|
||||||
qemuArgs: string;
|
qemuArgs: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export default interface NetworkClient {
|
export default interface NetworkClient {
|
||||||
getIP() : string;
|
getIP(): string;
|
||||||
send(msg: string) : Promise<void>;
|
send(msg: string): Promise<void>;
|
||||||
close() : void;
|
close(): void;
|
||||||
on(event: string, listener: (...args: any[]) => void) : void;
|
on(event: string, listener: (...args: any[]) => void): void;
|
||||||
off(event: string, listener: (...args: any[]) => void) : void;
|
off(event: string, listener: (...args: any[]) => void): void;
|
||||||
isOpen() : boolean;
|
isOpen(): boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default interface NetworkServer {
|
export default interface NetworkServer {
|
||||||
start() : void;
|
start(): void;
|
||||||
stop() : void;
|
stop(): void;
|
||||||
on(event: string, listener: (...args: any[]) => void) : void;
|
on(event: string, listener: (...args: any[]) => void): void;
|
||||||
off(event: string, listener: (...args: any[]) => void) : void;
|
off(event: string, listener: (...args: any[]) => void): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
import EventEmitter from "events";
|
import EventEmitter from 'events';
|
||||||
import NetworkClient from "../NetworkClient.js";
|
import NetworkClient from '../NetworkClient.js';
|
||||||
import { Socket } from "net";
|
import { Socket } from 'net';
|
||||||
|
|
||||||
export default class TCPClient extends EventEmitter implements NetworkClient {
|
export default class TCPClient extends EventEmitter implements NetworkClient {
|
||||||
private socket: Socket;
|
private socket: Socket;
|
||||||
private cache: string;
|
private cache: string;
|
||||||
|
|
||||||
constructor(socket: Socket) {
|
|
||||||
super();
|
|
||||||
this.socket = socket;
|
|
||||||
this.cache = '';
|
|
||||||
this.socket.on('end', () => {
|
|
||||||
this.emit('disconnect');
|
|
||||||
})
|
|
||||||
this.socket.on('data', (data) => {
|
|
||||||
var msg = data.toString('utf-8');
|
|
||||||
if (msg[msg.length - 1] === '\n') msg = msg.slice(0, -1);
|
|
||||||
this.cache += msg;
|
|
||||||
this.readCache();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private readCache() {
|
constructor(socket: Socket) {
|
||||||
for (var index = this.cache.indexOf(';'); index !== -1; index = this.cache.indexOf(';')) {
|
super();
|
||||||
this.emit('msg', this.cache.slice(0, index + 1));
|
this.socket = socket;
|
||||||
this.cache = this.cache.slice(index + 1);
|
this.cache = '';
|
||||||
}
|
this.socket.on('end', () => {
|
||||||
}
|
this.emit('disconnect');
|
||||||
|
});
|
||||||
|
this.socket.on('data', (data) => {
|
||||||
|
var msg = data.toString('utf-8');
|
||||||
|
if (msg[msg.length - 1] === '\n') msg = msg.slice(0, -1);
|
||||||
|
this.cache += msg;
|
||||||
|
this.readCache();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getIP(): string {
|
private readCache() {
|
||||||
return this.socket.remoteAddress!;
|
for (var index = this.cache.indexOf(';'); index !== -1; index = this.cache.indexOf(';')) {
|
||||||
}
|
this.emit('msg', this.cache.slice(0, index + 1));
|
||||||
|
this.cache = this.cache.slice(index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
send(msg: string): Promise<void> {
|
getIP(): string {
|
||||||
return new Promise((res, rej) => {
|
return this.socket.remoteAddress!;
|
||||||
this.socket.write(msg, (err) => {
|
}
|
||||||
if (err) {
|
|
||||||
rej(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
send(msg: string): Promise<void> {
|
||||||
this.emit('disconnect');
|
return new Promise((res, rej) => {
|
||||||
this.socket.end();
|
this.socket.write(msg, (err) => {
|
||||||
}
|
if (err) {
|
||||||
|
rej(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
isOpen(): boolean {
|
close(): void {
|
||||||
return this.socket.writable;
|
this.emit('disconnect');
|
||||||
}
|
this.socket.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isOpen(): boolean {
|
||||||
|
return this.socket.writable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
import EventEmitter from "events";
|
import EventEmitter from 'events';
|
||||||
import NetworkServer from "../NetworkServer.js";
|
import NetworkServer from '../NetworkServer.js';
|
||||||
import { Server, Socket } from "net";
|
import { Server, Socket } from 'net';
|
||||||
import IConfig from "../IConfig.js";
|
import IConfig from '../IConfig.js';
|
||||||
import { Logger } from "@cvmts/shared";
|
import { Logger } from '@cvmts/shared';
|
||||||
import TCPClient from "./TCPClient.js";
|
import TCPClient from './TCPClient.js';
|
||||||
import { IPDataManager } from "../IPData.js";
|
import { IPDataManager } from '../IPData.js';
|
||||||
import { User } from "../User.js";
|
import { User } from '../User.js';
|
||||||
|
|
||||||
export default class TCPServer extends EventEmitter implements NetworkServer {
|
export default class TCPServer extends EventEmitter implements NetworkServer {
|
||||||
listener: Server;
|
listener: Server;
|
||||||
Config: IConfig;
|
Config: IConfig;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
clients: TCPClient[];
|
clients: TCPClient[];
|
||||||
|
|
||||||
constructor(config: IConfig) {
|
constructor(config: IConfig) {
|
||||||
super();
|
super();
|
||||||
this.logger = new Logger("CVMTS.TCPServer");
|
this.logger = new Logger('CVMTS.TCPServer');
|
||||||
this.Config = config;
|
this.Config = config;
|
||||||
this.listener = new Server();
|
this.listener = new Server();
|
||||||
this.clients = [];
|
this.clients = [];
|
||||||
this.listener.on('connection', socket => this.onConnection(socket));
|
this.listener.on('connection', (socket) => this.onConnection(socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
private onConnection(socket: Socket) {
|
private onConnection(socket: Socket) {
|
||||||
this.logger.Info(`New TCP connection from ${socket.remoteAddress}`);
|
this.logger.Info(`New TCP connection from ${socket.remoteAddress}`);
|
||||||
var client = new TCPClient(socket);
|
var client = new TCPClient(socket);
|
||||||
this.clients.push(client);
|
this.clients.push(client);
|
||||||
this.emit('connect', new User(client, IPDataManager.GetIPData(client.getIP()), this.Config));
|
this.emit('connect', new User(client, IPDataManager.GetIPData(client.getIP()), this.Config));
|
||||||
}
|
}
|
||||||
|
|
||||||
start(): void {
|
start(): void {
|
||||||
this.listener.listen(this.Config.tcp.port, this.Config.tcp.host, () => {
|
this.listener.listen(this.Config.tcp.port, this.Config.tcp.host, () => {
|
||||||
this.logger.Info(`TCP server listening on ${this.Config.tcp.host}:${this.Config.tcp.port}`);
|
this.logger.Info(`TCP server listening on ${this.Config.tcp.host}:${this.Config.tcp.port}`);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
stop(): void {
|
stop(): void {
|
||||||
this.listener.close();
|
this.listener.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export class User {
|
|||||||
clearInterval(this.msgRecieveInterval);
|
clearInterval(this.msgRecieveInterval);
|
||||||
this.msgRecieveInterval = setInterval(() => this.onNoMsg(), 10000);
|
this.msgRecieveInterval = setInterval(() => this.onNoMsg(), 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.nopSendInterval = setInterval(() => this.sendNop(), 5000);
|
this.nopSendInterval = setInterval(() => this.sendNop(), 5000);
|
||||||
this.msgRecieveInterval = setInterval(() => this.onNoMsg(), 10000);
|
this.msgRecieveInterval = setInterval(() => this.onNoMsg(), 10000);
|
||||||
this.sendNop();
|
this.sendNop();
|
||||||
@@ -66,7 +66,7 @@ export class User {
|
|||||||
this.VoteRateLimit = new RateLimiter(3, 3);
|
this.VoteRateLimit = new RateLimiter(3, 3);
|
||||||
this.VoteRateLimit.on('limit', () => this.closeConnection());
|
this.VoteRateLimit.on('limit', () => this.closeConnection());
|
||||||
}
|
}
|
||||||
|
|
||||||
assignGuestName(existingUsers: string[]): string {
|
assignGuestName(existingUsers: string[]): string {
|
||||||
var username;
|
var username;
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import VMDisplay from "./VMDisplay.js";
|
import VMDisplay from './VMDisplay.js';
|
||||||
|
|
||||||
export default interface VM {
|
export default interface VM {
|
||||||
Start(): Promise<void>;
|
Start(): Promise<void>;
|
||||||
Stop(): Promise<void>;
|
Stop(): Promise<void>;
|
||||||
Reboot(): Promise<void>;
|
Reboot(): Promise<void>;
|
||||||
Reset(): Promise<void>;
|
Reset(): Promise<void>;
|
||||||
MonitorCommand(command: string): Promise<any>;
|
MonitorCommand(command: string): Promise<any>;
|
||||||
GetDisplay(): VMDisplay;
|
GetDisplay(): VMDisplay;
|
||||||
SnapshotsSupported(): boolean;
|
SnapshotsSupported(): boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Size } from "@cvmts/shared";
|
import { Size } from '@cvmts/shared';
|
||||||
import EventEmitter from "node:events";
|
import EventEmitter from 'node:events';
|
||||||
|
|
||||||
export default interface VMDisplay extends EventEmitter {
|
export default interface VMDisplay extends EventEmitter {
|
||||||
Connect(): void;
|
Connect(): void;
|
||||||
Disconnect(): void;
|
Disconnect(): void;
|
||||||
Connected(): boolean;
|
Connected(): boolean;
|
||||||
Buffer(): Buffer;
|
Buffer(): Buffer;
|
||||||
Size(): Size;
|
Size(): Size;
|
||||||
MouseEvent(x: number, y: number, buttons: number): void;
|
MouseEvent(x: number, y: number, buttons: number): void;
|
||||||
KeyboardEvent(keysym: number, pressed: boolean): void;
|
KeyboardEvent(keysym: number, pressed: boolean): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,28 @@
|
|||||||
import EventEmitter from "events";
|
import EventEmitter from 'events';
|
||||||
import VNCVMDef from "./VNCVMDef";
|
import VNCVMDef from './VNCVMDef';
|
||||||
import VM from "../VM";
|
import VM from '../VM';
|
||||||
import VMDisplay from "../VMDisplay";
|
import VMDisplay from '../VMDisplay';
|
||||||
import { Clamp, Logger, Rect, Size, Sleep } from "@cvmts/shared";
|
import { Clamp, Logger, Rect, Size, Sleep } from '@cvmts/shared';
|
||||||
import { VncClient } from '@computernewb/nodejs-rfb';
|
import { VncClient } from '@computernewb/nodejs-rfb';
|
||||||
import { BatchRects } from "@cvmts/qemu";
|
import { BatchRects } from '@cvmts/qemu';
|
||||||
import { execaCommand } from "execa";
|
import { execaCommand } from 'execa';
|
||||||
|
|
||||||
export default class VNCVM extends EventEmitter implements VM, VMDisplay {
|
export default class VNCVM extends EventEmitter implements VM, VMDisplay {
|
||||||
def : VNCVMDef;
|
def: VNCVMDef;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
private displayVnc = new VncClient({
|
private displayVnc = new VncClient({
|
||||||
debug: false,
|
debug: false,
|
||||||
fps: 60,
|
fps: 60,
|
||||||
encodings: [
|
encodings: [VncClient.consts.encodings.raw, VncClient.consts.encodings.pseudoDesktopSize]
|
||||||
VncClient.consts.encodings.raw,
|
});
|
||||||
VncClient.consts.encodings.pseudoDesktopSize
|
private vncShouldReconnect: boolean = false;
|
||||||
]
|
|
||||||
});
|
|
||||||
private vncShouldReconnect: boolean = false;
|
|
||||||
|
|
||||||
constructor(def : VNCVMDef) {
|
constructor(def: VNCVMDef) {
|
||||||
super();
|
super();
|
||||||
this.def = def;
|
this.def = def;
|
||||||
this.logger = new Logger(`CVMTS.VNCVM/${this.def.vncHost}:${this.def.vncPort}`);
|
this.logger = new Logger(`CVMTS.VNCVM/${this.def.vncHost}:${this.def.vncPort}`);
|
||||||
|
|
||||||
this.displayVnc.on('connectTimeout', () => {
|
this.displayVnc.on('connectTimeout', () => {
|
||||||
this.Reconnect();
|
this.Reconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -34,7 +31,7 @@ export default class VNCVM extends EventEmitter implements VM, VMDisplay {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.displayVnc.on('disconnect', () => {
|
this.displayVnc.on('disconnect', () => {
|
||||||
this.logger.Info('Disconnected');
|
this.logger.Info('Disconnected');
|
||||||
this.Reconnect();
|
this.Reconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,7 +40,7 @@ export default class VNCVM extends EventEmitter implements VM, VMDisplay {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.displayVnc.on('firstFrameUpdate', () => {
|
this.displayVnc.on('firstFrameUpdate', () => {
|
||||||
this.logger.Info('Connected');
|
this.logger.Info('Connected');
|
||||||
// apparently this library is this good.
|
// apparently this library is this good.
|
||||||
// at least it's better than the two others which exist.
|
// at least it's better than the two others which exist.
|
||||||
this.displayVnc.changeFps(60);
|
this.displayVnc.changeFps(60);
|
||||||
@@ -77,17 +74,16 @@ export default class VNCVM extends EventEmitter implements VM, VMDisplay {
|
|||||||
|
|
||||||
this.emit('frame');
|
this.emit('frame');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async Reset(): Promise<void> {
|
||||||
async Reset(): Promise<void> {
|
if (this.def.restoreCmd) await execaCommand(this.def.restoreCmd, { shell: true });
|
||||||
if (this.def.restoreCmd) await execaCommand(this.def.restoreCmd, {shell: true});
|
else {
|
||||||
else {
|
await this.Stop();
|
||||||
await this.Stop();
|
await Sleep(1000);
|
||||||
await Sleep(1000);
|
await this.Start();
|
||||||
await this.Start();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private Reconnect() {
|
private Reconnect() {
|
||||||
if (this.displayVnc.connected) return;
|
if (this.displayVnc.connected) return;
|
||||||
@@ -98,60 +94,60 @@ export default class VNCVM extends EventEmitter implements VM, VMDisplay {
|
|||||||
// if we fail after max tries, emit a event
|
// if we fail after max tries, emit a event
|
||||||
|
|
||||||
this.displayVnc.connect({
|
this.displayVnc.connect({
|
||||||
host: this.def.vncHost,
|
host: this.def.vncHost,
|
||||||
port: this.def.vncPort,
|
port: this.def.vncPort,
|
||||||
path: null,
|
path: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async Start(): Promise<void> {
|
async Start(): Promise<void> {
|
||||||
this.logger.Info('Connecting');
|
this.logger.Info('Connecting');
|
||||||
if (this.def.startCmd) await execaCommand(this.def.startCmd, {shell: true});
|
if (this.def.startCmd) await execaCommand(this.def.startCmd, { shell: true });
|
||||||
this.Connect();
|
this.Connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
async Stop(): Promise<void> {
|
async Stop(): Promise<void> {
|
||||||
this.logger.Info('Disconnecting');
|
this.logger.Info('Disconnecting');
|
||||||
this.Disconnect();
|
this.Disconnect();
|
||||||
if (this.def.stopCmd) await execaCommand(this.def.stopCmd, {shell: true});
|
if (this.def.stopCmd) await execaCommand(this.def.stopCmd, { shell: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async Reboot(): Promise<void> {
|
async Reboot(): Promise<void> {
|
||||||
if (this.def.rebootCmd) await execaCommand(this.def.rebootCmd, {shell: true});
|
if (this.def.rebootCmd) await execaCommand(this.def.rebootCmd, { shell: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async MonitorCommand(command: string): Promise<any> {
|
async MonitorCommand(command: string): Promise<any> {
|
||||||
// TODO: This can maybe run a specified command?
|
// TODO: This can maybe run a specified command?
|
||||||
return "This VM does not support monitor commands.";
|
return 'This VM does not support monitor commands.';
|
||||||
}
|
}
|
||||||
|
|
||||||
GetDisplay(): VMDisplay {
|
GetDisplay(): VMDisplay {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
SnapshotsSupported(): boolean {
|
SnapshotsSupported(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Connect(): void {
|
Connect(): void {
|
||||||
this.vncShouldReconnect = true;
|
this.vncShouldReconnect = true;
|
||||||
this.Reconnect();
|
this.Reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
Disconnect(): void {
|
Disconnect(): void {
|
||||||
this.vncShouldReconnect = false;
|
this.vncShouldReconnect = false;
|
||||||
this.displayVnc.disconnect();
|
this.displayVnc.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connected(): boolean {
|
Connected(): boolean {
|
||||||
return this.displayVnc.connected;
|
return this.displayVnc.connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer(): Buffer {
|
Buffer(): Buffer {
|
||||||
return this.displayVnc.fb;
|
return this.displayVnc.fb;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size(): Size {
|
Size(): Size {
|
||||||
if (!this.displayVnc.connected)
|
if (!this.displayVnc.connected)
|
||||||
return {
|
return {
|
||||||
width: 0,
|
width: 0,
|
||||||
@@ -162,13 +158,13 @@ export default class VNCVM extends EventEmitter implements VM, VMDisplay {
|
|||||||
width: this.displayVnc.clientWidth,
|
width: this.displayVnc.clientWidth,
|
||||||
height: this.displayVnc.clientHeight
|
height: this.displayVnc.clientHeight
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseEvent(x: number, y: number, buttons: number): void {
|
MouseEvent(x: number, y: number, buttons: number): void {
|
||||||
if (this.displayVnc.connected) this.displayVnc.sendPointerEvent(Clamp(x, 0, this.displayVnc.clientWidth), Clamp(y, 0, this.displayVnc.clientHeight), buttons);
|
if (this.displayVnc.connected) this.displayVnc.sendPointerEvent(Clamp(x, 0, this.displayVnc.clientWidth), Clamp(y, 0, this.displayVnc.clientHeight), buttons);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardEvent(keysym: number, pressed: boolean): void {
|
KeyboardEvent(keysym: number, pressed: boolean): void {
|
||||||
if (this.displayVnc.connected) this.displayVnc.sendKeyEvent(keysym, pressed);
|
if (this.displayVnc.connected) this.displayVnc.sendKeyEvent(keysym, pressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export default interface VNCVMDef {
|
export default interface VNCVMDef {
|
||||||
vncHost : string;
|
vncHost: string;
|
||||||
vncPort : number;
|
vncPort: number;
|
||||||
startCmd : string | null;
|
startCmd: string | null;
|
||||||
stopCmd : string | null;
|
stopCmd: string | null;
|
||||||
rebootCmd : string | null;
|
rebootCmd: string | null;
|
||||||
restoreCmd : string | null;
|
restoreCmd: string | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +1,60 @@
|
|||||||
import { WebSocket } from "ws";
|
import { WebSocket } from 'ws';
|
||||||
import NetworkClient from "../NetworkClient.js";
|
import NetworkClient from '../NetworkClient.js';
|
||||||
import EventEmitter from "events";
|
import EventEmitter from 'events';
|
||||||
import { Logger } from "@cvmts/shared";
|
import { Logger } from '@cvmts/shared';
|
||||||
|
|
||||||
export default class WSClient extends EventEmitter implements NetworkClient {
|
export default class WSClient extends EventEmitter implements NetworkClient {
|
||||||
socket: WebSocket;
|
socket: WebSocket;
|
||||||
ip: string;
|
ip: string;
|
||||||
|
|
||||||
constructor(ws: WebSocket, ip: string) {
|
constructor(ws: WebSocket, ip: string) {
|
||||||
super();
|
super();
|
||||||
this.socket = ws;
|
this.socket = ws;
|
||||||
this.ip = ip;
|
this.ip = ip;
|
||||||
this.socket.on('message', (buf: Buffer, isBinary: boolean) => {
|
this.socket.on('message', (buf: Buffer, isBinary: boolean) => {
|
||||||
// Close the user's connection if they send a non-string message
|
// Close the user's connection if they send a non-string message
|
||||||
if (isBinary) {
|
if (isBinary) {
|
||||||
this.close();
|
this.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('msg', buf.toString("utf-8"));
|
this.emit('msg', buf.toString('utf-8'));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('close', () => {
|
this.socket.on('close', () => {
|
||||||
this.emit('disconnect');
|
this.emit('disconnect');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isOpen(): boolean {
|
isOpen(): boolean {
|
||||||
return this.socket.readyState === WebSocket.OPEN;
|
return this.socket.readyState === WebSocket.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIP(): string {
|
getIP(): string {
|
||||||
return this.ip;
|
return this.ip;
|
||||||
}
|
}
|
||||||
send(msg: string): Promise<void> {
|
send(msg: string): Promise<void> {
|
||||||
return new Promise((res,rej) => {
|
return new Promise((res, rej) => {
|
||||||
if(!this.isOpen())
|
if (!this.isOpen()) res();
|
||||||
|
|
||||||
|
this.socket.send(msg, (err) => {
|
||||||
|
if (err) {
|
||||||
|
rej(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
res();
|
res();
|
||||||
|
});
|
||||||
this.socket.send(msg, (err) => {
|
});
|
||||||
if (err) {
|
}
|
||||||
rej(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
if(this.isOpen()) {
|
if (this.isOpen()) {
|
||||||
// While this seems counterintutive, do note that the WebSocket protocol
|
// While this seems counterintutive, do note that the WebSocket protocol
|
||||||
// *sends* a data frame whilist closing a connection. Therefore, if the other end
|
// *sends* a data frame whilist closing a connection. Therefore, if the other end
|
||||||
// has forcibly hung up (closed) their connection, the best way to handle that
|
// has forcibly hung up (closed) their connection, the best way to handle that
|
||||||
// is to just let the inner TCP socket propegate that, which `ws` will do for us.
|
// is to just let the inner TCP socket propegate that, which `ws` will do for us.
|
||||||
// Otherwise, we'll try to send data to a closed client then SIGPIPE.
|
// Otherwise, we'll try to send data to a closed client then SIGPIPE.
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,18 +11,18 @@ import { User } from '../User.js';
|
|||||||
import { Logger } from '@cvmts/shared';
|
import { Logger } from '@cvmts/shared';
|
||||||
|
|
||||||
export default class WSServer extends EventEmitter implements NetworkServer {
|
export default class WSServer extends EventEmitter implements NetworkServer {
|
||||||
private httpServer: http.Server;
|
private httpServer: http.Server;
|
||||||
private wsServer: WebSocketServer;
|
private wsServer: WebSocketServer;
|
||||||
private clients: WSClient[];
|
private clients: WSClient[];
|
||||||
private Config: IConfig;
|
private Config: IConfig;
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(config : IConfig) {
|
constructor(config: IConfig) {
|
||||||
super();
|
super();
|
||||||
this.Config = config;
|
this.Config = config;
|
||||||
this.clients = [];
|
this.clients = [];
|
||||||
this.logger = new Logger("CVMTS.WSServer");
|
this.logger = new Logger('CVMTS.WSServer');
|
||||||
this.httpServer = http.createServer();
|
this.httpServer = http.createServer();
|
||||||
this.wsServer = new WebSocketServer({ noServer: true });
|
this.wsServer = new WebSocketServer({ noServer: true });
|
||||||
this.httpServer.on('upgrade', (req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) => this.httpOnUpgrade(req, socket, head));
|
this.httpServer.on('upgrade', (req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) => this.httpOnUpgrade(req, socket, head));
|
||||||
this.httpServer.on('request', (req, res) => {
|
this.httpServer.on('request', (req, res) => {
|
||||||
@@ -30,19 +30,19 @@ export default class WSServer extends EventEmitter implements NetworkServer {
|
|||||||
res.write('This server only accepts WebSocket connections.');
|
res.write('This server only accepts WebSocket connections.');
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
start(): void {
|
start(): void {
|
||||||
this.httpServer.listen(this.Config.http.port, this.Config.http.host, () => {
|
this.httpServer.listen(this.Config.http.port, this.Config.http.host, () => {
|
||||||
this.logger.Info(`WebSocket server listening on ${this.Config.http.host}:${this.Config.http.port}`);
|
this.logger.Info(`WebSocket server listening on ${this.Config.http.host}:${this.Config.http.port}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): void {
|
stop(): void {
|
||||||
this.httpServer.close();
|
this.httpServer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private httpOnUpgrade(req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) {
|
private httpOnUpgrade(req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) {
|
||||||
var killConnection = () => {
|
var killConnection = () => {
|
||||||
socket.write('HTTP/1.1 400 Bad Request\n\n400 Bad Request');
|
socket.write('HTTP/1.1 400 Bad Request\n\n400 Bad Request');
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
@@ -121,7 +121,7 @@ export default class WSServer extends EventEmitter implements NetworkServer {
|
|||||||
|
|
||||||
let ipdata = IPDataManager.GetIPDataMaybe(ip);
|
let ipdata = IPDataManager.GetIPDataMaybe(ip);
|
||||||
|
|
||||||
if(ipdata != null) {
|
if (ipdata != null) {
|
||||||
let connections = ipdata.refCount;
|
let connections = ipdata.refCount;
|
||||||
if (connections + 1 > this.Config.collabvm.maxConnections) {
|
if (connections + 1 > this.Config.collabvm.maxConnections) {
|
||||||
socket.write('HTTP/1.1 429 Too Many Requests\n\n429 Too Many Requests');
|
socket.write('HTTP/1.1 429 Too Many Requests\n\n429 Too Many Requests');
|
||||||
@@ -136,11 +136,11 @@ export default class WSServer extends EventEmitter implements NetworkServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onConnection(ws: WebSocket, req: http.IncomingMessage, ip: string) {
|
private onConnection(ws: WebSocket, req: http.IncomingMessage, ip: string) {
|
||||||
let client = new WSClient(ws, ip);
|
let client = new WSClient(ws, ip);
|
||||||
this.clients.push(client);
|
this.clients.push(client);
|
||||||
let user = new User(client, IPDataManager.GetIPData(ip), this.Config);
|
let user = new User(client, IPDataManager.GetIPData(ip), this.Config);
|
||||||
|
|
||||||
this.emit('connect', user);
|
this.emit('connect', user);
|
||||||
|
|
||||||
ws.on('error', (e) => {
|
ws.on('error', (e) => {
|
||||||
this.logger.Error(`${e} (caused by connection ${ip})`);
|
this.logger.Error(`${e} (caused by connection ${ip})`);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let exiting = false;
|
let exiting = false;
|
||||||
let VM : VM;
|
let VM: VM;
|
||||||
|
|
||||||
async function stop() {
|
async function stop() {
|
||||||
if (exiting) return;
|
if (exiting) return;
|
||||||
@@ -47,7 +47,7 @@ async function start() {
|
|||||||
// Init the auth manager if enabled
|
// Init the auth manager if enabled
|
||||||
let auth = Config.auth.enabled ? new AuthManager(Config.auth.apiEndpoint, Config.auth.secretKey) : null;
|
let auth = Config.auth.enabled ? new AuthManager(Config.auth.apiEndpoint, Config.auth.secretKey) : null;
|
||||||
switch (Config.vm.type) {
|
switch (Config.vm.type) {
|
||||||
case "qemu": {
|
case 'qemu': {
|
||||||
// Print a warning if qmpSockDir is set
|
// Print a warning if qmpSockDir is set
|
||||||
// and the host OS is Windows, as this
|
// and the host OS is Windows, as this
|
||||||
// configuration will very likely not work.
|
// configuration will very likely not work.
|
||||||
@@ -67,7 +67,7 @@ async function start() {
|
|||||||
VM = new QemuVM(def);
|
VM = new QemuVM(def);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "vncvm": {
|
case 'vncvm': {
|
||||||
VM = new VNCVM(Config.vncvm);
|
VM = new VNCVM(Config.vncvm);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule jpeg-turbo deleted from 6718ec1fc1
Reference in New Issue
Block a user