skynet/src/index.js

138 lines
4.3 KiB
JavaScript
Raw Normal View History

2018-09-15 17:27:45 +00:00
const express = require("express");
const low = require("lowdb");
const FileAsync = require('lowdb/adapters/FileAsync');
const websocket = require('ws');
const humanID = require("human-id");
2018-10-05 19:44:11 +00:00
const cors = require("cors");
2018-09-15 17:27:45 +00:00
const makeID = () => humanID({
separator: "-",
capitalize: false
});
const send = (sock, msg) => sock.send(JSON.stringify(msg));
const validateChannel = channel => { if (typeof channel !== "string" && typeof channel !== "number") throw new Error("Invalid type for channel!"); }
const wildcardChannel = "*";
2018-10-03 17:18:11 +00:00
let messageLogs = [];
2018-09-15 17:27:45 +00:00
const commands = {
open(ws, message) {
const channel = message.channel;
validateChannel(channel);
console.log("Opening", channel, "on", ws.ID);
// Avoid duplicate channels
if (!ws.channels.includes(channel)) ws.channels.push(channel);
return { channels: ws.channels };
},
close(ws, message) {
const channel = message.channel;
validateChannel(channel);
console.log("Closing", channel, "on", ws.ID);
// Remove channel from list if exists
const index = ws.channels.indexOf(channel);
if (index > -1) {
ws.channels.splice(index, 1);
2018-09-16 14:05:58 +00:00
return { closed: channel }
2018-09-15 17:27:45 +00:00
} else {
throw new Error("Channel " + channel + " not open.");
}
},
message(ws, message, db, wss) {
const toSend = {
...message,
ID: makeID(),
2018-10-03 17:34:03 +00:00
senderID: ws.ID,
time: new Date().getTime()
2018-09-15 17:27:45 +00:00
};
2018-09-16 15:19:21 +00:00
console.log("Sending", message.message, "on", message.channel, "from", ws.ID);
2018-09-15 21:08:32 +00:00
2018-09-15 17:27:45 +00:00
// Send message to all clients listening on this channel or the wildcard channel.
const sentTo = [];
wss.clients.forEach(client => {
if ((client.channels.includes(message.channel) || client.channels.includes(wildcardChannel)) && client.readyState === websocket.OPEN && client.ID !== ws.ID) {
2018-09-15 17:27:45 +00:00
client.send(JSON.stringify(toSend));
sentTo.push(client.ID);
}
});
2018-10-03 17:18:11 +00:00
toSend.sentTo = sentTo;
messageLogs.unshift(toSend);
return { sentTo, ID: toSend.ID };
2018-09-15 17:27:45 +00:00
},
2018-10-03 17:18:11 +00:00
ID(ws) {
2018-09-16 14:05:58 +00:00
return { ID: ws.ID };
2018-10-03 17:18:11 +00:00
},
log(ws, data) {
const start = data.start || 0;
const end = data.end || 100;
return { log: messageLogs.slice(start, end) };
2018-09-15 17:27:45 +00:00
}
}
low(new FileAsync(process.env.DB || "./db.json")).then(db => {
db.defaults({ channels: [] }).write();
const app = express();
const expressWSS = require("express-ws")(app);
2018-10-05 19:44:11 +00:00
app.use(cors());
app.use(express.static(__dirname));
2018-09-15 17:27:45 +00:00
app.ws("/connect/", function(ws, req) {
ws.channels = [];
ws.ID = makeID();
ws.on("message", msg => {
let data;
try {
data = JSON.parse(msg);
} catch(e) {
console.log("Invalid data:", msg);
send(ws, {
type: "error",
error: "JSON expected."
})
}
// If sent data has required keys, execute corresponding command
if (typeof data === "object" && data !== null && data.type) {
const cmd = data.type;
try {
const result = commands[cmd](ws, data, db, expressWSS.getWss());
if (result !== null && result !== undefined) {
if ("then" in result) { // Allow returning promises
result.then(res => send(ws, {
type: "result",
for: cmd,
...res
}));
} else {
send(ws, {
type: "result",
for: cmd,
...result
});
}
}
} catch(e) {
// Send & log error
console.log(e);
send(ws, {
type: "error",
for: cmd,
error: e.message
})
}
}
});
});
app.listen(parseInt(process.env.PORT) || 4567);
});