improve login system on the server
This commit is contained in:
parent
d24929da0c
commit
ee2e192204
6 changed files with 86 additions and 25 deletions
|
@ -2,11 +2,17 @@ import EventEmitter from "events";
|
||||||
import zlib from "zlib";
|
import zlib from "zlib";
|
||||||
import { WebSocket } from "ws";
|
import { WebSocket } from "ws";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
import { logger } from "./common.js";
|
||||||
|
|
||||||
|
const log = logger("log", "DiscordClient");
|
||||||
|
const logError = logger("error", "DiscordClient");
|
||||||
|
const logWarn = logger("warn", "DiscordClient");
|
||||||
|
|
||||||
const opcodes = {
|
const opcodes = {
|
||||||
EVENT: 0,
|
EVENT: 0,
|
||||||
CLIENT_HEARTBEAT: 1,
|
CLIENT_HEARTBEAT: 1,
|
||||||
IDENTIFY: 2,
|
IDENTIFY: 2,
|
||||||
|
RECONNECT: 7,
|
||||||
INVALID_SESSION: 9,
|
INVALID_SESSION: 9,
|
||||||
HELLO: 10,
|
HELLO: 10,
|
||||||
HEARTBEAT_ACK: 11,
|
HEARTBEAT_ACK: 11,
|
||||||
|
@ -82,7 +88,7 @@ class DiscordClient extends EventEmitter {
|
||||||
try {
|
try {
|
||||||
message = JSON.parse(message);
|
message = JSON.parse(message);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("error: DiscordClient: on 'message': failed to parse incoming message as JSON", e);
|
logError("on 'message': failed to parse incoming message as JSON", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +100,7 @@ class DiscordClient extends EventEmitter {
|
||||||
|
|
||||||
switch (message.op) {
|
switch (message.op) {
|
||||||
case opcodes.HELLO: {
|
case opcodes.HELLO: {
|
||||||
console.log(`DiscordClient: HELLO; heartbeat_interval=${payload.heartbeat_interval}`);
|
log(`HELLO; heartbeat_interval=${payload.heartbeat_interval}`);
|
||||||
this._setHeartbeat(payload.heartbeat_interval);
|
this._setHeartbeat(payload.heartbeat_interval);
|
||||||
|
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
|
@ -107,7 +113,7 @@ class DiscordClient extends EventEmitter {
|
||||||
case opcodes.EVENT: {
|
case opcodes.EVENT: {
|
||||||
switch (message.t) {
|
switch (message.t) {
|
||||||
case "READY": {
|
case "READY": {
|
||||||
console.log("DiscordClient: READY");
|
log("READY");
|
||||||
this.user = payload.user;
|
this.user = payload.user;
|
||||||
this.sessionId = payload.session_id;
|
this.sessionId = payload.session_id;
|
||||||
this.guilds = payload.guilds;
|
this.guilds = payload.guilds;
|
||||||
|
@ -194,22 +200,28 @@ class DiscordClient extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
case opcodes.INVALID_SESSION: {
|
case opcodes.INVALID_SESSION: {
|
||||||
console.error("DiscordClient: INVALID_SESSION - please check your authentication token");
|
logError("INVALID_SESSION - please check your authentication token");
|
||||||
console.error("DiscordClient: INVALID_SESSION: will not reconnect");
|
logError("INVALID_SESSION: will not reconnect");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case opcodes.RECONNECT: {
|
||||||
|
log("gateway is requesting reconnect (payload RECONNECT)");
|
||||||
|
this.connect();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
console.warn(`warn: DiscordClient: got unhandled opcode "${message.op}"`);
|
logWarn(`got unhandled opcode "${message.op}"`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
console.log("DiscordClient: connecting...");
|
log("connecting...");
|
||||||
if (this.ws) {
|
if (this.ws) {
|
||||||
console.log("DiscordClient: a websocket connection already exists, killing...");
|
log("a websocket connection already exists, killing...");
|
||||||
this.ws.removeAllListeners();
|
this.ws.removeAllListeners();
|
||||||
this.ws.close();
|
this.ws.close();
|
||||||
this.ws = null;
|
this.ws = null;
|
||||||
|
@ -233,26 +245,26 @@ class DiscordClient extends EventEmitter {
|
||||||
|
|
||||||
|
|
||||||
ws.on("open", () => {
|
ws.on("open", () => {
|
||||||
console.log("DiscordClient: WebSocket 'open'")
|
log("WebSocket 'open'");
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("close", (code, reason) => {
|
ws.on("close", (code, reason) => {
|
||||||
reason = reason.toString();
|
reason = reason.toString();
|
||||||
console.error(`DiscordClient: on 'close': disconnected from gateway: code '${code}', reason '${reason}'`);
|
logError(`on 'close': disconnected from gateway: code '${code}', reason '${reason}'`);
|
||||||
|
|
||||||
this.emit("close", code, reason);
|
this.emit("close", code, reason);
|
||||||
this._setHeartbeat(-1);
|
this._setHeartbeat(-1);
|
||||||
if (skipReconnectFor.includes(code)) {
|
if (skipReconnectFor.includes(code)) {
|
||||||
console.error("DiscordClient: on 'close': the exit code above is in skipReconnectFor, and thus the server will not reconnect.");
|
logError("on 'close': the exit code above is in skipReconnectFor, and thus the server will not reconnect.");
|
||||||
} else {
|
} else {
|
||||||
console.log("DiscordClient: on 'close': the client will now attempt to reconnect...");
|
log("on 'close': the client will now attempt to reconnect...");
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("error", (e) => {
|
ws.on("error", (e) => {
|
||||||
console.error("DiscordClient: on 'error': websocket error:", e);
|
logError("on 'error': websocket error:", e);
|
||||||
console.log("DiscordClient: on 'error': reconnecting due to previous websocket error...");
|
log("on 'error': reconnecting due to previous websocket error...");
|
||||||
this._setHeartbeat(-1);
|
this._setHeartbeat(-1);
|
||||||
this.connect();
|
this.connect();
|
||||||
});
|
});
|
||||||
|
|
|
@ -99,7 +99,6 @@ class GatewayServer {
|
||||||
try {
|
try {
|
||||||
message = JSON.parse(message.toString());
|
message = JSON.parse(message.toString());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("GatewayServer: payload decode error", e);
|
|
||||||
return ws.close(4000, "Payload error.");
|
return ws.close(4000, "Payload error.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +112,6 @@ class GatewayServer {
|
||||||
try {
|
try {
|
||||||
user = await decodeToken(message.d.token);
|
user = await decodeToken(message.d.token);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error(e);
|
|
||||||
ws.close(4001, "Bad token.");
|
ws.close(4001, "Bad token.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
32
common.js
32
common.js
|
@ -1,4 +1,4 @@
|
||||||
import { discordToken, watchedGuildIds } from "./config.js";
|
import { discordToken, logContextMap, watchedGuildIds } from "./config.js";
|
||||||
import DiscordClient from "./DiscordClient.js";
|
import DiscordClient from "./DiscordClient.js";
|
||||||
import WatchedGuild from "./WatchedGuild.js";
|
import WatchedGuild from "./WatchedGuild.js";
|
||||||
|
|
||||||
|
@ -13,6 +13,36 @@ export function wait(time, shouldReject=false) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function logger(sink, context) {
|
||||||
|
let sinkFunction;
|
||||||
|
switch (sink) {
|
||||||
|
case "log": {
|
||||||
|
sinkFunction = console.log;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "warn": {
|
||||||
|
sinkFunction = console.warn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "error": {
|
||||||
|
sinkFunction = console.error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
sinkFunction = () => {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logContextMap[context] && logContextMap[context][sink]) {
|
||||||
|
return (...e) => {
|
||||||
|
sinkFunction(`[${context}]`, ...e);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return (...e) => {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bot.once("READY", () => {
|
bot.once("READY", () => {
|
||||||
watchedGuildIds.forEach(id => {
|
watchedGuildIds.forEach(id => {
|
||||||
const watchedGuild = new WatchedGuild();
|
const watchedGuild = new WatchedGuild();
|
||||||
|
|
17
config.js
17
config.js
|
@ -3,3 +3,20 @@ export const watchedGuildIds = ["822089558886842418", "736292509134749807"];
|
||||||
export const jwtSecret = process.env.JWT_SECRET;
|
export const jwtSecret = process.env.JWT_SECRET;
|
||||||
export const discordToken = process.env.DISCORD_TOKEN;
|
export const discordToken = process.env.DISCORD_TOKEN;
|
||||||
export const dangerousAdminMode = true;
|
export const dangerousAdminMode = true;
|
||||||
|
export const logContextMap = {
|
||||||
|
DiscordClient: {
|
||||||
|
log: true,
|
||||||
|
warn: true,
|
||||||
|
error: true,
|
||||||
|
},
|
||||||
|
ServerMain: {
|
||||||
|
log: true,
|
||||||
|
warn: true,
|
||||||
|
error: true,
|
||||||
|
},
|
||||||
|
API: {
|
||||||
|
log: true,
|
||||||
|
warn: true,
|
||||||
|
error: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
6
index.js
6
index.js
|
@ -2,9 +2,11 @@ import http from "node:http";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import apiRoute from "./routes/api.js";
|
import apiRoute from "./routes/api.js";
|
||||||
import { mainHttpListenPort } from "./config.js";
|
import { mainHttpListenPort } from "./config.js";
|
||||||
import { bot } from "./common.js";
|
import { bot, logger } from "./common.js";
|
||||||
import GatewayServer from "./GatewayServer.js";
|
import GatewayServer from "./GatewayServer.js";
|
||||||
|
|
||||||
|
const log = logger("log", "ServerMain");
|
||||||
|
|
||||||
// might introduce bugs and probably a bad idea
|
// might introduce bugs and probably a bad idea
|
||||||
Object.freeze(Object.prototype);
|
Object.freeze(Object.prototype);
|
||||||
Object.freeze(Object);
|
Object.freeze(Object);
|
||||||
|
@ -21,6 +23,6 @@ app.use("/", express.static("frontend/public/"));
|
||||||
app.use("/api/v1", apiRoute);
|
app.use("/api/v1", apiRoute);
|
||||||
|
|
||||||
httpServer.listen(mainHttpListenPort, () => {
|
httpServer.listen(mainHttpListenPort, () => {
|
||||||
console.log(`server main: listen on ${mainHttpListenPort}`);
|
log(`http listen on ${mainHttpListenPort}`);
|
||||||
bot.connect();
|
bot.connect();
|
||||||
});
|
});
|
|
@ -1,8 +1,10 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { guildMap, wait } from "../common.js";
|
import { guildMap, logger } from "../common.js";
|
||||||
import { dangerousAdminMode } from "../config.js";
|
import { dangerousAdminMode } from "../config.js";
|
||||||
import { checkAuth, createToken } from "../tokens.js";
|
import { checkAuth, createToken } from "../tokens.js";
|
||||||
|
|
||||||
|
const error = logger("error", "API");
|
||||||
|
|
||||||
const router = express();
|
const router = express();
|
||||||
|
|
||||||
router.get("/", (req, res) => {
|
router.get("/", (req, res) => {
|
||||||
|
@ -69,7 +71,7 @@ router.post("/guilds/:guildId/channels/:channelId/messages/create", checkAuth(as
|
||||||
await guild.discordSendMessage(messageContent, channelId, username, avatarURL);
|
await guild.discordSendMessage(messageContent, channelId, username, avatarURL);
|
||||||
res.status(201).send({ error: false });
|
res.status(201).send({ error: false });
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("server main: api: message create: error: ", e);
|
error("[message create] [error]", e);
|
||||||
res.status(500).send({ error: true, message: "ERROR_MESSAGE_SEND_FAILURE" });
|
res.status(500).send({ error: true, message: "ERROR_MESSAGE_SEND_FAILURE" });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -88,7 +90,7 @@ router.get("/guilds/:guildId/channels", checkAuth(async (req, res) => {
|
||||||
try {
|
try {
|
||||||
res.status(200).send({ error: false, channels: guild.userFacingChannelList() });
|
res.status(200).send({ error: false, channels: guild.userFacingChannelList() });
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("server main: api: guild get channels: error: ", e);
|
error("[guild get channels] [error]", e);
|
||||||
res.status(500).send({ error: true, message: "ERROR_CHANNELS_FETCH_FAILURE" });
|
res.status(500).send({ error: true, message: "ERROR_CHANNELS_FETCH_FAILURE" });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -107,7 +109,7 @@ router.get("/guilds/:guildId", checkAuth(async (req, res) => {
|
||||||
try {
|
try {
|
||||||
res.status(200).send({ error: false, guild: guild.guildObject });
|
res.status(200).send({ error: false, guild: guild.guildObject });
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("server main: api: guild get info: error: ", e);
|
error("[guild get info]", e);
|
||||||
res.status(500).send({ error: true, message: "ERROR_GUILD_INFO_FETCH_FAILURE" });
|
res.status(500).send({ error: true, message: "ERROR_GUILD_INFO_FETCH_FAILURE" });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -133,7 +135,7 @@ router.get("/guilds/:guildId/events/poll", checkAuth(async (req, res) => {
|
||||||
guild.holdForEvent(15000)
|
guild.holdForEvent(15000)
|
||||||
.then(result => res.status(200).send({ error: false, event: result }));
|
.then(result => res.status(200).send({ error: false, event: result }));
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("server main: api: guild poll events: error: ", e);
|
error("[guild poll events]", e);
|
||||||
res.status(500).send({ error: true, message: "ERROR_POLL_FAILURE" });
|
res.status(500).send({ error: true, message: "ERROR_POLL_FAILURE" });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -157,7 +159,7 @@ router.get("/events/poll", checkAuth(async (req, res) => {
|
||||||
res.status(200).send({ error: false, event: e })
|
res.status(200).send({ error: false, event: e })
|
||||||
});
|
});
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("server main: api: guild poll events: error: ", e);
|
error("[guilds poll events]", e);
|
||||||
res.status(500).send({ error: true, message: "ERROR_POLL_FAILURE" });
|
res.status(500).send({ error: true, message: "ERROR_POLL_FAILURE" });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in a new issue