From 106bd88f1703e2d2557416d44064f34670da19d1 Mon Sep 17 00:00:00 2001 From: mrfry Date: Wed, 26 May 2021 18:38:22 +0200 Subject: [PATCH] Added chat submodule and socketAuth middleware --- src/middlewares/socketAuth.middleware.ts | 54 ++++++ src/modules/api/submodules/chat.ts | 212 +++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 src/middlewares/socketAuth.middleware.ts create mode 100644 src/modules/api/submodules/chat.ts diff --git a/src/middlewares/socketAuth.middleware.ts b/src/middlewares/socketAuth.middleware.ts new file mode 100644 index 0000000..d55b6f0 --- /dev/null +++ b/src/middlewares/socketAuth.middleware.ts @@ -0,0 +1,54 @@ +import logger from '../utils/logger' +import dbtools from '../utils/dbtools' +import cookie from 'cookie' + +interface Options { + userDB: any +} + +export default function (options: Options): any { + const { userDB } = options + + return function (socket, next) { + const cookies = cookie.parse(socket.handshake.headers.cookie) + const sessionID = cookies.sessionID + + if (process.env.NS_NOUSER) { + next() + return + } + + if (!sessionID) { + next(new Error('Authentication error')) + return + } + + const user = GetUserBySessionID(userDB, sessionID) + + if (!user) { + next(new Error('Authentication error')) + return + } + next() + } +} + +function GetUserBySessionID(db: any, sessionID: string) { + logger.DebugLog(`Getting user from db`, 'auth', 2) + + const session = dbtools.Select(db, 'sessions', { + id: sessionID, + })[0] + + if (!session) { + return + } + + const user = dbtools.Select(db, 'users', { + id: session.userID, + })[0] + + if (user) { + return user + } +} diff --git a/src/modules/api/submodules/chat.ts b/src/modules/api/submodules/chat.ts new file mode 100644 index 0000000..c642441 --- /dev/null +++ b/src/modules/api/submodules/chat.ts @@ -0,0 +1,212 @@ +import { Server as socket } from 'socket.io' + +import dbtools from '../../../utils/dbtools' +import logger from '../../../utils/logger' +import { Request, SubmoduleData } from '../../../types/basicTypes' +import socketAuth from '../../../middlewares/socketAuth.middleware' + +const msgDbPath = './data/dbs/msgs.db' + +function setup(data: SubmoduleData): void { + const { app, httpServer, httpsServer, userDB } = data + const msgDB = dbtools.GetDB(msgDbPath) + + logger.Log(`Starting Socket.io Server on ${httpsServer ? 'https' : 'http'}`) + // https://socket.io/docs/v4/handling-cors/#Configuration + const io = new socket(httpsServer || httpServer, { + cors: { + credentials: true, + origin: true, + }, + }) + + function chatMessageRead(data) { + const { sender, reciever } = data + dbtools.runStatement( + msgDB, + `update msgs + set unread = 0 + where sender = ${sender} and reciever = ${reciever}`, + 'run' + ) + io.sockets.in(sender).emit('chat message read', { + userReadMsg: reciever, + }) + } + + io.use(socketAuth({ userDB: userDB })) + + io.on('connection', (socket) => { + // TODO + // https://stackoverflow.com/questions/19106861/authorizing-and-handshaking-with-socket-io#19106961 + // socket.handshake.headers + // if (data.id !== user.id) { + // socket.disconnect() + // socket.emit('connectData', { + // success: false, + // msg: `You are only authorized to connect as ${user.id}!` + // }) + // return + // } + + socket.on('join', function (data) { + socket.join(data.id) + const userid = data.id + let currUser = dbtools.Select(msgDB, 'users', { + id: userid, + })[0] + if (!currUser) { + currUser = { + id: userid, + } + dbtools.Insert(msgDB, 'users', currUser) + } + + const groups = dbtools + .runStatement( + msgDB, + `select * from + ( + select sender as a + from msgs + where sender = ${userid} or reciever = ${userid} + union + select reciever + from msgs + where sender = ${userid} or reciever = ${userid} + )t + order by t.a asc` + ) + .reduce((acc, x) => { + if (x.a !== userid) acc.push(x.a) + return acc + }, []) + + socket.emit('prev messages', { + prevMsgs: groups.map((to) => { + const first = dbtools.runStatement( + msgDB, + `select * from msgs + where sender = ${userid} and reciever = ${to} or + sender = ${to} and reciever = ${userid} + order by date desc + limit 1` + )[0] + return first + }), + }) + + socket.on('chat message open', (data) => { + const { chatPartner } = data + + // TODO: pagination + socket.emit( + 'chat message open', + dbtools.runStatement( + msgDB, + `select * from msgs + where sender = ${userid} and reciever = ${chatPartner} or + sender = ${chatPartner} and reciever = ${userid} + order by date asc` + ) + ) + + // Read update + chatMessageRead({ sender: chatPartner, reciever: userid }) + }) + + socket.on('chat message read', (data) => { + const { chatPartner } = data + chatMessageRead({ sender: chatPartner, reciever: userid }) + }) + + socket.on('chat message', (message) => { + const { reciever, msg } = message + const recieverUser = dbtools.Select(msgDB, 'users', { + id: reciever, + })[0] + if (!recieverUser) { + socket.emit('chat message', { + success: false, + date: new Date().getTime(), + sender: reciever, + reciever: userid, + msg: `A #${reciever} számú felhasználó nem létezik`, + }) + return + } + + const msgObj = { + sender: parseInt(userid), + reciever: parseInt(reciever), + msg: msg, + date: new Date().getTime(), + unread: 1, + } + dbtools.Insert(msgDB, 'msgs', msgObj) + if (userid !== reciever) { + io.sockets.in(reciever).emit('chat message', msgObj) + } + }) + }) + + // socket.on('disconnect', () => {}) + }) + + app.get('/hasNewMsg', (req: Request, res) => { + let userid: any = req.query.userid + if (!userid || isNaN(userid)) { + res.json({ + success: false, + msg: 'Query "userid" (number) is required!', + }) + return + } + userid = parseInt(userid) + + const groups = dbtools + .runStatement( + msgDB, + `select * from + ( + select sender as a + from msgs + where sender = ${userid} or reciever = ${userid} + union + select reciever + from msgs + where sender = ${userid} or reciever = ${userid} + )t + order by t.a asc` + ) + .reduce((acc, x) => { + if (x.a !== userid) acc.push(x.a) + return acc + }, []) + + const prevMsgs = groups.map((to) => { + const first = dbtools.runStatement( + msgDB, + `select * from msgs + where sender = ${userid} and reciever = ${to} or + sender = ${to} and reciever = ${userid} + order by date desc + limit 1` + )[0] + return first + }) + + res.json({ + unreads: prevMsgs.reduce((acc, msg) => { + if (msg && msg.unread === 1 && msg.sender !== userid) { + acc.push(msg.sender) + } + return acc + }, []), + }) + }) +} + +export default { + setup: setup, +}