forked from Hri7566/mpp-server-dev2
Implement events
This commit is contained in:
parent
683f14ddbc
commit
d46078efa1
|
@ -0,0 +1,3 @@
|
||||||
|
export class Gateway {
|
||||||
|
public hasSentDevices: boolean = false;
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
import { WebSocket } from "uWebSockets.js";
|
||||||
|
import { createColor, createID, createUserID } from "../util/id";
|
||||||
|
import { decoder, encoder } from "../util/helpers";
|
||||||
|
import EventEmitter from "events";
|
||||||
|
import { ChannelSettings, ClientEvents, UserFlags } from "../util/types";
|
||||||
|
import { User } from "@prisma/client";
|
||||||
|
import { createUser, readUser } from "../data/user";
|
||||||
|
import { eventGroups } from "./events";
|
||||||
|
import { loadConfig } from "../util/config";
|
||||||
|
import { Gateway } from "./Gateway";
|
||||||
|
|
||||||
|
interface UsersConfig {
|
||||||
|
defaultName: string;
|
||||||
|
defaultFlags: UserFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
const usersConfig = loadConfig<UsersConfig>("config/users.yml", {
|
||||||
|
defaultName: "Anonymous",
|
||||||
|
defaultFlags: {
|
||||||
|
volume: 100
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export class Socket extends EventEmitter {
|
||||||
|
private id: string;
|
||||||
|
private _id: string;
|
||||||
|
private ip: string;
|
||||||
|
private user: User | null = null;
|
||||||
|
|
||||||
|
public gateway = new Gateway();
|
||||||
|
|
||||||
|
public desiredChannel: {
|
||||||
|
_id: string | undefined;
|
||||||
|
set: Partial<ChannelSettings>;
|
||||||
|
} = {
|
||||||
|
_id: undefined,
|
||||||
|
set: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(private ws: WebSocket<unknown>) {
|
||||||
|
super();
|
||||||
|
this.ip = decoder.decode(this.ws.getRemoteAddressAsText());
|
||||||
|
|
||||||
|
// Participant ID
|
||||||
|
this.id = createID();
|
||||||
|
|
||||||
|
// User ID
|
||||||
|
this._id = createUserID(this.getIP());
|
||||||
|
// *cough* lapis
|
||||||
|
|
||||||
|
this.loadUser();
|
||||||
|
|
||||||
|
this.bindEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getIP() {
|
||||||
|
return this.ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUserID() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setChannel(_id: string, set: Partial<ChannelSettings>) {
|
||||||
|
this.desiredChannel._id = _id;
|
||||||
|
this.desiredChannel.set = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindEventListeners() {
|
||||||
|
for (const group of eventGroups) {
|
||||||
|
// TODO Check event group permissions
|
||||||
|
if (group.id == "admin") continue;
|
||||||
|
|
||||||
|
for (const event of group.eventList) {
|
||||||
|
this.on(event.id, event.callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendArray<EventID extends keyof ClientEvents>(
|
||||||
|
arr: ClientEvents[EventID][]
|
||||||
|
) {
|
||||||
|
this.ws.send(encoder.encode(JSON.stringify(arr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadUser() {
|
||||||
|
let user = await readUser(this._id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
await createUser(
|
||||||
|
this._id,
|
||||||
|
usersConfig.defaultName,
|
||||||
|
createColor(this.ip),
|
||||||
|
usersConfig.defaultFlags
|
||||||
|
);
|
||||||
|
|
||||||
|
user = await readUser(this._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUser() {
|
||||||
|
if (this.user) {
|
||||||
|
return this.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUserFlags() {
|
||||||
|
if (this.user) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(this.user.flags) as UserFlags;
|
||||||
|
} catch (err) {
|
||||||
|
return {} as UserFlags;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParticipant() {
|
||||||
|
if (this.user) {
|
||||||
|
const flags = this.getUserFlags();
|
||||||
|
let facadeID = this._id;
|
||||||
|
|
||||||
|
if (flags) {
|
||||||
|
if (flags.override_id) {
|
||||||
|
facadeID = flags.override_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: facadeID,
|
||||||
|
name: this.user.name,
|
||||||
|
color: this.user.color,
|
||||||
|
id: this.id
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParticipantID() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import "./events/user";
|
||||||
|
import "./events/admin";
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { ServerEventListener, ServerEvents } from "../util/types";
|
||||||
|
|
||||||
|
export class EventGroup {
|
||||||
|
public eventList = new Array<ServerEventListener<any>>();
|
||||||
|
constructor(public id: string) {}
|
||||||
|
|
||||||
|
public add(listener: ServerEventListener<any>) {
|
||||||
|
this.eventList.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(listener: ServerEventListener<any>) {
|
||||||
|
this.eventList.splice(this.eventList.indexOf(listener), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const eventGroups = new Array<EventGroup>();
|
||||||
|
|
||||||
|
import "./events.inc";
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { ServerEventListener } from "../../../../util/types";
|
||||||
|
import { eventGroups } from "../../../events";
|
||||||
|
|
||||||
|
export const color: ServerEventListener<"color"> = {
|
||||||
|
id: "color",
|
||||||
|
callback: (msg, socket) => {
|
||||||
|
// TODO color
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { EventGroup, eventGroups } from "../../events";
|
||||||
|
|
||||||
|
export const EVENT_GROUP_ADMIN = new EventGroup("user");
|
||||||
|
|
||||||
|
import { color } from "./handlers/color";
|
||||||
|
|
||||||
|
EVENT_GROUP_ADMIN.add(color);
|
||||||
|
|
||||||
|
eventGroups.push(EVENT_GROUP_ADMIN);
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { ServerEventListener } from "../../../../util/types";
|
||||||
|
import { eventGroups } from "../../../events";
|
||||||
|
|
||||||
|
export const devices: ServerEventListener<"devices"> = {
|
||||||
|
id: "devices",
|
||||||
|
callback: (msg, socket) => {
|
||||||
|
socket.gateway.hasSentDevices = true;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { ServerEventListener } from "../../../../util/types";
|
||||||
|
import { eventGroups } from "../../../events";
|
||||||
|
|
||||||
|
export const hi: ServerEventListener<"hi"> = {
|
||||||
|
id: "hi",
|
||||||
|
callback: (msg, socket) => {
|
||||||
|
// TODO Hi message tokens
|
||||||
|
let part = socket.getParticipant();
|
||||||
|
|
||||||
|
if (!part) {
|
||||||
|
part = {
|
||||||
|
_id: socket.getUserID(),
|
||||||
|
name: "Anonymous",
|
||||||
|
color: "#777",
|
||||||
|
id: socket.getParticipantID()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.sendArray([
|
||||||
|
{
|
||||||
|
m: "hi",
|
||||||
|
accountInfo: undefined,
|
||||||
|
permissions: undefined,
|
||||||
|
t: Date.now(),
|
||||||
|
u: part
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { EventGroup, eventGroups } from "../../events";
|
||||||
|
|
||||||
|
export const EVENTGROUP_USER = new EventGroup("user");
|
||||||
|
|
||||||
|
import { hi } from "./handlers/hi";
|
||||||
|
|
||||||
|
EVENTGROUP_USER.add(hi);
|
||||||
|
|
||||||
|
eventGroups.push(EVENTGROUP_USER);
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { WebSocket } from "uWebSockets.js";
|
||||||
|
import { Logger } from "../util/Logger";
|
||||||
|
import { Socket } from "./Socket";
|
||||||
|
import { hasOwn } from "../util/helpers";
|
||||||
|
|
||||||
|
const logger = new Logger("Message Handler");
|
||||||
|
|
||||||
|
export function handleMessage(socket: Socket, text: string) {
|
||||||
|
try {
|
||||||
|
const transmission = JSON.parse(text);
|
||||||
|
|
||||||
|
if (!Array.isArray(transmission)) {
|
||||||
|
logger.warn(
|
||||||
|
"Received message that isn't an array! --",
|
||||||
|
transmission,
|
||||||
|
" -- from",
|
||||||
|
socket.getUserID()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
for (const msg of transmission) {
|
||||||
|
if (!hasOwn(msg, "m")) continue;
|
||||||
|
socket.emit(msg.m, msg, socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn("Unable to decode message from", socket.getIP());
|
||||||
|
logger.error(err);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +1,97 @@
|
||||||
import { App, DEDICATED_COMPRESSOR_8KB } from "uWebSockets.js";
|
import {
|
||||||
|
App,
|
||||||
|
DEDICATED_COMPRESSOR_8KB,
|
||||||
|
HttpRequest,
|
||||||
|
HttpResponse,
|
||||||
|
WebSocket
|
||||||
|
} from "uWebSockets.js";
|
||||||
import { Logger } from "../util/Logger";
|
import { Logger } from "../util/Logger";
|
||||||
import { createUserID } from "../util/id";
|
import { createUserID } from "../util/id";
|
||||||
|
import { readFileSync, lstatSync } from "fs";
|
||||||
|
import { join } from "path/posix";
|
||||||
|
import { handleMessage } from "./message";
|
||||||
|
import { decoder } from "../util/helpers";
|
||||||
|
import { Socket } from "./Socket";
|
||||||
|
|
||||||
const logger = new Logger("WebSocket Server");
|
const logger = new Logger("WebSocket Server");
|
||||||
|
|
||||||
export const app = App()
|
export const app = App()
|
||||||
.get("/", (res, req) => {
|
.get("/*", async (res, req) => {
|
||||||
const url = req.getUrl();
|
const url = req.getUrl();
|
||||||
logger.debug(`${req.getMethod()} ${url} ${req}`);
|
const ip = decoder.decode(res.getRemoteAddressAsText());
|
||||||
res.writeStatus(`200 OK`).end("HI!");
|
// logger.debug(`${req.getMethod()} ${url} ${ip}`);
|
||||||
|
// res.writeStatus(`200 OK`).end("HI!");
|
||||||
|
const file = join("./public/", url);
|
||||||
|
|
||||||
|
// TODO Cleaner file serving
|
||||||
|
try {
|
||||||
|
const stats = lstatSync(file);
|
||||||
|
|
||||||
|
let data;
|
||||||
|
if (!stats.isDirectory()) {
|
||||||
|
data = readFileSync(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// logger.debug(filename);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
const index = readFileSync("./public/index.html");
|
||||||
|
|
||||||
|
if (!index) {
|
||||||
|
return void res
|
||||||
|
.writeStatus(`404 Not Found`)
|
||||||
|
.end("uh oh :(");
|
||||||
|
} else {
|
||||||
|
return void res.writeStatus(`200 OK`).end(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeStatus(`200 OK`).end(data);
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn("Unable to serve file at", file);
|
||||||
|
logger.error(err);
|
||||||
|
const index = readFileSync("./public/index.html");
|
||||||
|
|
||||||
|
if (!index) {
|
||||||
|
return void res.writeStatus(`404 Not Found`).end("uh oh :(");
|
||||||
|
} else {
|
||||||
|
return void res.writeStatus(`200 OK`).end(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.ws("/*", {
|
.ws("/*", {
|
||||||
idleTimeout: 30,
|
idleTimeout: 180,
|
||||||
maxBackpressure: 1024,
|
maxBackpressure: 1024,
|
||||||
maxPayloadLength: 8192,
|
maxPayloadLength: 8192,
|
||||||
compression: DEDICATED_COMPRESSOR_8KB,
|
compression: DEDICATED_COMPRESSOR_8KB,
|
||||||
|
|
||||||
open: ws => {
|
open: ((ws: WebSocket<unknown> & { socket: Socket }) => {
|
||||||
const ip = String(ws.getRemoteAddressAsText());
|
ws.socket = new Socket(ws);
|
||||||
const _id = createUserID(ip);
|
// logger.debug("Connection at " + ws.socket.getIP());
|
||||||
|
}) as (ws: WebSocket<unknown>) => void,
|
||||||
|
|
||||||
logger.debug(ip, _id);
|
message: ((
|
||||||
},
|
ws: WebSocket<unknown> & { socket: Socket },
|
||||||
|
message,
|
||||||
|
isBinary
|
||||||
|
) => {
|
||||||
|
const msg = decoder.decode(message);
|
||||||
|
handleMessage(ws.socket, msg);
|
||||||
|
}) as (
|
||||||
|
ws: WebSocket<unknown>,
|
||||||
|
message: ArrayBuffer,
|
||||||
|
isBinary: boolean
|
||||||
|
) => void,
|
||||||
|
|
||||||
message: (ws, message, isBinary) => {
|
close: ((
|
||||||
const msg = String(message);
|
ws: WebSocket<unknown> & { socket: Socket },
|
||||||
logger.debug(msg);
|
code: number,
|
||||||
}
|
message: ArrayBuffer
|
||||||
|
) => {
|
||||||
|
// TODO handle close event
|
||||||
|
}) as (
|
||||||
|
ws: WebSocket<unknown>,
|
||||||
|
code: number,
|
||||||
|
message: ArrayBuffer
|
||||||
|
) => void
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue