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 { 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");
|
||||
|
||||
export const app = App()
|
||||
.get("/", (res, req) => {
|
||||
.get("/*", async (res, req) => {
|
||||
const url = req.getUrl();
|
||||
logger.debug(`${req.getMethod()} ${url} ${req}`);
|
||||
res.writeStatus(`200 OK`).end("HI!");
|
||||
const ip = decoder.decode(res.getRemoteAddressAsText());
|
||||
// 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("/*", {
|
||||
idleTimeout: 30,
|
||||
idleTimeout: 180,
|
||||
maxBackpressure: 1024,
|
||||
maxPayloadLength: 8192,
|
||||
compression: DEDICATED_COMPRESSOR_8KB,
|
||||
|
||||
open: ws => {
|
||||
const ip = String(ws.getRemoteAddressAsText());
|
||||
const _id = createUserID(ip);
|
||||
open: ((ws: WebSocket<unknown> & { socket: Socket }) => {
|
||||
ws.socket = new Socket(ws);
|
||||
// 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) => {
|
||||
const msg = String(message);
|
||||
logger.debug(msg);
|
||||
}
|
||||
close: ((
|
||||
ws: WebSocket<unknown> & { socket: Socket },
|
||||
code: number,
|
||||
message: ArrayBuffer
|
||||
) => {
|
||||
// TODO handle close event
|
||||
}) as (
|
||||
ws: WebSocket<unknown>,
|
||||
code: number,
|
||||
message: ArrayBuffer
|
||||
) => void
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue