120 lines
3.3 KiB
JavaScript
120 lines
3.3 KiB
JavaScript
const express = require("express")
|
|
const websocket = require("ws")
|
|
const cors = require("cors")
|
|
const level = require("level")
|
|
const expressWS = require("express-ws")
|
|
const cuid = require("cuid")
|
|
|
|
const db = level("./db.level", {
|
|
valueEncoding: "json"
|
|
})
|
|
|
|
const app = express()
|
|
const eWSS = expressWS(app)
|
|
|
|
app.use(cors())
|
|
app.use(express.static(__dirname))
|
|
|
|
const validateChannel = channel => { if (typeof channel !== "string" && typeof channel !== "number") throw new Error("Invalid type for channel!") }
|
|
const wildcardChannel = "*"
|
|
|
|
const messageLog = []
|
|
|
|
const broadcast = (wss, msg, sender) => {
|
|
const toSend = {
|
|
...msg,
|
|
type: "message",
|
|
ID: cuid(),
|
|
time: new Date().getTime()
|
|
}
|
|
|
|
wss.clients.forEach(socket => {
|
|
let send = socket.readyState === websocket.OPEN
|
|
if (socket.type === "client") { send = send && (socket.channels.includes(msg.channel) || socket.channels.includes(wildcardChannel)) && socket !== sender }
|
|
else if (socket.type === "peer") { send = send && true }
|
|
if (send) {
|
|
socket.send(JSON.stringify(toSend))
|
|
}
|
|
})
|
|
|
|
messageLog.unshift(toSend) // push to front for nice ordering
|
|
}
|
|
|
|
const clientCommands = {
|
|
open(message, sendBack, ctx) {
|
|
const channel = message.channel
|
|
const ws = ctx.ws
|
|
|
|
validateChannel(channel)
|
|
|
|
if (!ws.channels.includes(channel)) ws.channels.push(channel)
|
|
|
|
sendBack({ channels: ws.channels })
|
|
},
|
|
close(message, sendBack, ctx) {
|
|
const channel = message.channel
|
|
const ws = ctx.ws
|
|
validateChannel(channel)
|
|
|
|
// Remove channel from list if exists
|
|
const index = ws.channels.indexOf(channel)
|
|
if (index > -1) {
|
|
ws.channels.splice(index, 1)
|
|
sendBack({ channels: ws.channels })
|
|
} else {
|
|
throw new Error("Channel " + channel + " not open.")
|
|
}
|
|
},
|
|
log(message, sendBack) {
|
|
const start = message.start || 0
|
|
const end = message.end || 100
|
|
sendBack({ log: messageLog.slice(start, end) })
|
|
},
|
|
message(message, sendBack, { wss, ws }) {
|
|
broadcast(wss, message, ws)
|
|
}
|
|
}
|
|
|
|
const ctx = {
|
|
wss: eWSS.getWss(),
|
|
db
|
|
}
|
|
|
|
const websocketCommandProcessor = (ws, commands) => {
|
|
const send = x => ws.send(JSON.stringify(x))
|
|
ws.on("message", raw => {
|
|
try {
|
|
const message = JSON.parse(raw)
|
|
if (typeof message === "object" && message !== null && message !== undefined) {
|
|
const commandName = message.type
|
|
const command = commands[commandName]
|
|
if (!command) { throw new Error("No such command " + commandName) }
|
|
|
|
const sendBack = x => send({type: "result", for: commandName, ...x})
|
|
|
|
command(message, sendBack, { ws, ...ctx })
|
|
} else {
|
|
throw new Error("Message must be object")
|
|
}
|
|
} catch(e) {
|
|
console.error(e)
|
|
send({
|
|
type: "error",
|
|
error: e.toString()
|
|
})
|
|
return
|
|
}
|
|
})
|
|
}
|
|
|
|
app.ws("/connect", (ws, req) => {
|
|
ws.channels = []
|
|
ws.type = "client"
|
|
websocketCommandProcessor(ws, clientCommands)
|
|
})
|
|
|
|
app.ws("/peer", (ws, req) => {
|
|
|
|
})
|
|
|
|
app.listen(parseInt(process.env.PORT) || 4567) |