displaying rudimentary logs
This commit is contained in:
@@ -20,7 +20,6 @@ export default class Database {
|
|||||||
this.logs = dbJson
|
this.logs = dbJson
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
console.log("saving db")
|
|
||||||
global.db.saveData()
|
global.db.saveData()
|
||||||
}, 5000)
|
}, 5000)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ import chalk from 'chalk'
|
|||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import * as useragent from "express-useragent";
|
import * as useragent from "express-useragent";
|
||||||
|
import forms from 'forms'
|
||||||
|
|
||||||
import "./util.js"
|
import "./util.js"
|
||||||
import Socket from './ws/ws.js'
|
import Clients from './ws/Clients.js'
|
||||||
import Database from "./db/db.js"
|
import Database from "./db/db.js"
|
||||||
import AuthHandler from './auth.js';
|
import AuthHandler from './auth.js';
|
||||||
|
|
||||||
export default class Server {
|
export default class Server {
|
||||||
|
store;
|
||||||
db;
|
db;
|
||||||
auth;
|
auth;
|
||||||
UIPath = path.join(global.__dirname, '../ui')
|
UIPath = path.join(global.__dirname, '../ui')
|
||||||
@@ -30,6 +32,10 @@ export default class Server {
|
|||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNewLog(log) {
|
||||||
|
global.Clients.broadcast(log)
|
||||||
|
}
|
||||||
|
|
||||||
verifyToken = (req, res, next) => {
|
verifyToken = (req, res, next) => {
|
||||||
const { token } = req.query;
|
const { token } = req.query;
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@@ -131,6 +137,10 @@ export default class Server {
|
|||||||
this.db = new Database()
|
this.db = new Database()
|
||||||
global.db = this.db
|
global.db = this.db
|
||||||
this.auth = new AuthHandler()
|
this.auth = new AuthHandler()
|
||||||
|
this.store = new forms.client()
|
||||||
|
this.store.connect()
|
||||||
|
this.store.watch("log", this.handleNewLog)
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(cors({ origin: '*' }));
|
app.use(cors({ origin: '*' }));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
@@ -146,11 +156,11 @@ export default class Server {
|
|||||||
app.use('/', router);
|
app.use('/', router);
|
||||||
|
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
global.Socket = new Socket(server);
|
global.Clients = new Clients(server);
|
||||||
const PORT = 4001;
|
const PORT = 4001;
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log("\n")
|
console.log("\n")
|
||||||
console.log(chalk.yellow("*************** Hyperia ***************"))
|
console.log(chalk.yellow("*************** Console ***************"))
|
||||||
console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`));
|
console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`));
|
||||||
console.log(chalk.yellow("***************************************"))
|
console.log(chalk.yellow("***************************************"))
|
||||||
console.log("\n")
|
console.log("\n")
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
import { WebSocket, WebSocketServer } from 'ws'
|
import { WebSocket, WebSocketServer } from 'ws'
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import ForumHandler from "./handlers/ForumHandler.js"
|
import handler from "./handler.js"
|
||||||
import MessagesHandler from "./handlers/MessagesHandler.js"
|
import chalk from 'chalk'
|
||||||
|
|
||||||
export default class Socket {
|
export default class Clients {
|
||||||
wss;
|
wss;
|
||||||
messageSchema = z.object({
|
messageSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
app: z.string(),
|
op: z.string().optional(),
|
||||||
operation: z.string().optional(),
|
|
||||||
msg: z.union([
|
msg: z.union([
|
||||||
z.object({}).passthrough(), // allows any object
|
z.object({}).passthrough(), // allows any object
|
||||||
z.array(z.any()) // allows any array
|
z.array(z.any()) // allows any array
|
||||||
]).optional()
|
]).optional()
|
||||||
})
|
})
|
||||||
.superRefine((data, ctx) => {
|
.superRefine((data, ctx) => {
|
||||||
if (data.operation !== "GET" && data.msg === undefined) {
|
if (data.op !== "GET" && data.msg === undefined) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
path: ["msg"],
|
path: ["msg"],
|
||||||
@@ -43,12 +42,17 @@ export default class Socket {
|
|||||||
|
|
||||||
const cookies = parseCookies(req.headers.cookie);
|
const cookies = parseCookies(req.headers.cookie);
|
||||||
const token = cookies.auth_token;
|
const token = cookies.auth_token;
|
||||||
if (!token) throw new Error("No auth token");
|
if (!token) {
|
||||||
|
console.error("No auth token");
|
||||||
|
return
|
||||||
|
}
|
||||||
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
ws.userEmail = payload.email;
|
ws.userEmail = payload.email;
|
||||||
|
|
||||||
ws.on('message', (msg) => {
|
ws.on('message', (msg) => {
|
||||||
this.handleMessage(msg, ws);
|
const text = msg.toString("utf8");
|
||||||
|
const data = JSON.parse(text);
|
||||||
|
this.handleMessage(data, ws);
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
@@ -62,7 +66,12 @@ export default class Socket {
|
|||||||
// Build a system where the ws obj is updated every time on navigate, so it already has context
|
// Build a system where the ws obj is updated every time on navigate, so it already has context
|
||||||
// this way, we can only send broadcast messages to clients that actually have that app / subapp open
|
// this way, we can only send broadcast messages to clients that actually have that app / subapp open
|
||||||
handleMessage = (msg, ws) => {
|
handleMessage = (msg, ws) => {
|
||||||
|
if(!this.messageSchema.safeParse(msg).success) {
|
||||||
|
console.log(chalk.red("Socket.handleMessage: Incoming ws message has incorrect format! ", this.messageSchema.safeParse(msg).error))
|
||||||
|
return
|
||||||
|
}
|
||||||
console.log("websocket message received: ", msg)
|
console.log("websocket message received: ", msg)
|
||||||
|
handler.handle(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(event) {
|
broadcast(event) {
|
||||||
14
server/ws/handler.js
Normal file
14
server/ws/handler.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export default class handler {
|
||||||
|
|
||||||
|
static handleGet(msg) {
|
||||||
|
// let data = forms.get("log", 10)
|
||||||
|
// return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle(msg, ws) {
|
||||||
|
switch(msg.op) {
|
||||||
|
case "GET":
|
||||||
|
return this.handleGet(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
const sendSchema = z.object({
|
|
||||||
forum: z.string(),
|
|
||||||
text: z.string(),
|
|
||||||
})
|
|
||||||
.strict()
|
|
||||||
|
|
||||||
const getSchema = z.object({
|
|
||||||
forum: z.string(),
|
|
||||||
number: z.number()
|
|
||||||
})
|
|
||||||
.strict()
|
|
||||||
|
|
||||||
|
|
||||||
export default class ForumHandler {
|
|
||||||
static handleSend(msg, ws) {
|
|
||||||
try {
|
|
||||||
global.db.posts.add(msg.text, msg.forum, ws.userEmail)
|
|
||||||
global.Socket.broadcast({event: "new-post", app: "FORUM", forum: msg.forum, msg: this.handleGet({forum: msg.forum, number: 100})})
|
|
||||||
return {success: true}
|
|
||||||
} catch(e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static handleGet(msg) {
|
|
||||||
let data = global.db.posts.get(msg.forum, msg.number)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle(operation, msg, ws) {
|
|
||||||
switch(operation) {
|
|
||||||
case "SEND":
|
|
||||||
if(!sendSchema.safeParse(msg).success) throw new Error("Incorrectly formatted Forum ws message!")
|
|
||||||
return this.handleSend(msg, ws)
|
|
||||||
case "GET":
|
|
||||||
if(!getSchema.safeParse(msg).success) throw new Error("Incorrectly formatted Forum ws message!")
|
|
||||||
return this.handleGet(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
const sendSchema = z.object({
|
|
||||||
conversation: z.string(),
|
|
||||||
text: z.string(),
|
|
||||||
})
|
|
||||||
.strict()
|
|
||||||
|
|
||||||
export default class MessagesHandler {
|
|
||||||
|
|
||||||
static handleSend(msg, ws) {
|
|
||||||
let user = global.db.members.getByEmail(ws.userEmail)
|
|
||||||
let convo = global.db.conversations.get(msg.conversation)
|
|
||||||
if(convo.between.includes(`MEMBER-${user.id}`)) {
|
|
||||||
global.db.messages.add(msg.conversation, msg.text, `MEMBER-${user.id}`)
|
|
||||||
global.Socket.broadcast({event: "new-message", app: "MESSAGES", msg: {conversationID: convo.id, messages: global.db.messages.getByConversation(`CONVERSATION-${msg.conversation}`)}})
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new Error("Can't add to a conversation user is not a part of!")
|
|
||||||
}
|
|
||||||
return {success: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
static handleGet(ws) {
|
|
||||||
let user = global.db.members.getByEmail(ws.userEmail)
|
|
||||||
let data = global.db.conversations.getByMember(`MEMBER-${user.id}`)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle(operation, msg, ws) {
|
|
||||||
switch(operation) {
|
|
||||||
case "GET":
|
|
||||||
return this.handleGet(ws)
|
|
||||||
case "SEND":
|
|
||||||
if(!sendSchema.safeParse(msg).success) throw new Error("Incorrectly formatted Forum ws message!")
|
|
||||||
return this.handleSend(msg, ws)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
8
ui/_/code/zod_4.2.1.js
Normal file
8
ui/_/code/zod_4.2.1.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,7 +1,29 @@
|
|||||||
class LogTable extends Shadow {
|
class LogTable extends Shadow {
|
||||||
|
state = {
|
||||||
|
logs: []
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
VStack(() => {
|
VStack(() => {
|
||||||
|
this.state.logs.forEach((log) => {
|
||||||
|
HStack(() => {
|
||||||
|
p(log.time)
|
||||||
|
p(log.ip)
|
||||||
|
p(log.host)
|
||||||
|
p(log.path)
|
||||||
|
.maxWidth(50, vw)
|
||||||
|
})
|
||||||
|
.gap(1, em)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.onAppear(async () => {
|
||||||
|
let logs = await ServerClient.send("GET")
|
||||||
|
this.state.logs = logs
|
||||||
|
})
|
||||||
|
.onEvent("log", (e) => {
|
||||||
|
console.log(e.detail)
|
||||||
|
this.state.logs.push(e.detail)
|
||||||
|
console.log(this.state.logs)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Hyperia</title>
|
<title>Console</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" href="/_/icons/logo.svg">
|
<link rel="icon" href="/_/icons/logo.svg">
|
||||||
<link rel="stylesheet" href="/_/code/shared.css">
|
<link rel="stylesheet" href="/_/code/shared.css">
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Socket from "./ws/Socket.js"
|
import ServerClient from "./ws/ServerClient.js"
|
||||||
import "./components/Home.js"
|
import "./components/Home.js"
|
||||||
|
|
||||||
import util from "./util.js"
|
import util from "./util.js"
|
||||||
window.util = util
|
window.util = util
|
||||||
|
|
||||||
window.Socket = new Socket()
|
window.ServerClient = new ServerClient()
|
||||||
Home()
|
Home()
|
||||||
@@ -44,7 +44,7 @@ class Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
send = (msg) => {
|
send = (msg) => {
|
||||||
console.log("sending")
|
console.log("sending: ", msg)
|
||||||
if (this.ws.readyState === WebSocket.OPEN) {
|
if (this.ws.readyState === WebSocket.OPEN) {
|
||||||
this.ws.send(msg);
|
this.ws.send(msg);
|
||||||
}
|
}
|
||||||
|
|||||||
78
ui/desktop/ws/ServerClient.js
Normal file
78
ui/desktop/ws/ServerClient.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import Connection from "./Connection.js";
|
||||||
|
import { z } from '/_/code/zod_4.2.1.js';
|
||||||
|
|
||||||
|
export default class ServerClient {
|
||||||
|
connection;
|
||||||
|
disabled = true;
|
||||||
|
requestID = 1;
|
||||||
|
pending = new Map();
|
||||||
|
|
||||||
|
messageSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
op: z.string().optional(),
|
||||||
|
msg: z.union([
|
||||||
|
z.object({}).passthrough(), // allows any object
|
||||||
|
z.array(z.any()) // allows any array
|
||||||
|
]).optional()
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
if (data.op !== "GET" && data.msg === undefined) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
path: ["msg"],
|
||||||
|
message: "msg is required when operation is not GET"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.connection = new Connection(this.receive);
|
||||||
|
}
|
||||||
|
|
||||||
|
isOpen() {
|
||||||
|
if(this.connection.checkOpen()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send(op, msg) {
|
||||||
|
const id = (++this.requestID).toString();
|
||||||
|
let toSend = {
|
||||||
|
id: (++this.requestID).toString(),
|
||||||
|
op: op,
|
||||||
|
msg: msg
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(this.messageSchema.safeParse(toSend).error)
|
||||||
|
if(!this.messageSchema.safeParse(toSend).success) throw new Error("ServerClient.send: ws message has incorrect format!")
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.pending.set(id, resolve);
|
||||||
|
this.connection.send(JSON.stringify(toSend));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
receive = async (event) => {
|
||||||
|
let msg = event.data;
|
||||||
|
if(msg instanceof Blob) {
|
||||||
|
msg = await msg.text()
|
||||||
|
}
|
||||||
|
msg = JSON.parse(msg);
|
||||||
|
if (msg.id && this.pending.has(msg.id)) {
|
||||||
|
this.pending.get(msg.id)(msg);
|
||||||
|
this.pending.delete(msg.id);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.onBroadcast(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBroadcast(msg) {
|
||||||
|
window.dispatchEvent(new CustomEvent("log", {
|
||||||
|
detail: msg
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import Connection from "./Connection.js";
|
|
||||||
|
|
||||||
export default class Socket {
|
|
||||||
connection;
|
|
||||||
disabled = true;
|
|
||||||
requestID = 1;
|
|
||||||
pending = new Map();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.connection = new Connection(this.receive);
|
|
||||||
}
|
|
||||||
|
|
||||||
isOpen() {
|
|
||||||
if(this.connection.checkOpen()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
send(msg) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const id = (++this.requestID).toString();
|
|
||||||
this.pending.set(id, resolve);
|
|
||||||
this.connection.send(JSON.stringify({ id, ...msg }));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
receive = (event) => {
|
|
||||||
const msg = JSON.parse(event.data);
|
|
||||||
if (msg.id && this.pending.has(msg.id)) {
|
|
||||||
this.pending.get(msg.id)(msg);
|
|
||||||
this.pending.delete(msg.id);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.onBroadcast(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onBroadcast(msg) {
|
|
||||||
window.dispatchEvent(new CustomEvent(msg.event, {
|
|
||||||
detail: msg.msg
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Hyperia</title>
|
<title>Console</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" href="/_/icons/logo.svg">
|
<link rel="icon" href="/_/icons/logo.svg">
|
||||||
<link rel="stylesheet" href="/_/code/shared.css">
|
<link rel="stylesheet" href="/_/code/shared.css">
|
||||||
|
|||||||
Reference in New Issue
Block a user