From b60e49e0807b51a758e2ad3eabfd91bad60892e9 Mon Sep 17 00:00:00 2001 From: AtHeartEngineer Date: Thu, 27 Jul 2023 20:43:59 -0400 Subject: [PATCH] chore(code reorg) segmented code better --- prisma/seed.js | 45 ++++++ src/config/{rooms.ts => serverConfig.ts} | 4 +- src/{ => crypto}/verification_key.ts | 0 src/{ => crypto}/verifier.ts | 0 src/data/db.ts | 18 +++ src/{ => data}/mock.ts | 9 +- src/endpoints/index.ts | 143 ++++++++++++++++++ src/server.ts | 184 ++--------------------- src/{types.ts => types/index.ts} | 0 src/utils.ts | 5 +- src/websockets/index.ts | 51 +++++++ 11 files changed, 284 insertions(+), 175 deletions(-) create mode 100644 prisma/seed.js rename src/config/{rooms.ts => serverConfig.ts} (86%) rename src/{ => crypto}/verification_key.ts (100%) rename src/{ => crypto}/verifier.ts (100%) create mode 100644 src/data/db.ts rename src/{ => data}/mock.ts (79%) create mode 100644 src/endpoints/index.ts rename src/{types.ts => types/index.ts} (100%) create mode 100644 src/websockets/index.ts diff --git a/prisma/seed.js b/prisma/seed.js new file mode 100644 index 0000000..da5318c --- /dev/null +++ b/prisma/seed.js @@ -0,0 +1,45 @@ +import { PrismaClient } from '@prisma/client'; +import { genId } from 'discreetly-interfaces'; +import { generateClaimCodes } from 'discreetly-claimcodes'; +const prisma = new PrismaClient(); +const idc = genId(0n, "First User").toString(); +const idc2 = genId(0n, "Second User").toString(); +const claimCodes = generateClaimCodes(10); +// console.log(claimCodes); +let codeArr = []; +claimCodes.forEach(code => { + codeArr.push({ claimcode: code.code }); +}); +const seedData = { + where: { + roomId: genId(0n, "First Room").toString() + }, + update: {}, + create: { + roomId: genId(0n, "First Room").toString(), + name: "First Room", + identities: [idc, idc2], + claimCodes: { + create: codeArr + } + } +}; +async function main() { + await prisma.rooms.upsert(seedData); + await prisma.rooms.upsert({ + where: { + roomId: genId(0n, "Room Two").toString() + }, + update: {}, + create: { + roomId: genId(0n, "Room Two").toString(), + name: "Room Two", + identities: [idc], + claimCodes: { + create: codeArr + } + } + }); + console.log(seedData); +} +main(); diff --git a/src/config/rooms.ts b/src/config/serverConfig.ts similarity index 86% rename from src/config/rooms.ts rename to src/config/serverConfig.ts index 807834b..f2900b1 100644 --- a/src/config/rooms.ts +++ b/src/config/serverConfig.ts @@ -15,7 +15,7 @@ console.log('SERVERID:', serverID); export const serverConfig: ServerI = { id: serverID, name: 'Localhost', - serverInfoEndpoint: 'localhost:3001', - messageHandlerSocket: 'http://localhost:3002', + serverInfoEndpoint: '3001', + messageHandlerSocket: '3002', version: '0.0.1' }; diff --git a/src/verification_key.ts b/src/crypto/verification_key.ts similarity index 100% rename from src/verification_key.ts rename to src/crypto/verification_key.ts diff --git a/src/verifier.ts b/src/crypto/verifier.ts similarity index 100% rename from src/verifier.ts rename to src/crypto/verifier.ts diff --git a/src/data/db.ts b/src/data/db.ts new file mode 100644 index 0000000..0c08774 --- /dev/null +++ b/src/data/db.ts @@ -0,0 +1,18 @@ +import { PrismaClient } from '@prisma/client'; +import { RoomI } from 'discreetly-interfaces'; + +const prisma = new PrismaClient(); + +export function getRoomByID(id: string): RoomI | null { + prisma.rooms + .findUnique({ + where: { + roomId: id + } + }) + .then((room) => { + return room; + }) + .catch((err) => console.error(err)); + return null; +} diff --git a/src/mock.ts b/src/data/mock.ts similarity index 79% rename from src/mock.ts rename to src/data/mock.ts index 66b4201..69d3ed1 100644 --- a/src/mock.ts +++ b/src/data/mock.ts @@ -1,11 +1,16 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { faker } from '@faker-js/faker'; import { MessageI } from 'discreetly-interfaces'; import { Server as SocketIOServer } from 'socket.io'; export default function Mock(io: SocketIOServer) { class randomMessagePicker { - values: any; - weightSums: any[]; + values: any[]; + weightSums: number[]; constructor(values, weights) { this.values = values; this.weightSums = []; diff --git a/src/endpoints/index.ts b/src/endpoints/index.ts new file mode 100644 index 0000000..32cd921 --- /dev/null +++ b/src/endpoints/index.ts @@ -0,0 +1,143 @@ +import { PrismaClient } from '@prisma/client'; +import { serverConfig } from '../config/serverConfig'; +import { pp } from '../utils.js'; +import { getRoomByID } from '../data/db'; +import { genId } from 'discreetly-interfaces'; + +// TODO! Properly handle authentication for admin controls +// TODO api endpoint that creates new rooms and generates invite codes for them + +export function initEndpoints(app) { + const prisma = new PrismaClient(); + + app.get(['/', '/api'], (req, res) => { + pp('Express: fetching server info'); + res.json(serverConfig); + }); + + app.get('/logclaimcodes', () => { + pp('Express: fetching claim codes'); + prisma.claimCodes + .findMany() + .then((claimCodes) => { + console.log(claimCodes); + }) + .catch((err) => { + console.error(err); + }); + }); + + app.get('/api/rooms', (req, res) => { + pp(String('Express: fetching all rooms')); + prisma.rooms + .findMany() + .then((rooms) => { + res.status(200).json(rooms); + }) + .catch((err) => { + console.error(err); + res.status(500).json({ error: 'Internal Server Error' }); + }); + }); + + app.get('/api/rooms/:id', (req, res) => { + // TODO This should return the room info for the given room ID + pp(String('Express: fetching room info for ' + req.params.id)); + const room = getRoomByID(req.params.id); + if (!room) { + res.status(500).json({ error: 'Internal Server Error' }); + } + res.status(200).json(room); + }); + + app.post('/join', (req, res) => { + interface JoinRequestBody { + code: string; + idc: string; + } + + const { code, idc } = req.body as JoinRequestBody; + + pp(`Express[/join]: claiming code: ${code}`); + + prisma.claimCodes + .findUnique({ + where: { + claimcode: code + } + }) + .then((codeStatus: { claimed: boolean; roomIds: string[] }) => { + if (codeStatus.claimed === false) { + prisma.claimCodes + .update({ + where: { + claimcode: code + }, + data: { + claimed: true + } + }) + .then((claimCode: { roomIds: string[] }) => { + const roomIds = claimCode.roomIds.map((room) => room); + prisma.rooms + .updateMany({ + where: { + roomId: { + in: roomIds + } + }, + data: { + identities: { + push: idc + } + } + }) + .then((updatedRooms) => { + res.status(200).json(updatedRooms); + }) + .catch((err) => { + console.error(err); + res.status(500).json({ error: 'Internal Server Error' }); + }); + }) + .catch((err) => { + console.error(err); + res.status(500).json({ error: 'Internal Server Error' }); + }); + } else { + res.status(400).json({ message: 'Claim code already used' }); + } + }) + .catch((err) => { + console.error(err); + res.status(500).json({ error: 'Internal Server Error' }); + }); + }); + + app.post('/room/add', (req, res) => { + interface RoomData { + password: string; + roomName: string; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const { password, roomName } = req.body.data as RoomData; + if (password === process.env.PASSWORD) { + prisma.rooms + .create({ + data: { + roomId: genId(BigInt(serverConfig.id), roomName).toString(), + name: roomName + } + }) + .then((newRoom) => { + res.status(200).json(newRoom); + }) + .catch((error: Error) => { + console.error(error); + res.status(500).send('Error creating new room'); + }); + } else { + res.status(401).send('Unauthorized'); + } + }); +} diff --git a/src/server.ts b/src/server.ts index 69486f5..6115df7 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,19 +1,13 @@ import express from 'express'; import { Server } from 'http'; -import { Socket, Server as SocketIOServer } from 'socket.io'; +import { Server as SocketIOServer } from 'socket.io'; import cors from 'cors'; -import { PrismaClient } from '@prisma/client'; -import { serverConfig } from './config/rooms.js'; -import { type MessageI, genId, RoomI } from 'discreetly-interfaces'; -import verifyProof from './verifier.js'; +import { serverConfig } from './config/serverConfig'; +import { pp, shim } from './utils'; +import mock from './data/mock'; +import { websocketSetup } from './websockets/index'; +import { initEndpoints } from './endpoints/index'; -import { pp, shim } from './utils.js'; -import mock from './mock.js'; -// HTTP is to get info from the server about configuration, rooms, etc -const HTTP_PORT = 3001; -// Socket is to communicate chat room messages back and forth -const SOCKET_PORT = 3002; -const userCount = {}; const app = express(); const socket_server = new Server(app); @@ -32,167 +26,13 @@ const io = new SocketIOServer(socket_server, { } }); -// Create a MongoClient with a MongoClientOptions object to set the Stable API version - -const prisma = new PrismaClient(); -console.log('Prisma connected'); - -function getRoomByID(id: string) { - return prisma.rooms.findUnique({ - where: { - roomId: id - } - }); -} - -io.on('connection', (socket: Socket) => { - pp('SocketIO: a user connected', 'debug'); - - socket.on('validateMessage', (msg: MessageI) => { - pp({ 'VALIDATING MESSAGE ID': msg.id.slice(0, 11), 'MSG:': msg.message }); - let room: RoomI; - let valid: boolean; - getRoomByID(msg.room.toString()) - .then((r) => { - room = r; - }) - .catch((err) => { - console.error(err); - }); - verifyProof(msg, room) - .then((v) => { - valid = v; - }) - .catch((err) => { - err; - }); - if (!valid) { - pp('INVALID MESSAGE', 'warn'); - return; - } - io.emit('messageBroadcast', msg); - }); - - socket.on('disconnect', () => { - pp('SocketIO: user disconnected'); - }); - - socket.on('joinRoom', (roomID: bigint) => { - const id = roomID.toString(); - userCount[id] = userCount[id] ? userCount[id] + 1 : 1; - }); - - socket.on('leaveRoom', (roomID: bigint) => { - const id = roomID.toString(); - userCount[id] = userCount[id] ? userCount[id] - 1 : 0; - }); -}); - -app.use( - cors({ - origin: '*' - }) -); - -app.get(['/', '/api'], (req, res) => { - pp('Express: fetching server info'); - res.json(serverConfig); -}); - -app.get('/logclaimcodes', async (req, res) => { - pp('Express: fetching claim codes'); - const claimCodes = await prisma.claimCodes.findMany(); - res.status(200).json(claimCodes); -}); - -app.get('/identities', async (req, res) => { - pp(String('Express: fetching all identities')); - const identities = await prisma.rooms.findMany({ - select: { - name: true, - roomId: true, - identities: true - } - }); - res.status(200).json(identities); -}); - -app.get('/api/rooms', async (req, res) => { - pp(String('Express: fetching all rooms')); - const rooms = await prisma.rooms.findMany(); - res.status(200).json(rooms); -}); - -app.get('/api/rooms/:id', async (req, res) => { - // TODO This should return the room info for the given room ID - pp(String('Express: fetching room info for ' + req.params.id)); - const room = await prisma.rooms.findUnique({ - where: { - roomId: req.params.id - } - }); - res.status(200).json(room); -}); - -app.post('/join', async (req, res) => { - const data = req.body; - const { code, idc } = data; - pp('Express[/join]: claiming code:' + code); - const codeStatus = await prisma.claimCodes.findUnique({ - where: { - claimcode: code - } - }); - if (codeStatus.claimed === false) { - const claimCode = await prisma.claimCodes.update({ - where: { - claimcode: code - }, - data: { - claimed: true - } - }); - const roomIds = claimCode['roomIds'].map((room) => room); - const updatedRooms = await prisma.rooms.updateMany({ - where: { - roomId: { - in: roomIds - } - }, - data: { - identities: { - push: idc - } - } - }); - res.status(200).json(updatedRooms); - } else { - res.status(400).json({ message: 'Claim code already used' }); - } -}); -// TODO api endpoint that creates new rooms and generates invite codes for them - -app.post('/room/add', async (req, res) => { - const data = req.body; - const { password, roomName } = data; - if (password === process.env.PASSWORD) { - const newRoom = await prisma.rooms.create({ - data: { - roomId: genId(BigInt(999), roomName).toString(), - name: roomName - } - }); - res.status(200).json(newRoom); - } -}); - function initAppListeners() { - app.listen(HTTP_PORT, () => { - pp(`Express Http Server is running at port ${HTTP_PORT}`); + app.listen(serverConfig.serverInfoEndpoint, () => { + pp(`Express Http Server is running at port ${serverConfig.serverInfoEndpoint}`); }); - socket_server.listen(SOCKET_PORT, () => { - pp(`SocketIO Server is running at port ${SOCKET_PORT}`); + socket_server.listen(serverConfig.messageHandlerSocket, () => { + pp(`SocketIO Server is running at port ${serverConfig.messageHandlerSocket}`); }); } @@ -201,8 +41,12 @@ function initAppListeners() { */ if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { console.log('~~~~DEVELOPMENT MODE~~~~'); + websocketSetup(io); + initEndpoints(app); initAppListeners(); mock(io); } else { + websocketSetup(io); + initEndpoints(app); initAppListeners(); } diff --git a/src/types.ts b/src/types/index.ts similarity index 100% rename from src/types.ts rename to src/types/index.ts diff --git a/src/utils.ts b/src/utils.ts index e2dbfe4..8885dbd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,7 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ export function shim() { // Deal with bigints in JSON (BigInt.prototype as any).toJSON = function () { @@ -5,7 +9,6 @@ export function shim() { }; } - // Pretty Print to console export const pp = (str: any, level = 'log') => { str = JSON.stringify(str, null, 2); diff --git a/src/websockets/index.ts b/src/websockets/index.ts new file mode 100644 index 0000000..eb0e848 --- /dev/null +++ b/src/websockets/index.ts @@ -0,0 +1,51 @@ +import { MessageI, RoomI } from 'discreetly-interfaces'; +import { Socket, Server as SocketIOServer } from 'socket.io'; +import verifyProof from '../crypto/verifier'; +import { getRoomByID } from '../data/db'; +import { pp } from '../utils'; + +const userCount: { + [key: string]: number; +} = {}; + +export function websocketSetup(io: SocketIOServer) { + io.on('connection', (socket: Socket) => { + pp('SocketIO: a user connected', 'debug'); + + socket.on('validateMessage', (msg: MessageI) => { + pp({ 'VALIDATING MESSAGE ID': msg.id.slice(0, 11), 'MSG:': msg.message }); + let valid: boolean; + const room: RoomI = getRoomByID(msg.room.toString()); + if (!room) { + pp('INVALID ROOM', 'warn'); + return; + } + verifyProof(msg, room) + .then((v) => { + valid = v; + }) + .catch((err) => { + err; + }); + if (!valid) { + pp('INVALID MESSAGE', 'warn'); + return; + } + io.emit('messageBroadcast', msg); + }); + + socket.on('disconnect', () => { + pp('SocketIO: user disconnected'); + }); + + socket.on('joinRoom', (roomID: bigint) => { + const id = roomID.toString(); + userCount[id] = userCount[id] ? userCount[id] + 1 : 1; + }); + + socket.on('leaveRoom', (roomID: bigint) => { + const id = roomID.toString(); + userCount[id] = userCount[id] ? userCount[id] - 1 : 0; + }); + }); +}