added alert dialog on leave group, new add member to group button icon, adjusted delete contact api to also requre conversation_id to leave group, refactored insertContact

This commit is contained in:
slawk0
2024-12-07 12:59:27 +01:00
parent c5b6bb34ac
commit e2e34f9776
9 changed files with 219 additions and 195 deletions

View File

@@ -1,6 +1,4 @@
const { Client } = require("pg");
const crypto = require("crypto");
const { callback } = require("pg/lib/native/query");
require("dotenv").config();
const client = new Client({
user: process.env.PG_USER,
@@ -327,37 +325,6 @@ async function getMessages(user_id, conversation_id, limit = 50, cursor = 0) {
}
}
async function getConversationId(senderUsername, receiverUsername) {
console.log(
`kldsdfjklsdfjklsdjfkl , senderUsername: ${senderUsername}, receiverUsername: ${receiverUsername}`,
);
const query = `
SELECT conversation_id FROM Conversations
WHERE conversation_type = 'direct'
AND EXISTS (
SELECT 1 FROM Memberships WHERE conversation_id = Conversations.conversation_id AND user_id = $1
)
AND EXISTS (
SELECT 1 FROM Memberships WHERE conversation_id = Conversations.conversation_id AND user_id = $2
)
LIMIT 1;
`;
try {
const result = await client.query(query, [
senderUsername,
receiverUsername,
]);
if (result.rowCount > 0) {
return result.rows[0].conversation_id;
} else {
console.log("No conversation found between these users.");
return null;
}
} catch (e) {
console.error("Failed to get conversation id ", e);
}
}
async function checkUserExist(username) {
const query = `
SELECT 1 FROM Accounts
@@ -408,34 +375,18 @@ async function changePassword(username, newPasswordHash) {
async function insertContact(userUsername, receiverUsername, read) {
const query = `
WITH sender AS (
SELECT user_id
FROM Accounts
WHERE LOWER(username) = $1
LIMIT 1
),
receiver AS (
SELECT user_id
FROM Accounts
WHERE LOWER(username) = $2
LIMIT 1
WITH user_ids AS (
SELECT
(SELECT user_id FROM Accounts WHERE LOWER(username) = LOWER($1)) AS sender_id,
(SELECT user_id FROM Accounts WHERE LOWER(username) = LOWER($2)) AS receiver_id
),
existing_conversation AS (
SELECT conversation_id
FROM Conversations
WHERE conversation_type = 'direct'
AND EXISTS (
SELECT 1
FROM Memberships
WHERE conversation_id = Conversations.conversation_id
AND user_id = (SELECT user_id FROM sender)
)
AND EXISTS (
SELECT 1
FROM Memberships
WHERE conversation_id = Conversations.conversation_id
AND user_id = (SELECT user_id FROM receiver)
)
SELECT c.conversation_id
FROM Conversations c
JOIN Memberships m1 ON c.conversation_id = m1.conversation_id
JOIN Memberships m2 ON c.conversation_id = m2.conversation_id
JOIN user_ids ON m1.user_id = user_ids.sender_id AND m2.user_id = user_ids.receiver_id
WHERE c.conversation_type = 'direct'
LIMIT 1
),
new_conversation AS (
@@ -444,46 +395,45 @@ async function insertContact(userUsername, receiverUsername, read) {
WHERE NOT EXISTS (SELECT 1 FROM existing_conversation)
RETURNING conversation_id
),
final_conversation AS (
conversation AS (
SELECT conversation_id FROM existing_conversation
UNION ALL
SELECT conversation_id FROM new_conversation
),
insert_memberships AS (
new_memberships AS (
INSERT INTO Memberships (conversation_id, user_id)
SELECT conversation_id, user_id
FROM final_conversation, sender
SELECT conv.conversation_id, u.user_id
FROM conversation conv
CROSS JOIN (
SELECT sender_id AS user_id FROM user_ids
UNION ALL
SELECT receiver_id FROM user_ids
) u
WHERE NOT EXISTS (
SELECT 1
FROM Memberships
WHERE conversation_id = final_conversation.conversation_id
AND user_id = sender.user_id
)
UNION ALL
SELECT conversation_id, user_id
FROM final_conversation, receiver
WHERE NOT EXISTS (
SELECT 1
FROM Memberships
WHERE conversation_id = final_conversation.conversation_id
AND user_id = receiver.user_id
SELECT 1
FROM Memberships m
WHERE m.conversation_id = conv.conversation_id
AND m.user_id = u.user_id
)
),
insert_contact AS (
INSERT INTO Contacts (user_id, contact_user_id, read, last_active)
SELECT (SELECT user_id FROM sender), (SELECT user_id FROM receiver), $3, CURRENT_TIMESTAMP
ON CONFLICT DO NOTHING
RETURNING contact_id AS id, contact_user_id AS user_id, last_active, (SELECT conversation_id FROM final_conversation) AS conversation_id
contact_insert AS (
INSERT INTO Contacts (user_id, contact_user_id, read)
SELECT sender_id, receiver_id, $3
FROM user_ids
ON CONFLICT (user_id, contact_user_id)
DO UPDATE SET last_active = CURRENT_TIMESTAMP, read = $3
RETURNING contact_id, contact_user_id, last_active
)
SELECT
ic.id,
ic.user_id,
a.username AS username,
ic.last_active,
ci.contact_id AS id,
ci.contact_user_id AS user_id,
a.username,
ci.last_active,
'direct' AS type,
ic.conversation_id
FROM insert_contact ic
JOIN Accounts a ON a.user_id = ic.user_id;
c.conversation_id
FROM contact_insert ci
JOIN Accounts a ON a.user_id = ci.contact_user_id
CROSS JOIN conversation c;
`;
try {
@@ -492,42 +442,9 @@ async function insertContact(userUsername, receiverUsername, read) {
receiverUsername,
read,
]);
if (result.rows.length > 0) {
return result.rows[0];
}
return null;
} catch (e) {
console.error("Failed to insert contact:", e);
return null;
}
}
async function getConversationId(senderUsername, receiverUsername) {
const query = `
SELECT conversation_id FROM Conversations
WHERE conversation_type = 'direct'
AND EXISTS (
SELECT 1 FROM Memberships WHERE conversation_id = Conversations.conversation_id AND user_id = (SELECT user_id FROM Accounts WHERE username = $1)
)
AND EXISTS (
SELECT 1 FROM Memberships WHERE conversation_id = Conversations.conversation_id AND user_id = (SELECT user_id FROM Accounts WHERE username = $2)
)
LIMIT 1;
`;
try {
const result = await client.query(query, [
senderUsername,
receiverUsername,
]);
if (result.rowCount > 0) {
return result.rows[0].conversation_id;
} else {
console.log("No conversation found between these users.");
return null;
}
} catch (e) {
console.error("Failed to get conversation id ", e);
return result.rows[0] || null;
} catch (error) {
console.error("Failed to insert contact:", error);
return null;
}
}
@@ -590,33 +507,65 @@ async function getContacts(user_id) {
}));
// Combine contacts and groups
const combinedContacts = [...contacts, ...groups];
return combinedContacts;
return [...contacts, ...groups];
} catch (e) {
console.error("Failed to get contacts and groups:", e);
return [];
}
}
async function deleteContact(user_id, contact_id) {
const query = `
DELETE FROM Contacts
WHERE (user_id = $1 AND contact_id = $2)
async function deleteContact(user_id, contact_id, conversation_id) {
// Check if the conversation is a group
const checkConversationTypeQuery = `
SELECT conversation_type FROM Conversations WHERE conversation_id = $1;
`;
try {
const result = await client.query(query, [user_id, contact_id]);
if (result.rowCount === 0) {
console.log("No matching contact found with:", {
const conversationTypeResult = await client.query(
checkConversationTypeQuery,
[conversation_id],
);
if (conversationTypeResult.rows.length === 0) {
console.log(
"No conversation found with conversation_id: ",
conversation_id,
);
return;
}
const conversationType = conversationTypeResult.rows[0].conversation_type;
if (conversationType === "group") {
// Remove the user from the group in the Memberships table
const removeUserFromGroupQuery = `
DELETE FROM Memberships WHERE conversation_id = $1 AND user_id = $2;
`;
const removeResult = await client.query(removeUserFromGroupQuery, [
conversation_id,
user_id,
contact_id,
});
]);
if (removeResult.rowCount === 0) {
console.log("No matching membership found with:", {
conversation_id,
user_id,
});
} else {
console.log("Successfully removed user from group for: ", user_id);
}
} else {
console.log("Successfully deleted contact for: ", user_id);
// For direct conversations, proceed with deleting the contact
const query = `
DELETE FROM Contacts WHERE (user_id = $1 AND contact_id = $2);
`;
const result = await client.query(query, [user_id, contact_id]);
if (result.rowCount === 0) {
console.log("No matching contact found with:", { user_id, contact_id });
} else {
console.log("Successfully deleted contact for: ", user_id);
}
}
} catch (e) {
console.error("Failed to remove contact ", e);
console.error("Failed to remove contact or user from group ", e);
}
}
@@ -736,10 +685,8 @@ async function getMembers(conversation_id) {
const result = await client.query(query, [conversation_id]);
const members = result.rows;
if (members.length > 0) {
console.log(`Members of conversation_id ${conversation_id}:`, members);
return members;
} else {
console.log(`No members found for conversation_id ${conversation_id}.`);
return [];
}
} catch (e) {

View File

@@ -6,7 +6,6 @@ const server = createServer(app);
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const bcrypt = require("bcrypt");
const crypto = require("crypto");
const saltRounds = 10;
const { Server } = require("socket.io");
const io = new Server(server);
@@ -17,9 +16,7 @@ require("dotenv").config();
const PORT = process.env.SERVER_PORT;
const {
insertUser,
insertMessage,
checkUserExist,
changePassword,
getPassword,
getUserId,
deleteContact,
@@ -34,13 +31,12 @@ const {
MAX_PASSWORD_LENGTH,
MIN_PASSWORD_LENGTH,
} = require("./utils/filter");
const { generateJwtToken, verifyJwtToken } = require("./auth/jwt");
const { generateJwtToken } = require("./auth/jwt");
const { initializeSocket } = require("./socket/socket");
const {
getContacts,
insertContact,
createGroup,
addMemberToGroup,
addMemberToGroupByUsername,
contactSuggestion,
deleteMessage,
@@ -67,8 +63,7 @@ const storage = multer.diskStorage({
const extension = extname(file.originalname).toString();
const originalName = file.originalname;
const filename = originalName?.substring(0, originalName.lastIndexOf("."));
//const finalName = uniqueSuffix + extension;
const finalName = `${filename}-${Math.round(Math.random() * 1e9)}${extension}`;
const finalName = `${filename}-${uniqueSuffix}${extension}`;
file.finalName = finalName;
cb(null, finalName);
},
@@ -207,15 +202,22 @@ app.get("/api/auth/validate", authorizeUser, (req, res) => {
});
app.delete(
"/api/chat/contacts/:contact_id",
"/api/chat/contacts/:contact_id/:conversation_id",
authorizeUser,
async (req, res) => {
if (!req.params.contact_id) {
return res.status(400).json({ message: "Missing contact_id parameter" });
}
const contact_id = parseInt(req.params.contact_id);
const conversation_id = parseInt(req.params.conversation_id);
await deleteContact(req.user.user_id, contact_id);
console.log("Delete contact for: ", req.params.contact_id, conversation_id);
const result = await deleteContact(
req.user.user_id,
contact_id,
conversation_id,
);
if (result?.message) {
return res.status(400).json({ message: result.message });
}
return res.status(200).json({ message: "Successfully deleted contact" });
},
);
@@ -251,14 +253,16 @@ app.post("/api/chat/contact/:contact", authorizeUser, async (req, res) => {
}
try {
console.log("contact post request: ", usernameContact);
const result = await insertContact(
req.user.username,
usernameContact,
true,
);
if (result.conversation_id === null) {
res.status(500).json({ message: "Failed to create conversation" });
if (result === null) {
return res.status(500).json({ message: "Failed to create conversation" });
}
console.log("sent contact post response: ", result);
return res.status(200).json(result);
} catch (e) {
console.error("Failed to insert contact: ", e);
@@ -283,7 +287,7 @@ app.get(
async (req, res) => {
const contact = req.params.contact;
if (!contact) {
return res.status(401).json({
return res.status(400).json({
message: "contact not provided",
});
}
@@ -313,7 +317,9 @@ app.get("/api/chat/messages/:contact", authorizeUser, async (req, res) => {
cursor,
);
if (messages && messages.length === 0) {
return res.status(404).json({ message: "No more messages found" });
return res
.status(200)
.json({ messages: [], message: "No more messages found" });
}
console.log("Sent messages for: ", req.user.user_id);

View File

@@ -1,9 +1,7 @@
const { Server } = require("socket.io");
const { insertMessage, getConversationsForUser } = require("../db/db");
const { isValidUsername } = require("../utils/filter");
const { verifyJwtToken } = require("../auth/jwt");
const console = require("node:console");
const { callback } = require("pg/lib/native/query");
function initializeSocket(io) {
io.use((socket, next) => {
@@ -113,14 +111,8 @@ function initializeSocket(io) {
return;
}
const {
message_id,
content,
sent_at,
sender_id,
conversation_id,
timestamp,
} = insertedMessage;
const { message_id, content, sent_at, sender_id, conversation_id } =
insertedMessage;
console.log("(socket) received from chat message", msg);
io.to(username).to(recipient).to(recipient_id).emit("chat message", {
@@ -138,7 +130,7 @@ function initializeSocket(io) {
message,
attachment_urls,
recipient,
timestamp,
sent_at,
message_id,
sender_id,
});