chore: reformat all code with prettier

This commit is contained in:
modeco80
2024-04-24 03:50:17 -04:00
parent bcbf7db8d9
commit ddae307874
13 changed files with 1053 additions and 1074 deletions

View File

@@ -1,12 +1,11 @@
import { Logger } from '@cvmts/shared'; import { Logger } from '@cvmts/shared';
import { Rank, User } from './User.js'; import { Rank, User } from './User.js';
export default class AuthManager { export default class AuthManager {
apiEndpoint: string; apiEndpoint: string;
secretKey: string; secretKey: string;
private logger = new Logger("CVMTS.AuthMan"); private logger = new Logger('CVMTS.AuthMan');
constructor(apiEndpoint: string, secretKey: string) { constructor(apiEndpoint: string, secretKey: string) {
this.apiEndpoint = apiEndpoint; this.apiEndpoint = apiEndpoint;
@@ -14,7 +13,7 @@ export default class AuthManager {
} }
async Authenticate(token: string, user: User): Promise<JoinResponse> { async Authenticate(token: string, user: User): Promise<JoinResponse> {
var response = await fetch(this.apiEndpoint + '/api/v1/join', { let response = await fetch(this.apiEndpoint + '/api/v1/join', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@@ -26,7 +25,7 @@ export default class AuthManager {
}) })
}); });
var json = (await response.json()) as JoinResponse; let json = (await response.json()) as JoinResponse;
if (!json.success) { if (!json.success) {
this.logger.Error(`Failed to query auth server: ${json.error}`); this.logger.Error(`Failed to query auth server: ${json.error}`);

View File

@@ -1,4 +1,4 @@
import { Logger } from "@cvmts/shared"; import { Logger } from '@cvmts/shared';
export class IPData { export class IPData {
tempMuteExpireTimeout?: NodeJS.Timeout; tempMuteExpireTimeout?: NodeJS.Timeout;
@@ -15,19 +15,17 @@ export class IPData {
// Call when a connection is closed to "release" the ip data // Call when a connection is closed to "release" the ip data
Unref() { Unref() {
if(this.refCount - 1 < 0) if (this.refCount - 1 < 0) this.refCount = 0;
this.refCount = 0;
this.refCount--; this.refCount--;
} }
} }
export class IPDataManager { export class IPDataManager {
static ipDatas = new Map<string, IPData>(); static ipDatas = new Map<string, IPData>();
static logger = new Logger("CVMTS.IPDataManager"); static logger = new Logger('CVMTS.IPDataManager');
static GetIPData(address: string) { static GetIPData(address: string) {
if(IPDataManager.ipDatas.has(address)) { if (IPDataManager.ipDatas.has(address)) {
// Note: We already check for if it exists, so we use ! here // Note: We already check for if it exists, so we use ! here
// because TypeScript can't exactly tell that in this case, // because TypeScript can't exactly tell that in this case,
// only in explicit null or undefined checks // only in explicit null or undefined checks
@@ -35,16 +33,15 @@ export class IPDataManager {
ref.refCount++; ref.refCount++;
return ref; return ref;
} }
let data = new IPData(address); let data = new IPData(address);
data.refCount++; data.refCount++;
IPDataManager.ipDatas.set(address, data); IPDataManager.ipDatas.set(address, data);
return data; return data;
} }
static ForEachIPData(callback: (d: IPData) => void) { static ForEachIPData(callback: (d: IPData) => void) {
for(let tuple of IPDataManager.ipDatas) for (let tuple of IPDataManager.ipDatas) callback(tuple[1]);
callback(tuple[1]);
} }
} }
@@ -52,11 +49,10 @@ export class IPDataManager {
// Strictly speaking this will just allow the v8 GC to finally // Strictly speaking this will just allow the v8 GC to finally
// delete the objects, but same difference. // delete the objects, but same difference.
setInterval(() => { setInterval(() => {
for(let tuple of IPDataManager.ipDatas) { for (let tuple of IPDataManager.ipDatas) {
if(tuple[1].refCount == 0) { if (tuple[1].refCount == 0) {
IPDataManager.logger.Info("Deleted ipdata for IP {0}", tuple[0]); IPDataManager.logger.Info('Deleted ipdata for IP {0}', tuple[0]);
IPDataManager.ipDatas.delete(tuple[0]); IPDataManager.ipDatas.delete(tuple[0]);
} }
} }
}, 15000); }, 15000);

View File

@@ -1,36 +1,36 @@
import { EventEmitter } from "events"; import { EventEmitter } from 'events';
// Class to ratelimit a resource (chatting, logging in, etc) // Class to ratelimit a resource (chatting, logging in, etc)
export default class RateLimiter extends EventEmitter { export default class RateLimiter extends EventEmitter {
private limit : number; private limit: number;
private interval : number; private interval: number;
private requestCount : number; private requestCount: number;
private limiter? : NodeJS.Timeout; private limiter?: NodeJS.Timeout;
private limiterSet : boolean; private limiterSet: boolean;
constructor(limit : number, interval : number) { constructor(limit: number, interval: number) {
super(); super();
this.limit = limit; this.limit = limit;
this.interval = interval; this.interval = interval;
this.requestCount = 0; this.requestCount = 0;
this.limiterSet = false; this.limiterSet = false;
} }
// Return value is whether or not the action should be continued // Return value is whether or not the action should be continued
request() : boolean { request(): boolean {
this.requestCount++; this.requestCount++;
if (this.requestCount === this.limit) { if (this.requestCount === this.limit) {
this.emit('limit'); this.emit('limit');
clearTimeout(this.limiter); clearTimeout(this.limiter);
this.limiterSet = false; this.limiterSet = false;
this.requestCount = 0; this.requestCount = 0;
return false; return false;
} }
if (!this.limiterSet) { if (!this.limiterSet) {
this.limiter = setTimeout(() => { this.limiter = setTimeout(() => {
this.limiterSet = false; this.limiterSet = false;
this.requestCount = 0; this.requestCount = 0;
}, this.interval * 1000); }, this.interval * 1000);
this.limiterSet = true; this.limiterSet = true;
} }
return true; return true;
} }
} }

View File

@@ -26,7 +26,7 @@ export class User {
TurnRateLimit: RateLimiter; TurnRateLimit: RateLimiter;
VoteRateLimit: RateLimiter; VoteRateLimit: RateLimiter;
private logger = new Logger("CVMTS.User"); private logger = new Logger('CVMTS.User');
constructor(ws: WebSocket, ip: IPData, config: IConfig, username?: string, node?: string) { constructor(ws: WebSocket, ip: IPData, config: IConfig, username?: string, node?: string) {
this.IP = ip; this.IP = ip;
@@ -59,6 +59,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 {
@@ -67,25 +68,30 @@ export class User {
this.username = username; this.username = username;
return username; return username;
} }
sendNop() { sendNop() {
this.socket.send('3.nop;'); this.socket.send('3.nop;');
} }
sendMsg(msg: string | Buffer) { sendMsg(msg: string | Buffer) {
if (this.socket.readyState !== this.socket.OPEN) return; if (this.socket.readyState !== this.socket.OPEN) return;
clearInterval(this.nopSendInterval); clearInterval(this.nopSendInterval);
this.nopSendInterval = setInterval(() => this.sendNop(), 5000); this.nopSendInterval = setInterval(() => this.sendNop(), 5000);
this.socket.send(msg); this.socket.send(msg);
} }
private onNoMsg() { private onNoMsg() {
this.sendNop(); this.sendNop();
this.nopRecieveTimeout = setTimeout(() => { this.nopRecieveTimeout = setTimeout(() => {
this.closeConnection(); this.closeConnection();
}, 3000); }, 3000);
} }
closeConnection() { closeConnection() {
this.socket.send(guacutils.encode('disconnect')); this.socket.send(guacutils.encode('disconnect'));
this.socket.close(); this.socket.close();
} }
onMsgSent() { onMsgSent() {
if (!this.Config.collabvm.automute.enabled) return; if (!this.Config.collabvm.automute.enabled) return;
// rate limit guest and unregistered chat messages, but not staff ones // rate limit guest and unregistered chat messages, but not staff ones
@@ -99,6 +105,7 @@ export class User {
break; break;
} }
} }
mute(permanent: boolean) { mute(permanent: boolean) {
this.IP.muted = true; this.IP.muted = true;
this.sendMsg(guacutils.encode('chat', '', `You have been muted${permanent ? '' : ` for ${this.Config.collabvm.tempMuteTime} seconds`}.`)); this.sendMsg(guacutils.encode('chat', '', `You have been muted${permanent ? '' : ` for ${this.Config.collabvm.tempMuteTime} seconds`}.`));

View File

@@ -1,54 +1,54 @@
import { Permissions } from "./IConfig"; import { Permissions } from './IConfig';
export function Randint(min : number, max : number) { export function Randint(min: number, max: number) {
return Math.floor((Math.random() * (max - min)) + min); return Math.floor(Math.random() * (max - min) + min);
}
export function HTMLSanitize(input : string) : string {
var output = "";
for (var i = 0; i < input.length; i++) {
switch (input[i]) {
case "<":
output += "&lt;"
break;
case ">":
output += "&gt;"
break;
case "&":
output += "&amp;"
break;
case "\"":
output += "&quot;"
break;
case "'":
output += "&#x27;";
break;
case "/":
output += "&#x2F;";
break;
case "\n":
output += "&#13;&#10;";
break;
default:
var charcode : number = input.charCodeAt(i);
if (charcode >= 32 && charcode <= 126)
output += input[i];
break;
}
}
return output;
} }
export function MakeModPerms(modperms : Permissions) : number { export function HTMLSanitize(input: string): string {
var perms = 0; var output = '';
if (modperms.restore) perms |= 1; for (var i = 0; i < input.length; i++) {
if (modperms.reboot) perms |= 2; switch (input[i]) {
if (modperms.ban) perms |= 4; case '<':
if (modperms.forcevote) perms |= 8; output += '&lt;';
if (modperms.mute) perms |= 16; break;
if (modperms.kick) perms |= 32; case '>':
if (modperms.bypassturn) perms |= 64; output += '&gt;';
if (modperms.rename) perms |= 128; break;
if (modperms.grabip) perms |= 256; case '&':
if (modperms.xss) perms |= 512; output += '&amp;';
return perms; break;
} case '"':
output += '&quot;';
break;
case "'":
output += '&#x27;';
break;
case '/':
output += '&#x2F;';
break;
case '\n':
output += '&#13;&#10;';
break;
default:
var charcode: number = input.charCodeAt(i);
if (charcode >= 32 && charcode <= 126) output += input[i];
break;
}
}
return output;
}
export function MakeModPerms(modperms: Permissions): number {
var perms = 0;
if (modperms.restore) perms |= 1;
if (modperms.reboot) perms |= 2;
if (modperms.ban) perms |= 4;
if (modperms.forcevote) perms |= 8;
if (modperms.mute) perms |= 16;
if (modperms.kick) perms |= 32;
if (modperms.bypassturn) perms |= 64;
if (modperms.rename) perms |= 128;
if (modperms.grabip) perms |= 256;
if (modperms.xss) perms |= 512;
return perms;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,37 @@
export function decode(string : string) : string[] { export function decode(string: string): string[] {
let pos = -1; let pos = -1;
let sections = []; let sections = [];
for(;;) { for (;;) {
let len = string.indexOf('.', pos + 1); let len = string.indexOf('.', pos + 1);
if(len === -1) if (len === -1) break;
break;
pos = parseInt(string.slice(pos + 1, len)) + len + 1; pos = parseInt(string.slice(pos + 1, len)) + len + 1;
// don't allow funky protocol length // don't allow funky protocol length
if(pos > string.length) if (pos > string.length) return [];
return [];
sections.push(string.slice(len + 1, pos)); sections.push(string.slice(len + 1, pos));
const sep = string.slice(pos, pos + 1);
const sep = string.slice(pos, pos + 1); if (sep === ',') continue;
else if (sep === ';') break;
// Invalid data.
else return [];
}
if(sep === ',') return sections;
continue;
else if(sep === ';')
break;
else
// Invalid data.
return [];
}
return sections;
} }
export function encode(...string : string[]) : string { export function encode(...string: string[]): string {
let command = ''; let command = '';
for(var i = 0; i < string.length; i++) { for (var i = 0; i < string.length; i++) {
let current = string[i]; let current = string[i];
command += current.toString().length + '.' + current; command += current.toString().length + '.' + current;
command += ( i < string.length - 1 ? ',' : ';'); command += i < string.length - 1 ? ',' : ';';
} }
return command; return command;
} }

View File

@@ -1,6 +1,6 @@
import * as toml from 'toml'; import * as toml from 'toml';
import IConfig from './IConfig.js'; import IConfig from './IConfig.js';
import * as fs from "fs"; import * as fs from 'fs';
import WSServer from './WSServer.js'; import WSServer from './WSServer.js';
import { QemuVM, QemuVmDefinition } from '@cvmts/qemu'; import { QemuVM, QemuVmDefinition } from '@cvmts/qemu';
@@ -8,51 +8,50 @@ import { QemuVM, QemuVmDefinition } from '@cvmts/qemu';
import * as Shared from '@cvmts/shared'; import * as Shared from '@cvmts/shared';
import AuthManager from './AuthManager.js'; import AuthManager from './AuthManager.js';
let logger = new Shared.Logger("CVMTS.Init"); let logger = new Shared.Logger('CVMTS.Init');
logger.Info("CollabVM Server starting up"); logger.Info('CollabVM Server starting up');
// Parse the config file // Parse the config file
var Config : IConfig; let Config: IConfig;
if (!fs.existsSync("config.toml")) { if (!fs.existsSync('config.toml')) {
logger.Error("Fatal error: Config.toml not found. Please copy config.example.toml and fill out fields") logger.Error('Fatal error: Config.toml not found. Please copy config.example.toml and fill out fields');
process.exit(1); process.exit(1);
} }
try { try {
var configRaw = fs.readFileSync("config.toml").toString(); var configRaw = fs.readFileSync('config.toml').toString();
Config = toml.parse(configRaw); Config = toml.parse(configRaw);
} catch (e) { } catch (e) {
logger.Error("Fatal error: Failed to read or parse the config file: {0}", (e as Error).message); logger.Error('Fatal error: Failed to read or parse the config file: {0}', (e as Error).message);
process.exit(1); process.exit(1);
} }
async function start() { async function start() {
// 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.
if(process.platform === "win32" && Config.vm.qmpSockDir) { if (process.platform === 'win32' && Config.vm.qmpSockDir) {
logger.Warning("You appear to have the option 'qmpSockDir' enabled in the config.") logger.Warning("You appear to have the option 'qmpSockDir' enabled in the config.");
logger.Warning("This is not supported on Windows, and you will likely run into issues."); logger.Warning('This is not supported on Windows, and you will likely run into issues.');
logger.Warning("To remove this warning, use the qmpHost and qmpPort options instead."); logger.Warning('To remove this warning, use the qmpHost and qmpPort options instead.');
} }
// 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;
// Fire up the VM // Fire up the VM
let def: QemuVmDefinition = { let def: QemuVmDefinition = {
id: Config.collabvm.node, id: Config.collabvm.node,
command: Config.vm.qemuArgs command: Config.vm.qemuArgs
} };
var VM = new QemuVM(def); var VM = new QemuVM(def);
await VM.Start(); await VM.Start();
// Start up the websocket server // Start up the websocket server
var WS = new WSServer(Config, VM, auth); var WS = new WSServer(Config, VM, auth);
WS.listen(); WS.listen();
} }
start(); start();

View File

@@ -1,4 +1,4 @@
import { Size, Rect } from "@cvmts/shared"; import { Size, Rect } from '@cvmts/shared';
export function BatchRects(size: Size, rects: Array<Rect>): Rect { export function BatchRects(size: Size, rects: Array<Rect>): Rect {
var mergedX = size.width; var mergedX = size.width;
@@ -38,4 +38,4 @@ export function BatchRects(size: Size, rects: Array<Rect>): Rect {
width: mergedWidth, width: mergedWidth,
height: mergedHeight height: mergedHeight
}; };
} }

View File

@@ -30,8 +30,6 @@ const kMaxFailCount = 5;
// TODO: This should be added to QemuVmDefinition and the below export removed // TODO: This should be added to QemuVmDefinition and the below export removed
let gVMShouldSnapshot = true; let gVMShouldSnapshot = true;
export function setSnapshot(val: boolean) { export function setSnapshot(val: boolean) {
gVMShouldSnapshot = val; gVMShouldSnapshot = val;
} }
@@ -68,10 +66,9 @@ export class QemuVM extends EventEmitter {
// build additional command line statements to enable qmp/vnc over unix sockets // build additional command line statements to enable qmp/vnc over unix sockets
// FIXME: Still use TCP if on Windows. // FIXME: Still use TCP if on Windows.
if(!this.addedAdditionalArguments) { if (!this.addedAdditionalArguments) {
cmd += ' -no-shutdown'; cmd += ' -no-shutdown';
if(gVMShouldSnapshot) if (gVMShouldSnapshot) cmd += ' -snapshot';
cmd += ' -snapshot';
cmd += ` -qmp unix:${this.GetQmpPath()},server,wait -vnc unix:${this.GetVncPath()}`; cmd += ` -qmp unix:${this.GetQmpPath()},server,wait -vnc unix:${this.GetVncPath()}`;
this.definition.command = cmd; this.definition.command = cmd;
this.addedAdditionalArguments = true; this.addedAdditionalArguments = true;
@@ -183,7 +180,7 @@ export class QemuVM extends EventEmitter {
if (self.qmpConnected) { if (self.qmpConnected) {
await self.DisconnectQmp(); await self.DisconnectQmp();
} }
self.DisconnectDisplay(); self.DisconnectDisplay();
if (self.state != VMState.Stopping) { if (self.state != VMState.Stopping) {
@@ -275,16 +272,13 @@ export class QemuVM extends EventEmitter {
private async DisconnectQmp() { private async DisconnectQmp() {
if (this.qmpConnected) return; if (this.qmpConnected) return;
if(this.qmpInstance == null) if (this.qmpInstance == null) return;
return;
this.qmpConnected = false; this.qmpConnected = false;
this.qmpInstance.end(); this.qmpInstance.end();
this.qmpInstance = null; this.qmpInstance = null;
try { try {
await unlink(this.GetQmpPath()); await unlink(this.GetQmpPath());
} catch(err) { } catch (err) {}
}
} }
} }

View File

@@ -76,7 +76,7 @@ export default class QmpClient extends Socket {
// just rethrow lol // just rethrow lol
//throw err; //throw err;
console.log("you have pants: rules,", err); console.log('you have pants: rules,', err);
}); });
this.once('data', (data) => { this.once('data', (data) => {

View File

@@ -1,4 +1,3 @@
// TODO: `Object` has a toString(), but we should probably gate that off // TODO: `Object` has a toString(), but we should probably gate that off
/// Interface for things that can be turned into strings /// Interface for things that can be turned into strings
export interface ToStringable { export interface ToStringable {
@@ -6,4 +5,4 @@ export interface ToStringable {
} }
/// A type for strings, or things that can (in a valid manner) be turned into strings /// A type for strings, or things that can (in a valid manner) be turned into strings
export type StringLike = string | ToStringable; export type StringLike = string | ToStringable;

View File

@@ -17,8 +17,8 @@ export type Size = {
}; };
export type Rect = { export type Rect = {
x: number, x: number;
y: number, y: number;
width: number, width: number;
height: number height: number;
}; };