2023-02-25 07:08:56 +00:00
|
|
|
import WebSocket, { WebSocketServer } from 'ws'
|
2023-02-25 07:08:05 +00:00
|
|
|
import express, { Express, Request, Response, Application } from 'express'
|
2023-02-25 05:24:07 +00:00
|
|
|
import { readFile } from 'fs/promises'
|
2023-02-25 07:08:56 +00:00
|
|
|
import mime from 'mime'
|
2023-02-25 16:59:56 +00:00
|
|
|
import { v4 as uuidv4 } from 'uuid'
|
2023-02-25 07:08:56 +00:00
|
|
|
import Log = require('log')
|
2023-02-25 16:59:56 +00:00
|
|
|
import { delay } from 'delay';
|
2023-02-25 05:24:07 +00:00
|
|
|
|
|
|
|
interface ServerData {
|
|
|
|
[key: string]: string;
|
|
|
|
PORT? : string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ServerTemplate {
|
|
|
|
name : string;
|
|
|
|
path : string;
|
2023-02-25 07:08:56 +00:00
|
|
|
data? : string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ServerProxy {
|
|
|
|
path : string;
|
|
|
|
mime : string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ServerProxyList {
|
|
|
|
[key: string]: ServerProxy;
|
2023-02-25 05:24:07 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 16:59:56 +00:00
|
|
|
interface ServerQueue {
|
|
|
|
[key: string]: Function;
|
|
|
|
}
|
|
|
|
|
2023-02-25 05:24:07 +00:00
|
|
|
class Server {
|
|
|
|
private id : string = 'server'
|
|
|
|
public isActive : boolean = false
|
|
|
|
private log : any
|
|
|
|
private templates : ServerTemplate[] = [
|
|
|
|
{
|
|
|
|
name :'index',
|
|
|
|
path : 'server.html'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name : 'script',
|
|
|
|
path : 'lib/client/index.js'
|
|
|
|
}
|
|
|
|
]
|
2023-02-25 07:08:05 +00:00
|
|
|
private http : Application
|
|
|
|
private httpd : Server
|
2023-02-25 07:08:56 +00:00
|
|
|
private wss : WebSocketServer
|
2023-02-25 05:24:07 +00:00
|
|
|
private port : number = 9900
|
|
|
|
private wsPort : number = 9901
|
2023-02-25 07:08:56 +00:00
|
|
|
private proxy : ServerProxyList = {}
|
2023-02-25 16:59:56 +00:00
|
|
|
private queue : ServerQueue = {}
|
|
|
|
private interval : ReturnType<typeof setInterval>
|
|
|
|
private intervalPeriod : number = 10000 //10 sec
|
2023-02-25 07:08:05 +00:00
|
|
|
|
2023-02-25 05:24:07 +00:00
|
|
|
constructor () {
|
|
|
|
this.init()
|
|
|
|
}
|
|
|
|
|
|
|
|
async init () {
|
|
|
|
this.log = await Log({ label : this.id });
|
2023-02-25 07:08:05 +00:00
|
|
|
await this.load()
|
|
|
|
await this.start()
|
2023-02-25 05:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async load () {
|
2023-02-25 07:08:05 +00:00
|
|
|
this.http = express()
|
2023-02-25 05:24:07 +00:00
|
|
|
for (let tmpl of this.templates) {
|
|
|
|
tmpl.data = await readFile(tmpl.path, 'utf8')
|
|
|
|
}
|
2023-02-25 07:08:05 +00:00
|
|
|
|
|
|
|
this.http.get('/', this.index.bind(this))
|
|
|
|
this.http.get('/client.js', this.script.bind(this))
|
2023-02-25 07:08:56 +00:00
|
|
|
this.http.get('/image/:key', this.image.bind(this))
|
2023-02-25 07:08:05 +00:00
|
|
|
|
2023-02-25 05:24:07 +00:00
|
|
|
this.log.info("Server assets loaded")
|
|
|
|
}
|
|
|
|
|
|
|
|
template (name: string, data : ServerData) {
|
|
|
|
let html : string = this.templates.find(el => el.name === name).data
|
|
|
|
for (let key of Object.keys(data)) {
|
|
|
|
html = html.replace(`{{${key}}}`, data[key])
|
|
|
|
}
|
|
|
|
return html
|
|
|
|
}
|
|
|
|
|
2023-02-25 07:08:56 +00:00
|
|
|
async startWss () {
|
|
|
|
try {
|
|
|
|
this.wss = new WebSocketServer({ port: this.wsPort })
|
|
|
|
} catch (err) {
|
|
|
|
this.log.error(err)
|
|
|
|
return
|
|
|
|
}
|
2023-02-25 16:59:56 +00:00
|
|
|
this.wss.on('connection', async function (ws : WebSocket) {
|
|
|
|
ws.on("message", function (data : string ) {
|
|
|
|
let obj : any = JSON.parse(data)
|
2023-02-25 17:40:35 +00:00
|
|
|
//this.log.info(data)
|
2023-02-25 16:59:56 +00:00
|
|
|
if (obj.id && this.queue[obj.id]) {
|
|
|
|
this.queue[obj.id](obj)
|
|
|
|
delete this.queue[obj.id]
|
2023-02-25 17:40:35 +00:00
|
|
|
this.log.info(`${obj.action} complete`)
|
2023-02-25 16:59:56 +00:00
|
|
|
}
|
|
|
|
}.bind(this))
|
|
|
|
|
|
|
|
ws.on('close', function () {
|
|
|
|
this.log.info('Client disconnected')
|
|
|
|
}.bind(this))
|
|
|
|
|
|
|
|
await this.cmd(ws, 'mcopy')
|
|
|
|
this.log.info('Client connected')
|
|
|
|
|
|
|
|
}.bind(this))
|
|
|
|
this.log.info(`Websocket server started!`)
|
2023-02-25 07:08:56 +00:00
|
|
|
this.log.info(`WSS [ ws://localhost:${this.wsPort} ]`)
|
|
|
|
}
|
|
|
|
|
|
|
|
async startHttp () {
|
2023-02-25 07:08:05 +00:00
|
|
|
return new Promise(function (resolve : Function, reject : Function) {
|
|
|
|
this.httpd = this.http.listen(this.port, function () {
|
2023-02-25 16:59:56 +00:00
|
|
|
this.log.info(`HTTP server started!`)
|
2023-02-25 07:08:05 +00:00
|
|
|
this.log.info(`URL [ http://localhost:${this.port} ]`)
|
|
|
|
return resolve(true)
|
|
|
|
}.bind(this))
|
2023-02-25 05:24:07 +00:00
|
|
|
}.bind(this))
|
|
|
|
}
|
|
|
|
|
2023-02-25 07:08:56 +00:00
|
|
|
async start () {
|
|
|
|
await this.startHttp()
|
|
|
|
await this.startWss()
|
2023-02-25 16:59:56 +00:00
|
|
|
this.interval = setInterval(async function () {
|
|
|
|
await this.cmdAll('ping');
|
|
|
|
}.bind(this), this.intervalPeriod)
|
2023-02-25 07:08:56 +00:00
|
|
|
this.isActive = true
|
|
|
|
}
|
|
|
|
|
2023-02-25 16:59:56 +00:00
|
|
|
async stopHttp() {
|
2023-02-25 07:08:05 +00:00
|
|
|
return new Promise(function (resolve : Function, reject : Function) {
|
|
|
|
return this.httpd.close(function () {
|
2023-02-25 16:59:56 +00:00
|
|
|
this.log.info(`HTTP server stopped :(`)
|
2023-02-25 07:08:56 +00:00
|
|
|
return resolve(false)
|
2023-02-25 07:08:05 +00:00
|
|
|
}.bind(this))
|
|
|
|
}.bind(this))
|
2023-02-25 05:24:07 +00:00
|
|
|
}
|
|
|
|
|
2023-02-25 16:59:56 +00:00
|
|
|
async stop () {
|
|
|
|
await this.stopHttp()
|
|
|
|
clearInterval(this.interval)
|
|
|
|
this.isActive = false
|
|
|
|
}
|
|
|
|
|
2023-02-25 05:24:07 +00:00
|
|
|
index (req : Request, res : Response, next : Function) {
|
|
|
|
const html : string = this.template('index', { PORT : `${this.port}` })
|
2023-02-25 07:08:05 +00:00
|
|
|
this.log.info('GET /')
|
2023-02-25 05:24:07 +00:00
|
|
|
return res.send(html)
|
|
|
|
}
|
|
|
|
script (req : Request, res : Response, next : Function) {
|
|
|
|
const js : string = this.template('script', { PORT : `${this.wsPort}` })
|
2023-02-25 07:08:05 +00:00
|
|
|
res.contentType('text/javascript')
|
|
|
|
this.log.info('GET /script.js')
|
|
|
|
return res.send(js)
|
2023-02-25 05:24:07 +00:00
|
|
|
}
|
2023-02-25 07:08:56 +00:00
|
|
|
|
|
|
|
async image (req : Request, res : Response, next : Function) {
|
|
|
|
let filePath : string
|
|
|
|
if (req.params && req.params.key) {
|
|
|
|
if (this.proxy[req.params.key]) {
|
|
|
|
filePath = this.proxy[req.params.key].path
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return new Promise(function (resolve : Function, reject: Function) {
|
|
|
|
return res.sendFile(filePath, function (err : any) {
|
|
|
|
if (err) {
|
|
|
|
res.status(err.status).end()
|
|
|
|
return reject(err)
|
|
|
|
}
|
|
|
|
return resolve(true)
|
|
|
|
})
|
|
|
|
}.bind(this))
|
|
|
|
}
|
|
|
|
|
|
|
|
public addProxy (key : string, filePath : string) {
|
2023-02-25 16:59:56 +00:00
|
|
|
//wipe every time
|
|
|
|
this.proxy = {}
|
2023-02-25 07:08:56 +00:00
|
|
|
this.proxy[key] = {
|
|
|
|
path : filePath,
|
|
|
|
mime : mime.getType(filePath)
|
|
|
|
}
|
|
|
|
this.log.info(`Added proxy image [${key}]`)
|
|
|
|
}
|
2023-02-25 16:59:56 +00:00
|
|
|
|
|
|
|
public async cmdAll (action : string, options : any = {}) {
|
|
|
|
const cmds : any[] = []
|
|
|
|
if (this.isActive && this.wss.clients.size > 0) {
|
|
|
|
this.wss.clients.forEach(function (ws : WebSocket) {
|
|
|
|
cmds.push(this.cmd(ws, action, options))
|
|
|
|
}.bind(this))
|
|
|
|
return await Promise.all(cmds)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WSS
|
|
|
|
**/
|
|
|
|
|
|
|
|
public async cmd (ws : WebSocket, action : string, options : any = {}) {
|
|
|
|
const id : string = uuidv4()
|
|
|
|
let obj = {
|
|
|
|
id, action
|
|
|
|
}
|
|
|
|
let str : string
|
|
|
|
|
|
|
|
obj = Object.assign(obj, options)
|
|
|
|
str = JSON.stringify(obj)
|
|
|
|
ws.send(str)
|
|
|
|
|
|
|
|
return new Promise(function (resolve : Function, reject: Function) {
|
|
|
|
this.queue[id] = function (obj : any) {
|
|
|
|
return resolve(obj)
|
|
|
|
}
|
|
|
|
//setTimeout() ?
|
|
|
|
}.bind(this))
|
|
|
|
}
|
2023-02-25 07:08:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = function () {
|
|
|
|
return new Server()
|
2023-02-25 05:24:07 +00:00
|
|
|
}
|