a lot of changes in back and front ;)
This commit is contained in:
parent
6cb2bea05b
commit
a500916712
373
src/index.js
373
src/index.js
|
@ -2,10 +2,10 @@ const express = require('express');
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
const cors = require("cors");
|
const cors = require("cors");
|
||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
const http = require('http')
|
const http = require('http');
|
||||||
|
const session = require('express-session');
|
||||||
|
|
||||||
clients = []; // websocket clients
|
sessions = {}; // logged in clients, there are stored objects like {"token": "some_token_here", "socket": here-is-websocket-object}
|
||||||
sessions = {}; // logged in clients, there are stored their tokens.
|
|
||||||
|
|
||||||
const middleware = require("./middleware");
|
const middleware = require("./middleware");
|
||||||
const utils = require("./utils")
|
const utils = require("./utils")
|
||||||
|
@ -20,21 +20,40 @@ const httpServer = http.createServer(app).listen(PORT, "0.0.0.0", () => {
|
||||||
console.log("[LOG] Ready to use.");
|
console.log("[LOG] Ready to use.");
|
||||||
});
|
});
|
||||||
|
|
||||||
const ws = new WebSocket.Server({
|
const wss = new WebSocket.WebSocketServer({ clientTracking: false, noServer: true });
|
||||||
server: httpServer,
|
|
||||||
host: "0.0.0.0"
|
const onSocketError = (err) => {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
httpServer.on('upgrade', (request, socket, head) => {
|
||||||
|
socket.on('error', onSocketError);
|
||||||
|
|
||||||
|
sessionParser(request, {}, () => {
|
||||||
|
if (!request.session.token) {
|
||||||
|
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||||
|
socket.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
socket.removeListener('error', onSocketError);
|
||||||
|
|
||||||
|
wss.handleUpgrade(request, socket, head, function (ws) {
|
||||||
|
wss.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[LOG] Socket has been started");
|
wss.on('connection', function (ws, request) {
|
||||||
|
const token = request.session.token;
|
||||||
|
|
||||||
ws.on('connection', (wsclient) => {
|
sessions[token].socket = ws;
|
||||||
clients.push(wsclient)
|
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package structure:
|
Package structure:
|
||||||
{
|
{
|
||||||
"action": "...", // the request
|
"action": "...", // the request
|
||||||
"token": "...", // the token that is the key for sessions dictionary
|
|
||||||
"content": ..., // the rest content of an action.
|
"content": ..., // the rest content of an action.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,51 +61,91 @@ ws.on('connection', (wsclient) => {
|
||||||
message:
|
message:
|
||||||
{
|
{
|
||||||
"action": "message",
|
"action": "message",
|
||||||
"token": "some_token_here",
|
|
||||||
"content": {
|
"content": {
|
||||||
"chatId": 1,
|
"chatId": 1,
|
||||||
"text": "Hello!"
|
"text": "Hello!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
ws.on('message', async (message) => {
|
||||||
|
message = JSON.parse(message);
|
||||||
|
switch (message.action) {
|
||||||
|
case "message": {
|
||||||
|
let chatId = message.content.chatId;
|
||||||
|
let text = message.content.text;
|
||||||
|
let userId = sessions[token].userId
|
||||||
|
|
||||||
wsclient.on('message', async (msg) => {
|
let r = await client.query("SELECT ID FROM Chats WHERE ID = $1", [chatId]);
|
||||||
try {
|
if (r.rowCount == 0) { // no such chat exist
|
||||||
let jsonMsg = JSON.parse(msg)
|
sessions[token].socket.send(`No chat with ID ${chatId} exist.`);
|
||||||
switch (jsonMsg.action) {
|
|
||||||
case "message":{
|
|
||||||
let userId = sessions[jsonMsg.token];
|
|
||||||
console.log(`${userId} has sent a message: ${jsonMsg.content.text} to ${jsonMsg.content.chatId}`);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((await client.query("SELECT ID FROM Users WHERE ID = $1 AND $2 = ANY(chats)", [userId, chatId])).rowCount == 0) {
|
||||||
|
ws.send("You are not a member of this chat.");
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
//Inserting a message into database.
|
||||||
console.log(`Package cannot be understood: ${msg}`)
|
let messageId = (await client.query("INSERT INTO Messages (author_id, time_sent, content) VALUES($1, NOW(), $2) RETURNING ID", [userId, text])).rows[0].id;
|
||||||
|
await client.query("UPDATE Chats SET messages = ARRAY_APPEND(messages, $1) WHERE ID = $2", [messageId, chatId])
|
||||||
|
// sessions[token].socket.send("successful");
|
||||||
|
|
||||||
|
//Sending a message to everyone who is in the group.
|
||||||
|
|
||||||
|
let membersIds = (await client.query("SELECT ID FROM Users WHERE $1 = ANY(chats)", [chatId])).rows
|
||||||
|
let Ids = [];
|
||||||
|
for (let mId of membersIds) {
|
||||||
|
Ids.push(mId.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find all sessions by IDs and send to their sockets
|
||||||
|
for (const [key, value] of Object.entries(sessions)) {
|
||||||
|
let session = value;
|
||||||
|
if (session == undefined) continue
|
||||||
|
if (session.userId == userId) continue; // Skip the sender of the message
|
||||||
|
if (Ids.includes(session.userId)) {
|
||||||
|
session.socket.send(JSON.stringify({
|
||||||
|
action: "message",
|
||||||
|
content: {
|
||||||
|
chatId: chatId,
|
||||||
|
authorId: userId,
|
||||||
|
text: text
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
console.log(`[ERROR] Package cannot be understood: ${JSON.stringify(message)}`);
|
||||||
|
sessions[token].socket.send("Package cannot be understood.")
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.log(`[ERROR] in receiving message by websocket: ${e}`)
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
wsclient.on('close', () => {
|
ws.on('close', function () {
|
||||||
clients = clients.filter(c => c !== wsclient)
|
// sessions[token].socket = undefined
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
app.use(cors());
|
console.log("[LOG] Socket has been started");
|
||||||
app.use(express.json());
|
|
||||||
app.use(express.urlencoded({ extended: false }));
|
|
||||||
app.use(require('express-session')({
|
|
||||||
|
|
||||||
|
|
||||||
|
const sessionParser = session({
|
||||||
name: 'smk_chat',
|
name: 'smk_chat',
|
||||||
secret: 'PLEASE!!GENERATE!!A!!STRONG!!ONE!!',
|
secret: 'PLEASE!!GENERATE!!A!!STRONG!!ONE!!',
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false
|
saveUninitialized: false
|
||||||
|
});
|
||||||
|
|
||||||
}));
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(sessionParser);
|
||||||
app.use('/js', express.static(__dirname + "/js"));
|
app.use('/js', express.static(__dirname + "/js"));
|
||||||
|
|
||||||
|
|
||||||
|
//======================frontend===========================
|
||||||
app.get('/', middleware.requireToBeLoggedIn, (req, res) => {
|
app.get('/', middleware.requireToBeLoggedIn, (req, res) => {
|
||||||
res.sendFile('views/index.html', { root: __dirname });
|
res.sendFile('views/index.html', { root: __dirname });
|
||||||
});
|
});
|
||||||
|
@ -99,41 +158,124 @@ app.get('/login', middleware.requireToBeNotLoggedIn, (req, res) => {
|
||||||
res.sendFile('views/login.html', { root: __dirname });
|
res.sendFile('views/login.html', { root: __dirname });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/chat/:chatId', middleware.requireToBeLoggedIn, (req, res) => {
|
||||||
|
res.sendFile('views/chat.html', { root: __dirname })
|
||||||
|
});
|
||||||
|
|
||||||
|
//======================backend===========================
|
||||||
|
|
||||||
|
|
||||||
|
//IN: userId (in route)
|
||||||
|
//OUT: lastname, firstname, middlename
|
||||||
|
//Return credentials of the user whose id is userId.
|
||||||
|
//Return -1 if no such user exist.
|
||||||
|
//Requires client to be logged in.
|
||||||
|
app.get('/api/getCredentialsById/:id', middleware.requireToBeLoggedIn, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const userId = req.params.id;
|
||||||
|
let user = (await client.query("SELECT lastname, firstname, middlename FROM Users WHERE ID = $1", [userId])).rows[0];
|
||||||
|
if (user == undefined) return res.send("-1").status(400).end();
|
||||||
|
return res.send(user).status(200).end();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/getCredentialsById/${req.params.id}}: ${e}`)
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
//IN: lastname, firstname, middlename
|
//IN: lastname, firstname, middlename
|
||||||
//OUT: UserID
|
//OUT: UserID
|
||||||
//Returns an ID of the user, whose lastname, firstname and middlename were passed.
|
//Returns an ID of the user, whose lastname, firstname and middlename were passed.
|
||||||
//Returns -1 if user does not exist.
|
//Returns -1 if user does not exist.
|
||||||
//Requires client to be logged in.
|
//Requires client to be logged in.
|
||||||
app.get('/api/getIdByCredentials', middleware.requireToBeLoggedIn, async (req, res) => {
|
|
||||||
const { lastname, firstname, middlename } = req.body;
|
|
||||||
|
|
||||||
return res.send(await utils.getIdByCredentials(lastname, firstname, middlename)).end();
|
app.get('/api/getIdByCredentials', middleware.requireToBeLoggedIn, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { lastname, firstname, middlename } = req.body;
|
||||||
|
|
||||||
|
return res.send(await utils.getIdByCredentials(lastname, firstname, middlename)).end();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/getIdByCredentials: ${e}`)
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//IN: chatId, amount
|
||||||
|
//OUT: array of objects that represent a message, like {"author_id": ..., time_sent: ..., content: ...}
|
||||||
|
//Returns an array of last ${amount} ordered by date of described objects if everything is okay
|
||||||
|
//Returns HTTP/1.1 400 "Chat with id ${ID} does not exist." if no chat with supplied id does not exist.
|
||||||
|
//Returns HTTP/1.1 403 "You are not a member of this chat" if the statement is true ;)
|
||||||
|
//Requires client to be logged in.
|
||||||
|
app.post('/api/getMessagesFromChat/by-amount', middleware.requireToBeLoggedIn, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const userId = sessions[req.session.token].userId;
|
||||||
|
|
||||||
|
const { chatId, amount } = req.body;
|
||||||
|
let messages = []
|
||||||
|
|
||||||
|
if ((await client.query("SELECT ID FROM Users WHERE ID = $1 AND $2 = ANY(Chats)", [userId, chatId]).rowCount == 0)) {
|
||||||
|
return res.status(403).send("You are not a member of this chat");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((await client.query("SELECT ID FROM Chats WHERE ID = $1", [chatId])).rowCount == 0) {
|
||||||
|
return res.status(400).send(`Chat with id ${chatId} does not exist.`).end()
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = await client.query("SELECT messages FROM Chats WHERE ID = $1 LIMIT $2", [chatId, amount])
|
||||||
|
|
||||||
|
let messagesIds = result.rows[0].messages;
|
||||||
|
|
||||||
|
if (messagesIds == null || messagesIds == undefined) return res.send("").status(200).end();
|
||||||
|
|
||||||
|
|
||||||
|
let selectedMessagesIds = messagesIds.slice(-amount);
|
||||||
|
|
||||||
|
if (selectedMessagesIds == null || selectedMessagesIds == undefined) return res.send("").status(200).end();
|
||||||
|
for (let id of selectedMessagesIds) {
|
||||||
|
let message = (await client.query("SELECT * FROM messages WHERE ID = $1 ORDER BY time_sent DESC LIMIT $2", [id, amount])).rows[0];
|
||||||
|
messages.push(message)
|
||||||
|
}
|
||||||
|
return res.send(messages).status(200).end();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/getMessagesFromChat/by-amount: ${e}`)
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//IN: chatId, fromTimestamp, toTimestamp
|
//IN: chatId, fromTimestamp, toTimestamp
|
||||||
//OUT: array of objects that represent a message, like {"author_id": ..., time_sent: ..., content: ...}
|
//OUT: array of objects that represent a message, like {"author_id": ..., time_sent: ..., content: ...}
|
||||||
//fromTimestamp must be lower that toTimestamp, otherwise it might return nothing.
|
//fromTimestamp must be lower that toTimestamp, otherwise it might return nothing.
|
||||||
//Returns an array of described objects.
|
//Returns an array of described objects if everything is okay
|
||||||
//Return "Chat with id ${ID} does not exist." if no chat with supplied id does not exist.
|
//Returns HTTP/1.1 400 "Chat with id ${ID} does not exist." if no chat with supplied id does not exist.
|
||||||
|
//Returns HTTP/1.1 200 and an empty string if no messages in chat are found /shrug.
|
||||||
//Requires client to be logged in.
|
//Requires client to be logged in.
|
||||||
app.post('/api/getMessagesFromChat', middleware.requireToBeLoggedIn, async (req, res) => {
|
app.post('/api/getMessagesFromChat/by-time', middleware.requireToBeLoggedIn, async (req, res) => {
|
||||||
const userId = sessions[req.session.token];
|
try {
|
||||||
|
const userId = sessions[req.session.token].userId;
|
||||||
|
|
||||||
const { chatId, fromTimestamp, toTimestamp } = req.body;
|
const { chatId, fromTimestamp, toTimestamp } = req.body;
|
||||||
let messages = []
|
let messages = []
|
||||||
|
|
||||||
if ((await client.query("SELECT ID FROM Chats WHERE ID = $1", [chatId])).rowCount == 0) {
|
if ((await client.query("SELECT ID FROM Chats WHERE ID = $1", [chatId])).rowCount == 0) {
|
||||||
return res.status(400).send(`Chat with id ${chatId} does not exist.`).end()
|
return res.status(400).send(`Chat with id ${chatId} does not exist.`).end()
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = await client.query("SELECT messages FROM Chats WHERE ID = $1", [chatId])
|
||||||
|
|
||||||
|
let messagesIds = result.rows[0].messages;
|
||||||
|
if (messagesIds == null) return res.send("").status(200).end();
|
||||||
|
for (let id of messagesIds) {
|
||||||
|
let message = (await client.query("SELECT * FROM messages WHERE ID = $1 AND time_sent <= $2::TIMESTAMP AND time_sent >= $3::TIMESTAMP", [id, toTimestamp, fromTimestamp])).rows[0];
|
||||||
|
if (message == undefined) continue
|
||||||
|
messages.push(message)
|
||||||
|
}
|
||||||
|
return res.send(messages).status(200).end();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/getMessagesFromChat/by-time: ${e}`);
|
||||||
|
res.status(500).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
let messagesIds = (await client.query("SELECT messages FROM Chats WHERE ID = $1", [chatId])).rows[0].messages;
|
|
||||||
for (let id of messagesIds) {
|
|
||||||
let message = (await client.query("SELECT * FROM messages WHERE ID = $1 AND time_sent <= $2::TIMESTAMP AND time_sent >= $3::TIMESTAMP", [id, toTimestamp, fromTimestamp])).rows[0];
|
|
||||||
messages.push(message)
|
|
||||||
}
|
|
||||||
return res.send(messages).status(200).end();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,45 +284,101 @@ app.post('/api/getMessagesFromChat', middleware.requireToBeLoggedIn, async (req,
|
||||||
//Returs ids of chats which user with passed ID is member in.
|
//Returs ids of chats which user with passed ID is member in.
|
||||||
//Return empty string if user has no membership in any chat.
|
//Return empty string if user has no membership in any chat.
|
||||||
app.get('/api/getChats', middleware.requireToBeLoggedIn, async (req, res) => {
|
app.get('/api/getChats', middleware.requireToBeLoggedIn, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const userId = sessions[req.session.token].userId
|
||||||
|
|
||||||
const userId = sessions[req.session.token]
|
let chats = (await client.query("SELECT chats FROM Users WHERE ID = $1", [userId])).rows[0].chats
|
||||||
|
return res.send(chats).status(200).end();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/getChats: ${e}`)
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let chats = (await client.query("SELECT chats FROM Users WHERE ID = $1", [userId])).rows[0].chats
|
//IN: chatId (in route)
|
||||||
return res.send(chats).status(200).end();
|
//OUT: JSON describes a chat with id:
|
||||||
|
//{name: ..., admins: ..., members: ...}
|
||||||
|
//Note: JSON does not include messages. You have to use /api/getMessagesFromChat to query messages.
|
||||||
|
//Returns aforementioned json if the chat exists and user is a member of this chat.
|
||||||
|
//Returns -1 if chat does not exist
|
||||||
|
//Return -2 if user is not a member of the chat
|
||||||
|
app.get('/api/getChatInfo/:chatId', middleware.requireToBeLoggedIn, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const chatId = req.params.chatId;
|
||||||
|
|
||||||
|
if (Number(chatId) != chatId) return res.send("-1").status(400).end();
|
||||||
|
|
||||||
|
let chatRequest = await client.query("SELECT name, admins FROM Chats WHERE ID = $1::INT", [chatId]);
|
||||||
|
if (chatRequest.rowCount == 0) return res.send("-1").status(400).end();
|
||||||
|
|
||||||
|
|
||||||
|
let membersRequest = await client.query("SELECT ID FROM Users WHERE $1 = ANY(chats)", [chatId]);
|
||||||
|
|
||||||
|
let members = [];
|
||||||
|
|
||||||
|
membersRequest.rows.forEach((member, index, membersRequest) => {
|
||||||
|
members.push(member.id)
|
||||||
|
});
|
||||||
|
// console.log(members)
|
||||||
|
// console.log(sessions[req.session.token])
|
||||||
|
if (!members.includes(sessions[req.session.token].userId)) {
|
||||||
|
return res.send("-2").status(403).end();
|
||||||
|
}
|
||||||
|
|
||||||
|
let chatInfo = {
|
||||||
|
name: chatRequest.rows[0].name,
|
||||||
|
admins: chatRequest.rows[0].admins,
|
||||||
|
members: members
|
||||||
|
};
|
||||||
|
|
||||||
|
return res.send(JSON.stringify(chatInfo)).status(200).end();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/getChatInfo/${req.params.chatId}: ${e}`);
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//IN: UserId, array of UserIDs that are to be invited.
|
//IN: UserId, array of UserIDs that are to be invited.
|
||||||
//OUT: "Ok" if succsessful, "User with id ${MEMBERID} does not exist."
|
//OUT: "Ok" if successful, "User with id ${MEMBERID} does not exist."
|
||||||
|
//Return -1 if amout of users to invite is 0.
|
||||||
//Requires to be logged in
|
//Requires to be logged in
|
||||||
app.post('/api/createChat', middleware.requireToBeLoggedIn, async (req, res) => {
|
app.post('/api/createChat', middleware.requireToBeLoggedIn, async (req, res) => {
|
||||||
const userId = sessions[req.session.token]
|
try {
|
||||||
let { toInviteIds } = req.body;
|
const userId = sessions[req.session.token].userId
|
||||||
|
let { toInviteIds } = req.body;
|
||||||
|
if (toInviteIds == "" || toInviteIds == undefined || toInviteIds == null) return res.send("-1").status(400).end();
|
||||||
|
|
||||||
toInviteIds = toInviteIds.split(" ");
|
toInviteIds = toInviteIds.split(" ");
|
||||||
|
|
||||||
toInviteIds.forEach(async (id, index, toInviteIds) => {
|
toInviteIds.forEach(async (id, index, toInviteIds) => {
|
||||||
if ((await client.query("SELECT ID FROM Users WHERE ID = $1", [id])).rowCount == 0) {
|
if ((await client.query("SELECT ID FROM Users WHERE ID = $1", [id])).rowCount == 0) {
|
||||||
return res.send(`User with id ${id} does not exist.`)
|
return res.send(`User with id ${id} does not exist.`).end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let chatName
|
||||||
|
if (toInviteIds.length == 1) {
|
||||||
|
let invitedFullname = (await client.query("SELECT lastname, firstname, middlename FROM Users WHERE ID = $1;", [toInviteIds[0]])).rows[0]
|
||||||
|
let invitorFullname = (await client.query("SELECT lastname, firstname, middlename FROM Users WHERE ID = $1;", [userId])).rows[0]
|
||||||
|
chatName = invitedFullname.lastname + " " + invitedFullname.firstname + " " + invitedFullname.middlename + " и " + invitorFullname.lastname + " " + invitorFullname.firstname + " " + invitorFullname.middlename
|
||||||
|
} else {
|
||||||
|
chatName = "Новая группа"
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let chatName
|
let chatId = (await client.query("INSERT INTO Chats (name) VALUES ($1) RETURNING ID;", [chatName])).rows[0].id
|
||||||
if (toInviteIds.length == 1) {
|
|
||||||
let invitedFullname = (await client.query("SELECT lastname, firstname, middlename FROM Users WHERE ID = $1;", [toInviteIds[0]])).rows[0]
|
await client.query("UPDATE Chats SET admins = ARRAY_APPEND(admins, $1) WHERE ID = $2", [userId, chatId]);
|
||||||
let invitorFullname = (await client.query("SELECT lastname, firstname, middlename FROM Users WHERE ID = $1;", [userId])).rows[0]
|
|
||||||
chatName = invitedFullname.lastname + " " + invitedFullname.firstname + " " + invitedFullname.middlename + " и " + invitorFullname.lastname + " " + invitorFullname.firstname + " " + invitorFullname.middlename
|
toInviteIds.push(userId); // we need to invite the admin, too :)
|
||||||
} else {
|
toInviteIds.forEach(async (id, index, toInviteIds) => {
|
||||||
chatName = "Новая группа"
|
await client.query("UPDATE Users SET chats = ARRAY_APPEND(chats, $1) WHERE ID = $2;", [chatId, id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.send("Ok");
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/createChat: ${e}`);
|
||||||
|
res.status(500).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
let chatId = (await client.query("INSERT INTO Chats (name) VALUES ($1) RETURNING ID;", [chatName])).rows[0].id
|
|
||||||
|
|
||||||
await client.query("UPDATE Chats SET admins = ARRAY_APPEND(admins, $1) WHERE ID = $2", [userId, chatId]);
|
|
||||||
|
|
||||||
toInviteIds.forEach(async (id, index, toInviteIds) => {
|
|
||||||
await client.query("UPDATE Users SET chats = ARRAY_APPEND(chats, $1) WHERE ID = $2;", [chatId, id]);
|
|
||||||
});
|
|
||||||
return res.send("Ok")
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//IN: none.
|
//IN: none.
|
||||||
|
@ -188,9 +386,15 @@ app.post('/api/createChat', middleware.requireToBeLoggedIn, async (req, res) =>
|
||||||
//Removes client's session, thus unlogging a user.
|
//Removes client's session, thus unlogging a user.
|
||||||
//Requires to be logged in.
|
//Requires to be logged in.
|
||||||
app.get('/api/logout', middleware.requireToBeLoggedIn, (req, res) => {
|
app.get('/api/logout', middleware.requireToBeLoggedIn, (req, res) => {
|
||||||
sessions[req.session.token] = undefined;
|
try {
|
||||||
req.session.token = undefined;
|
sessions[req.session.token] = undefined;
|
||||||
res.redirect('/login');
|
req.session.token = undefined;
|
||||||
|
res.redirect('/login');
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ERROR] in /api/logout: ${e}`)
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//IN: lastname, firstname, middlename, password.
|
//IN: lastname, firstname, middlename, password.
|
||||||
|
@ -214,7 +418,10 @@ app.post('/api/register', middleware.requireToBeNotLoggedIn, async (req, res) =>
|
||||||
[lastname, firstname, middlename, hash]
|
[lastname, firstname, middlename, hash]
|
||||||
)).rows[0].id;
|
)).rows[0].id;
|
||||||
req.session.token = utils.generateRandomString();
|
req.session.token = utils.generateRandomString();
|
||||||
sessions[req.session.token] = id;
|
sessions[req.session.token] = {
|
||||||
|
userId: id,
|
||||||
|
socket: undefined
|
||||||
|
};
|
||||||
res.redirect('/');
|
res.redirect('/');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`[ERROR] in /api/register: ${err}`)
|
console.log(`[ERROR] in /api/register: ${err}`)
|
||||||
|
@ -240,7 +447,10 @@ app.post('/api/login', middleware.requireToBeNotLoggedIn, async (req, res) => {
|
||||||
|
|
||||||
if (bcrypt.compareSync(password, stored_password)) {
|
if (bcrypt.compareSync(password, stored_password)) {
|
||||||
req.session.token = utils.generateRandomString()
|
req.session.token = utils.generateRandomString()
|
||||||
sessions[req.session.token] = ID;
|
sessions[req.session.token] = {
|
||||||
|
userId: ID,
|
||||||
|
socket: undefined
|
||||||
|
};
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
} else {
|
} else {
|
||||||
return res.status(400).send("Wrong password").end();
|
return res.status(400).send("Wrong password").end();
|
||||||
|
@ -249,5 +459,4 @@ app.post('/api/login', middleware.requireToBeNotLoggedIn, async (req, res) => {
|
||||||
console.log(`[ERROR] in /api/login: ${err}`)
|
console.log(`[ERROR] in /api/login: ${err}`)
|
||||||
res.status(500).send();
|
res.status(500).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
const sendMessage = (event) => {
|
||||||
|
if (event.key == "Enter") {
|
||||||
|
console.log(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createMessageElement = async (message) => {
|
||||||
|
|
||||||
|
let author = JSON.parse(await fetch(`/api/getCredentialsById/${message.author_id}`).then(response => response.text()).then((response) => { return response; }))
|
||||||
|
|
||||||
|
|
||||||
|
let messageDiv = document.createElement("div");
|
||||||
|
messageDiv.id = `message-${message.id}`;
|
||||||
|
messageDiv.style = "background-color: gray; box-shadow: 1px 1px; margin: 10px; padding: 5px"
|
||||||
|
|
||||||
|
let authorName = document.createElement("b");
|
||||||
|
authorName.innerText = `${author.firstname}:`;
|
||||||
|
|
||||||
|
let messageContent = document.createElement("p");
|
||||||
|
messageContent.innerText = message.content;
|
||||||
|
messageContent.id = "content";
|
||||||
|
|
||||||
|
let messageTime = document.createElement("i");
|
||||||
|
messageTime.innerText = `@ ${message.time_sent.toString()}`;
|
||||||
|
messageTime.id = "time_sent";
|
||||||
|
|
||||||
|
messageDiv.appendChild(authorName);
|
||||||
|
messageDiv.appendChild(messageContent);
|
||||||
|
messageDiv.appendChild(messageTime);
|
||||||
|
|
||||||
|
return messageDiv
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener('load', async function () {
|
||||||
|
let location = window.location.href.split("/")
|
||||||
|
const chatId = location[location.length - 1];
|
||||||
|
|
||||||
|
let chatInfo = JSON.parse(
|
||||||
|
await fetch(`/api/getChatInfo/${chatId}`).then(response => response.text()).then(response => { return response })
|
||||||
|
);
|
||||||
|
console.log(chatInfo)
|
||||||
|
let header = document.getElementById('header-text');
|
||||||
|
header.innerText = chatInfo.name;
|
||||||
|
let content = document.getElementById('content-div')
|
||||||
|
|
||||||
|
|
||||||
|
let today = new Date();
|
||||||
|
let yesterday = new Date();
|
||||||
|
yesterday.setUTCDate(today.getUTCDate() - 10);
|
||||||
|
console.log(today.toISOString(), yesterday.toISOString())
|
||||||
|
// fetching messages from yesterday to today
|
||||||
|
let messages = await fetch("/api/getMessagesFromChat/by-time", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
chatId: chatId,
|
||||||
|
fromTimestamp: yesterday.toISOString(),
|
||||||
|
toTimestamp: today.toISOString()
|
||||||
|
})
|
||||||
|
}).then(response => response.text()).then((response) => { return response; })
|
||||||
|
messages = eval(messages)
|
||||||
|
for (let message of messages) {
|
||||||
|
content.appendChild(await createMessageElement(message));
|
||||||
|
}
|
||||||
|
console.log(messages)
|
||||||
|
});
|
|
@ -1,35 +0,0 @@
|
||||||
let socket
|
|
||||||
|
|
||||||
function getCookie(name) {
|
|
||||||
const value = `; ${document.cookie}`;
|
|
||||||
const parts = value.split(`; ${name}=`);
|
|
||||||
if (parts.length === 2) return parts.pop().split(';').shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', function () {
|
|
||||||
let connectionString = location.protocol == "https:" ? `wss://${window.location.hostname}:${window.location.port}` : `ws://${window.location.hostname}:${window.location.port}`
|
|
||||||
socket = new WebSocket(connectionString)
|
|
||||||
console.log(getCookie("token"))
|
|
||||||
socket.addEventListener('open', (e) => {
|
|
||||||
socket.send(JSON.stringify(
|
|
||||||
{
|
|
||||||
"action": "message",
|
|
||||||
"token": getCookie("token"),
|
|
||||||
"content": {
|
|
||||||
"chatId": 1,
|
|
||||||
"text": "test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
})
|
|
||||||
|
|
||||||
fetch("/api/getMessagesFromChat", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ chatId: 1, fromTimestamp: '2012.01.01 00:00:00.000000', toTimestamp: '2026.01.01 00:00:00.000000' })
|
|
||||||
}).then(response => response.text())
|
|
||||||
.then((response => console.log(response)))
|
|
||||||
})
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
let socket
|
||||||
|
|
||||||
|
window.addEventListener('load', async function () {
|
||||||
|
let connectionString = location.protocol == "https:" ? `wss://${window.location.hostname}:${window.location.port}` : `ws://${window.location.hostname}:${window.location.port}`
|
||||||
|
socket = new WebSocket(connectionString)
|
||||||
|
socket.addEventListener('open', (e) => {
|
||||||
|
socket.send(JSON.stringify(
|
||||||
|
{
|
||||||
|
"action": "message",
|
||||||
|
"content": {
|
||||||
|
"chatId": 1,
|
||||||
|
"text": "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.addEventListener('message', (m) => {
|
||||||
|
if (m.data == "successful") return;
|
||||||
|
const data = JSON.parse(m.data)
|
||||||
|
switch (data.action) {
|
||||||
|
case "message": {
|
||||||
|
console.log(`New message in chat ${data.content.chatId} from ${data.content.authorId}, which says '${data.content.text}'`)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let chatsIds = await fetch("/api/getChats").then(response => response.text()).then((response) => {
|
||||||
|
return response
|
||||||
|
})
|
||||||
|
let chats = []; // chats with their info
|
||||||
|
chatsIds = eval(chatsIds)
|
||||||
|
for (let chat of chatsIds) {
|
||||||
|
let chatInfo = await fetch(`/api/getChatInfo/${chat}`).then(response => response.text()).then(response => { return response });
|
||||||
|
chatInfo = JSON.parse(chatInfo)
|
||||||
|
chatInfo.id = chat
|
||||||
|
let lastMessage = await fetch(`/api/getMessagesFromChat/by-amount`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ chatId: chat, amount: 1 })
|
||||||
|
}).then(response => response.text()).then((response) => { return response });
|
||||||
|
|
||||||
|
if (lastMessage == undefined || lastMessage == "") {
|
||||||
|
chatInfo.lastMessage = {
|
||||||
|
id: "",
|
||||||
|
author_id: "",
|
||||||
|
time_sent: "",
|
||||||
|
content: ""
|
||||||
|
}
|
||||||
|
chats.push(chatInfo)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
chatInfo.lastMessage = JSON.parse(lastMessage)[0]
|
||||||
|
chats.push(chatInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let chatsContainer = this.document.getElementById('chats');
|
||||||
|
for (let chat of chats) {
|
||||||
|
let chatHTML = `
|
||||||
|
<div class=\"chat-entry\" name=\"${chat.name}\">
|
||||||
|
<p>
|
||||||
|
<a href=\"/chat/${chat.id}\">${chat.name}</a> | ${chat.lastMessage.author_id}: ${chat.lastMessage.content} ${chat.lastMessage.time_sent}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
chatsContainer.innerHTML += chatHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("/api/getMessagesFromChat/by-time", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ chatId: 1, fromTimestamp: '2012.01.01 00:00:00.000000', toTimestamp: '2026.01.01 00:00:00.000000' })
|
||||||
|
}).then(response => response.text())
|
||||||
|
// .then((response => console.log(response)))
|
||||||
|
})
|
|
@ -6,7 +6,8 @@ const { Client } = pg;
|
||||||
client = new Client({
|
client = new Client({
|
||||||
user: "smk",
|
user: "smk",
|
||||||
password: "CHANGEME", // do not forget to change it in docker-compose.yml in db section.
|
password: "CHANGEME", // do not forget to change it in docker-compose.yml in db section.
|
||||||
host: "10.5.0.6", //defined in docker-compose.yml.
|
// host: "10.5.0.6", //defined in docker-compose.yml.
|
||||||
|
host: 'localhost',
|
||||||
port: 5432,
|
port: 5432,
|
||||||
database: "chat"
|
database: "chat"
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Chat</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="header-div">
|
||||||
|
<b id="header-text"></b>
|
||||||
|
</div>
|
||||||
|
<div id="content-div"></div>
|
||||||
|
<div id="typefield-div">
|
||||||
|
<input type="text" onkeypress="sendMessage(event)">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script src="/js/chat.js"></script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -5,6 +5,9 @@
|
||||||
<title>index</title>
|
<title>index</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="chats" name="chats" id="chats">
|
||||||
|
|
||||||
|
</div>
|
||||||
<form action="/api/createChat" method="POST">
|
<form action="/api/createChat" method="POST">
|
||||||
<label for="toInviteIds">ID пользователей для приглашения через пробел</label><br/>
|
<label for="toInviteIds">ID пользователей для приглашения через пробел</label><br/>
|
||||||
<input type="text" id="toInviteIds" name="toInviteIds"><br/>
|
<input type="text" id="toInviteIds" name="toInviteIds"><br/>
|
||||||
|
@ -13,5 +16,5 @@
|
||||||
</form>
|
</form>
|
||||||
<a href="/api/logout">Выйти</a>
|
<a href="/api/logout">Выйти</a>
|
||||||
</body>
|
</body>
|
||||||
<script src="/js/frontend.js"></script>
|
<script src="/js/index.js"></script>
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue