/
Shymer123
/
Messenger
Обзор
Документация
Войти
/
Shymer123
/
Messenger
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Server/SourceFiles/DatabaseManager/DatabaseManager.cpp
1 677 строк
57 KB
Shymer123
added chat creation confirmation, and now the group and chat are created through procedures (reduced delays in accessing the database)
17 май 2025, 06:06
17 май 2025, 06:06
7df2171
Код
Авторство
О чём код?
#include "DatabaseManager.h" #include "FileHandler/FileHandler.h" #include "MainServer/Server.h" #include "ServerCache/ServerCache.h" #include "Logger/Logger.h" #include "SharedEnums/ChatTypes.h" #include "SharedEnums/MessageType.h" #include "Utils/Utils.h" #include <sys/socket.h> #include <fstream> DatabaseManager::DatabaseManager() : isConnected(false) {} DatabaseManager& DatabaseManager::getInstance() { static DatabaseManager instance; if(!instance.isConnected) { instance.connectToDatabase(); instance.isConnected = true; } return instance; } void DatabaseManager::setServerPointer(Server* server) { this->server = server; } void DatabaseManager::connectToDatabase() { try { std::ifstream inputFile("config.json"); if(!inputFile.is_open()) { throw std::runtime_error("Unable to open config.json file"); } json config; inputFile >> config; inputFile.close(); if(config.contains("database")) { const json& dbConfig = config["database"]; std::string host = dbConfig.value("host", "127.0.0.1"); std::string user = dbConfig.value("user", ""); std::string password = dbConfig.value("password", ""); std::string database = dbConfig.value("database_name", ""); sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance(); connection = std::shared_ptr<sql::Connection>(driver->connect(host, user, password)); connection->setSchema(database); Logger::getInstance().logError("Successfully connected to the database: " + database, Logger::logLevel::Info); std::cout << "Successfully connected to the database: " << database << std::endl; } else { throw std::runtime_error("Database section not found in config file"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError("SQL Error in connectToDatabase: " + std::string(sqlEx.what()), Logger::logLevel::Error); std::cerr << "SQL Error: " << sqlEx.what() << std::endl; } catch(const std::exception& ex) { Logger::getInstance().logError("General Error in connectToDatabase: " + std::string(ex.what()), Logger::logLevel::Error); std::cerr << "General Error in connectToDatabase: " << ex.what() << std::endl; } } json DatabaseManager::getIsNameAlreadyUse(const std::string& userName) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT COUNT(*) FROM Authorization WHERE nickName = ?" )); stmt->setString(1, userName); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { int count = res->getInt(1); response["action"] = "getIsNameAlreadyUse"; response["isNameUse"] = (count > 0); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getIsNameAlreadyUse: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getIsNameAlreadyUse: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getIsPhoneNumberAlreadyUse(const std::string& phoneNumber) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT COUNT(*) FROM Authorization WHERE phoneNumber = ?" )); stmt->setString(1, phoneNumber); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { int count = res->getInt(1); response["action"] = "getIsPhoneNumberAlreadyUse"; response["isPhoneNumberUse"] = (count > 0); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getIsPhoneNumberAlreadyUse: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getIsPhoneNumberAlreadyUse: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getStoredPasswordData(const std::string& userName) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT Password, Salt FROM Authorization WHERE nickName = ?" )); stmt->setString(1, userName); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getStoredPasswordData"; response["storedHash"]= res->getString("Password"); response["storedSalt"] = res->getString("Salt"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getStoredPasswordData: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getStoredPasswordData: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getUserID(const std::string& userName) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT id FROM Authorization WHERE nickName = ?" )); stmt->setString(1, userName); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getUserID"; response["userID"] = res->getInt(1); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getUserID: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getUserID: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getChats(const int userID) { json response; json chats = json::array(); try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "WITH LatestMessages AS ( " "SELECT " " chat_id, " " message, " " timestamp, " " message_type, " " message_id, " " (SELECT chatType FROM listChats WHERE chatID = Messages.chat_id) AS chatType " " FROM Messages " " WHERE (chat_id, timestamp) IN ( " " SELECT chat_id, MAX(timestamp) " " FROM Messages " " GROUP BY chat_id " " ) " " ), " "ChatData AS ( " " SELECT " " lc.chatID, " " CASE " " WHEN lc.user1ID = ? THEN uc.ContactName " " ELSE COALESCE(uc_reversed.ContactName, auth.nickName) " " END AS chatName, " " lm.message_id, " " lm.message_type, " " lm.message AS lastMessage, " " lm.timestamp AS lastMessageTime, " " NULL AS role, " " lc.chatType, " " CASE " " WHEN lc.user1ID = ? THEN lc.user2ID " " ELSE lc.user1ID " " END AS userID " " FROM listChats lc " " LEFT JOIN userContacts uc ON " " (lc.user1ID = ? AND uc.user1ID = lc.user1ID AND uc.user2ID = lc.user2ID) " " LEFT JOIN userContacts uc_reversed ON " " (lc.user2ID = ? AND uc_reversed.user1ID = ? AND uc_reversed.user2ID = lc.user1ID) " " LEFT JOIN Authorization auth ON " " (lc.user2ID = ? AND auth.id = lc.user1ID) " " LEFT JOIN LatestMessages lm ON " " lm.chat_id = lc.chatID AND " " (lm.chatType = 'dialog' OR lm.chatType = 'favourite') " " WHERE " " (lc.user1ID = ? OR lc.user2ID = ?) AND " " (lc.chatType = 'dialog' OR lc.chatType = 'favourite') " " " "UNION ALL " " " "SELECT " " lc.chatID, " " lc.groupName AS chatName, " " lm.message_id, " " lm.message_type, " " lm.message AS lastMessage, " " lm.timestamp AS lastMessageTime, " " gm.role, " " lc.chatType, " " NULL AS userID " " FROM listChats lc " " LEFT JOIN groupMembers gm ON " " lc.chatID = gm.groupID AND " " gm.userID = ? " " LEFT JOIN LatestMessages lm ON " " lm.chat_id = lc.chatID AND " " lm.chatType = 'group' " " WHERE " " lc.chatType = 'group' AND " " gm.userID IS NOT NULL " "), " "RankedChats AS ( " " SELECT *, " " ROW_NUMBER() OVER ( " " PARTITION BY chatID " " ORDER BY lastMessageTime DESC, message_id DESC " " ) AS rn " " FROM ChatData " ") " "SELECT " " chatID, " " userID, " " chatName, " " message_id, " " message_type, " " lastMessage, " " lastMessageTime, " " role, " " chatType " " FROM RankedChats " " WHERE rn = 1; " )); stmt->setInt(1, userID); stmt->setInt(2, userID); stmt->setInt(3, userID); stmt->setInt(4, userID); stmt->setInt(5, userID); stmt->setInt(6, userID); stmt->setInt(7, userID); stmt->setInt(8, userID); stmt->setInt(9, userID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); while(res->next()) { json chat; std::string rawChatType = res->getString("chatType"); ChatTypes chatType; if(rawChatType == "dialog") chatType = ChatTypes::Dialog; else if(rawChatType == "favourite") chatType = ChatTypes::Favourite; else if(rawChatType == "group") chatType = ChatTypes::Group; else chatType = ChatTypes::Unknown; chat["chatType"] = chatType; chat["id"] = res->getInt("chatID"); std::string rawMessageType = res->getString("message_type"); MessageType messageType; if(rawMessageType == "TEXT") messageType = MessageType::Text; else if(rawMessageType == "FILE") messageType = MessageType::File; else if(rawMessageType == "IMAGE") messageType = MessageType::Image; chat["lastMessageType"] = messageType; chat["lastMessage"] = res->getString("lastMessage"); chat["lastMessageTime"] = res->getString("lastMessageTime"); switch (chatType) { case ChatTypes::Dialog: chat["title"] = res->getString("chatName"); chat["participantID"] = res->getInt("userID"); break; case ChatTypes::Favourite: break; case ChatTypes::Group: chat["title"] = res->getString("chatName"); chat["memberRole"] = res->getInt("role"); break; case ChatTypes::Unknown: break; } chats.push_back(chat); } response["action"] = "getChats"; response["chats"] = chats; } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getChats: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getChats: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getSingleChat(const int chatID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT " "message_type, " "message AS lastMessage, " "timestamp AS lastMessageTime " "FROM Messages " "WHERE chat_id = ? " "ORDER BY timestamp DESC " "LIMIT 1" )); stmt->setInt(1, chatID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getSingleChat"; std::string rawMessageType = res->getString("message_type"); MessageType messageType; if(rawMessageType == "TEXT") messageType = MessageType::Text; else if(rawMessageType == "FILE") messageType = MessageType::File; else if(rawMessageType == "IMAGE") messageType = MessageType::Image; response["lastMessageType"] = messageType; response["lastMessage"] = res->getString("lastMessage"); response["lastMessageTime"] = res->getString("lastMessageTime"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getSingleChat: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getSingleChat: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getMessages(const int chatID) { json response; json messages = json::array(); try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT msg.message_id AS message_id, auth.id AS user_id, auth.nickName AS user_name, " "msg.message AS text_message, msg.timestamp AS time_message, " "f.id AS file_id, f.file_name, f.file_size " "FROM Messages msg " "JOIN Authorization auth ON msg.userID = auth.id " "LEFT JOIN Files f ON f.message_id = msg.message_id " "WHERE msg.chat_id = ? " "ORDER BY msg.timestamp" )); stmt->setInt(1, chatID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); int lastMessageId = -1; while(res->next()) { int messageId = res->getInt("message_id"); if(messageId != lastMessageId) { json currentMessage; currentMessage["userID"] = res->getInt("user_id"); currentMessage["userName"] = res->getString("user_name"); std::string textMessage = res->getString("text_message"); if(textMessage.empty()) { currentMessage["textMessage"] = ""; } else { currentMessage["textMessage"] = textMessage; } currentMessage["timeMessage"] = res->getString("time_message"); currentMessage["files"] = json::array(); messages.push_back(currentMessage); lastMessageId = messageId; } if(!res->isNull("file_id")) { json file; file["fileID"] = res->getInt("file_id"); file["fileName"] = res->getString("file_name"); file["fileSize"] = res->getUInt("file_size"); messages.back()["files"].push_back(file); } } response["action"] = "getMessages"; response["messages"] = messages; } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getMessages: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getMessages: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } void DatabaseManager::downloadFile(const int clientSocket, const int fileID) { try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT file_name, file_uuid FROM Files WHERE id = ?" )); stmt->setInt(1, fileID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { std::string fileName = res->getString("file_name"); std::string fileUUID = res->getString("file_uuid"); std::string joinName = fileUUID + "_" + fileName; FileHandler::sendFile(server, clientSocket, fileID, joinName); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in downloadFile: ") + sqlEx.what(), Logger::logLevel::Error); } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in downloadFile: ") + ex.what(), Logger::logLevel::Error);; } } json DatabaseManager::getContacts(const int userID) { json response; json contacts = json::array(); try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT contactName FROM userContacts WHERE user1ID = ?" )); stmt->setInt(1, userID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); while(res->next()) { json contact; contact["contactName"] = res->getString("contactName"); contacts.push_back(contact); } response["action"] = "getContacts"; response["contacts"] = contacts; } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getContacts: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getContacts: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getUserData(const int chatID, const int currentUserID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT " " auth.id, " " COALESCE(uc.contactName, auth.nickName) AS contactName, " " auth.nickName, " " auth.phoneNumber " "FROM " " listChats lc " "JOIN " " Authorization AS auth ON auth.id = " " CASE " " WHEN lc.user1ID = ? THEN lc.user2ID " " ELSE lc.user1ID " " END " "LEFT JOIN " " userContacts AS uc ON uc.user2ID = auth.id AND uc.user1ID = ? " "WHERE " " lc.chatID = ? AND (lc.user1ID = ? OR lc.user2ID = ?)" )); stmt->setInt(1, currentUserID); stmt->setInt(2, currentUserID); stmt->setInt(3, chatID); stmt->setInt(4, currentUserID); stmt->setInt(5, currentUserID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getUserData"; response["userID"] = res->getInt("id"); response["chatName"] = res->getString("contactName"); response["username"] = res->getString("nickName"); response["phoneNumber"] = res->getString("phoneNumber"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getUserData: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getUserData: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getGroupData(const int chatID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT " " COUNT(*) AS memberCount, " " lc.groupName AS groupName " "FROM " " groupMembers gm " "JOIN " " listChats lc ON gm.groupID = lc.chatID " "WHERE " " gm.groupID = ? " "GROUP BY " " lc.groupName" )); stmt->setInt(1, chatID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getGroupData"; response["chatName"] = res->getString("groupName"); int count = res->getInt("memberCount"); if(count == 1) { response["status"] = "1 member"; } else { response["status"] = std::to_string(count) + " members"; } } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getGroupData: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getGroupData: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getDialogIDAfterCreation(const int currentUserID, const int user2ID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT chatID FROM listChats WHERE user1ID = ? AND user2ID = ?" )); stmt->setInt(1, currentUserID); stmt->setInt(2, user2ID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getDialogIDAfterCreation"; response["dialogID"] = res->getInt("chatID"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getDialogIDAfterCreation: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getDialogIDAfterCreation: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getGroupID(const int userID, const std::string& groupName) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT lc.chatID " "FROM listChats lc " "JOIN groupMembers gm " "ON lc.chatID = gm.groupID " "WHERE gm.userID = ? " "AND lc.groupName = ? " "AND lc.chatType = 'group' " )); stmt->setInt(1, userID); stmt->setString(2, groupName); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getGroupID"; response["groupID"] = res->getInt("groupID"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getGroupID: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getGroupID: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getMembersIDInGroup(const int userID, const int chatID) { json response; json membersArray = json::array(); try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT userID FROM groupMembers WHERE userID != ? AND groupID = ?" )); stmt->setInt(1, userID); stmt->setInt(2, chatID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); while(res->next()) { int userID = res->getInt("userID"); membersArray.push_back(userID); } response["action"] = "getMembersIDInGroup"; response["participantsID"] = membersArray; } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getMembersIDInGroup: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getMembersIDInGroup: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getParticipantIDInDialogUsePhoneNumber(const std::string& user2PhoneNumber) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT id FROM Authorization WHERE phoneNumber = ?" )); stmt->setString(1, user2PhoneNumber); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getParticipantIDInDialogUsePhoneNumber"; response["participantID"] = res->getInt("id"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getParticipantIDInDialog: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getParticipantIDInDialog: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getPhoneNumberUser2InDialog(const int userID, const std::string &chatName) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT phoneNumber FROM userContacts WHERE user1ID = ? AND contactName = ?" )); stmt->setInt(1, userID); stmt->setString(2, chatName); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getPhoneNumberUser2InDialog"; response["phoneNumber"] = res->getString("phoneNumber"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getPhoneNumberUser2InDialog: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getPhoneNumberUser2InDialog: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getPhoneNumber(const int userID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT phoneNumber FROM Authorization WHERE id = ?" )); stmt->setInt(1, userID); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getPhoneNumber"; response["phoneNumber"] = res->getString("phoneNumber"); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getPhoneNumber: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getPhoneNumber: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getMembersIDForSelected(const int userID, const std::vector<std::string>& selectedMembers) { json response; try { std::ostringstream placeholders; for(size_t i = 0; i < selectedMembers.size(); ++i) { if(i > 0) { placeholders << ","; } placeholders << "?"; } std::string query = "SELECT user2ID FROM userContacts WHERE user1ID = ? AND contactName IN (" + placeholders.str() + ")"; std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement(query)); stmt->setInt(1, userID); for(size_t i = 0; i < selectedMembers.size(); ++i) { stmt->setString(static_cast<int>(i + 2), selectedMembers[i]); } std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); json membersArray = json::array(); while(res->next()) { membersArray.push_back(res->getInt("user2ID")); } response["action"] = "getMembersIDForSelected"; response["id"] = membersArray; } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getMembersIDForSelected: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getMembersIDForSelected: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getIsCombinedNameExists(const int userID, const std::string& combinedName) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT COUNT(*) FROM userContacts WHERE user1ID = ? AND contactName = ?" )); stmt->setInt(1, userID); stmt->setString(2, combinedName); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { int count = res->getInt(1); response["action"] = "getIsCombinedNameExists"; response["isNameExists"] = (count > 0); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getIsCombinedNameExists: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getIsCombinedNameExists: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } json DatabaseManager::getIsPhoneNumberValid(const int userID, const std::string& phoneNumber) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "SELECT " "EXISTS (SELECT 1 FROM Authorization WHERE phoneNumber = ?) AS isInDatabase, " "EXISTS (SELECT 1 FROM userContacts WHERE user1ID = ? AND phoneNumber = ?) AS isInMyContacts" )); stmt->setString(1, phoneNumber); stmt->setInt(2, userID); stmt->setString(3, phoneNumber); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery()); if(res->next()) { response["action"] = "getIsPhoneNumberValid"; response["isInDatabase"] = static_cast<bool>(res->getInt("isInDatabase")); response["isInMyContacts"] = static_cast<bool>(res->getInt("isInMyContacts")); } } catch(const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in getIsPhoneNumberValid: ") + sqlEx.what(), Logger::logLevel::Error); response["error"] = "Database query failed"; } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in getIsPhoneNumberValid: ") + ex.what(), Logger::logLevel::Error); response["error"] = "Internal server error"; } return response; } // adding data only void DatabaseManager::addUserInDatabase(const std::string& userName, const std::string& hashedPassword, const std::string& salt, const std::string& phoneNumber) { try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "INSERT INTO Authorization(nickName, Password, Salt, phoneNumber) VALUES(?, ?, ?, ?)" )); stmt->setString(1, userName); stmt->setString(2, hashedPassword); stmt->setString(3, salt); stmt->setString(4, phoneNumber); stmt->executeUpdate(); } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in addUserInDatabase: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in addUserInDatabase: ") + ex.what(), Logger::logLevel::Error); } } void DatabaseManager::addUserInServerCache(const int clientSocket, const int userID) { try { ServerCache::getInstance().addUserInServerCache(clientSocket, userID); } catch(const std::exception& ex) { Logger::getInstance().logError(std::string("Error in addUserInServerCache: ") + ex.what(), Logger::logLevel::Error); } } json DatabaseManager::sendMessage(MessageData& data, std::set<int>& participantsID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "INSERT INTO Messages(userID, chat_id, message, message_type) " "VALUES(?, ?, ?, ?)" )); stmt->setInt(1, data.getUserID()); stmt->setInt(2, data.getChatID()); stmt->setString(3, data.getContent<std::string>()); stmt->setInt(4, static_cast<int>(data.getMessageType())); stmt->executeUpdate(); response["action"] = "messageDelivered"; response["tempID"] = data.getMessageID(); response["chatID"] = data.getChatID(); std::shared_ptr<sql::Statement> lastIdStmt(connection->createStatement()); std::shared_ptr<sql::ResultSet> res(lastIdStmt->executeQuery( "SELECT LAST_INSERT_ID()" )); int lastMessageID = -1; if(res->next()) { lastMessageID = res->getInt(1); } if (lastMessageID == -1) { throw std::runtime_error("Failed to retrieve last inserted message ID."); } response["messageID"] = lastMessageID; data.setMessageID(std::to_string(lastMessageID)); std::shared_ptr<sql::PreparedStatement> chatTypeStmt(connection->prepareStatement( "SELECT chatType FROM listChats WHERE chatID = ?" )); chatTypeStmt->setInt(1, data.getChatID()); std::shared_ptr<sql::ResultSet> chatTypeRes(chatTypeStmt->executeQuery()); if(chatTypeRes->next()) { std::string rawChatType = chatTypeRes->getString("chatType"); ChatTypes chatType; if(rawChatType == "dialog") chatType = ChatTypes::Dialog; else if(rawChatType == "group") chatType = ChatTypes::Group; else if(rawChatType == "favourite") chatType = ChatTypes::Favourite; else chatType = ChatTypes::Unknown; if(chatType == ChatTypes::Group) { updateMessagesInParticipantsGroup(data, participantsID); } else if(chatType == ChatTypes::Dialog) { updateMessagesInParticipantDialog(data, *participantsID.begin()); } } else { throw std::runtime_error("Chat not found"); } } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in sendMessage: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in sendMessage: ") + ex.what(), Logger::logLevel::Error); } return response; } json DatabaseManager::sendFile(MessageData& data, std::set<int>& participantsID) { json response; try { int messageID = -1; std::string messageQuery = "INSERT INTO Messages (userID, chat_id, message, message_type) " "VALUES (?, ?, ?, ?)"; std::shared_ptr<sql::PreparedStatement> stmtMessage(connection->prepareStatement(messageQuery)); stmtMessage->setInt(1, data.getUserID()); stmtMessage->setInt(2, data.getChatID()); stmtMessage->setString(3, data.getAdditionalData<std::string>().empty() ? "" : data.getAdditionalData<std::string>()); stmtMessage->setInt(4, static_cast<int>(data.getMessageType())); stmtMessage->executeUpdate(); std::shared_ptr<sql::Statement> stmt(connection->createStatement()); std::shared_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT LAST_INSERT_ID()")); if (res->next()) { messageID = res->getInt(1); } if (messageID != -1) { std::string query = "INSERT INTO Files(user_id, chat_id, file_name, file_uuid, message_id, file_size) VALUES "; std::vector<std::string> valueArray; for (const auto& file : data.getContent<json>()) { std::string fileName = file["fileName"]; uint64_t fileSize = file["fileSize"]; std::string fileUUID = file["fileUUID"]; valueArray.push_back("(" + std::to_string(data.getUserID()) + ", " + std::to_string(data.getChatID()) + ", '" + fileName + "', '" + fileUUID + "'," + std::to_string(messageID) + ", " + std::to_string(fileSize) + ")"); } std::ostringstream oss; for (size_t i = 0; i < valueArray.size(); ++i) { oss << valueArray[i]; if (i != valueArray.size() - 1) { oss << ", "; } } query += oss.str(); std::shared_ptr<sql::PreparedStatement> stmtFiles(connection->prepareStatement(query)); stmtFiles->executeUpdate(); std::string selectFilesQuery = "SELECT id FROM Files WHERE message_id = ? ORDER BY id ASC"; std::shared_ptr<sql::PreparedStatement> stmtGetFiles(connection->prepareStatement(selectFilesQuery)); stmtGetFiles->setInt(1, messageID); std::shared_ptr<sql::ResultSet> resFiles(stmtGetFiles->executeQuery()); std::vector<int> fileIDVector; while(resFiles->next()) { fileIDVector.push_back(resFiles->getInt("id")); } data.setFilesID(fileIDVector); json filesID = json::array(); for(const auto& fileID : fileIDVector) { filesID.push_back(fileID); } response["action"] = "messageDelivered"; response["tempID"] = data.getMessageID(); response["chatID"] = data.getChatID(); response["messageID"] = messageID; response["filesID"] = filesID; data.setMessageID(std::to_string(messageID)); std::shared_ptr<sql::PreparedStatement> chatTypeStmt(connection->prepareStatement( "SELECT chatType FROM listChats WHERE chatID = ?" )); chatTypeStmt->setInt(1, data.getChatID()); std::shared_ptr<sql::ResultSet> chatTypeRes(chatTypeStmt->executeQuery()); if(chatTypeRes->next()) { std::string rawChatType = chatTypeRes->getString("chatType"); ChatTypes chatType; if(rawChatType == "dialog") chatType = ChatTypes::Dialog; else if(rawChatType == "group") chatType = ChatTypes::Group; else if(rawChatType == "favourite") chatType = ChatTypes::Favourite; else chatType = ChatTypes::Unknown; if(chatType == ChatTypes::Group) { updateMessagesInParticipantsGroup(data, participantsID); } else if(chatType == ChatTypes::Dialog) { updateMessagesInParticipantDialog(data, *participantsID.begin()); } } else { throw std::runtime_error("Chat not found"); } } } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in sendFile: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in sendFile: ") + ex.what(), Logger::logLevel::Error); } return response; } void DatabaseManager::addContact(const int currentUserID, const std::string& currentUserName, const int user2ID, const std::string& user2Name, const std::string& user2PhoneNumber) { try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "INSERT INTO userContacts(user1ID, nickName, user2ID, contactName, phoneNumber) VALUES(?, ?, ?, ?, ?)" )); stmt->setInt(1, currentUserID); stmt->setString(2, currentUserName); stmt->setInt(3, user2ID); stmt->setString(4, user2Name); stmt->setString(5, user2PhoneNumber); stmt->executeUpdate(); } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in addContact: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in addContact: ") + ex.what(), Logger::logLevel::Error); } } json DatabaseManager::addFavourite(const int userID, const int tempChatID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "INSERT INTO listChats(user1ID, user2ID, chatType, isExist) VALUES(?, ?, ?, 1)" )); stmt->setInt(1, userID); stmt->setInt(2, userID); stmt->setInt(3, static_cast<int>(ChatTypes::Favourite)); stmt->executeUpdate(); response["action"] = "chatDelivered"; response["chatType"] = ChatTypes::Favourite; std::shared_ptr<sql::Statement> lastIdStmt(connection->createStatement()); std::shared_ptr<sql::ResultSet> res(lastIdStmt->executeQuery( "SELECT LAST_INSERT_ID()" )); int lastChatID = -1; if(res->next()) { lastChatID = res->getInt(1); } if (lastChatID == -1) { throw std::runtime_error("Failed to retrieve last inserted chat ID."); } response["tempID"] = tempChatID; response["chatID"] = lastChatID; } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in addFavourite: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in addFavourite: ") + ex.what(), Logger::logLevel::Error); } return response; } json DatabaseManager::addDialog(const int currentUserID, const std::string& participantName, const int tempChatID) { json response; try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "CALL createDialog(?, ?, @newChatID, @user2ID)" )); stmt->setInt(1, currentUserID); stmt->setString(2, participantName); stmt->execute(); response["action"] = "chatDelivered"; response["chatType"] = ChatTypes::Dialog; std::shared_ptr<sql::ResultSet> res(connection->createStatement() ->executeQuery("SELECT @newChatID AS chatID, " "@user2ID AS user2ID")); if(res->next()) { int chatID = res->getInt("chatID"); int user2ID = res->getInt("user2ID"); if(chatID != -1) { response["chatID"] = chatID; response["tempID"] = tempChatID; response["participantID"] = user2ID; updateListDialogsForUsers(user2ID); } else { throw std::runtime_error("Error in receiving the chat ID."); } } } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in addDialog: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in addDialog: ") + ex.what(), Logger::logLevel::Error); } return response; } void DatabaseManager::addGroup(const std::string& groupName) { try { std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "INSERT INTO listChats(groupName, chatType, isExist) VALUES (?, ?, 1)" )); stmt->setString(1, groupName); stmt->setInt(2, static_cast<int>(ChatTypes::Group)); stmt->executeUpdate(); } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in addGroup: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in addGroup: ") + ex.what(), Logger::logLevel::Error); } } json DatabaseManager::addGroup(const int userID, const std::string& groupName, const std::vector<std::string>& selectedMembers, const int tempChatID) { json response; try { std::set<int> memberIDs; if(!selectedMembers.empty()) { std::string placeHolders; for(size_t i = 0; i < selectedMembers.size(); ++i) { if(i > 0) { placeHolders += ","; } placeHolders += "?"; } std::string query = "SELECT user2ID FROM userContacts " "WHERE user1ID = ? AND contactName IN (" + placeHolders + ")"; std::shared_ptr<sql::PreparedStatement> idStmt(connection->prepareStatement(query)); idStmt->setInt(1, userID); for(size_t i = 0; i < selectedMembers.size(); ++i) { idStmt->setString(static_cast<int>(i + 2), selectedMembers[i]); } std::shared_ptr<sql::ResultSet> idRes(idStmt->executeQuery()); while(idRes->next()) { memberIDs.insert(idRes->getInt("user2ID")); } } std::string memberIDsStr; for(const int& id : memberIDs) { if(!memberIDsStr.empty()) { memberIDsStr += ","; } memberIDsStr += std::to_string(id); } std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement( "CALL createGroup(?, ?, ?, @newChatID)" )); stmt->setInt(1, userID); stmt->setString(2, groupName); if(selectedMembers.empty()) { stmt->setNull(3, sql::DataType::VARCHAR); } else { stmt->setString(3, memberIDsStr); } stmt->execute(); response["action"] = "chatDelivered"; response["chatType"] = ChatTypes::Group; std::shared_ptr<sql::ResultSet> res(connection->createStatement() ->executeQuery("SELECT @newChatID AS chatID")); if(res->next()) { int newChatID = res->getInt("chatID"); if(newChatID != -1) { response["chatID"] = newChatID; response["tempID"] = tempChatID; updateListGroupsForUsers(newChatID, memberIDs); } else { std::runtime_error("error when adding a group"); } } } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in addGroup: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in addGroup: ") + ex.what(), Logger::logLevel::Error); } return response; } void DatabaseManager::addMembersToGroup(const int groupID, const json& members) { try { std::string query = "INSERT INTO groupMembers(groupID, userID, role) VALUES "; std::vector<std::string> valueArray; for(const auto& member : members) { int userID = member["userID"]; std::string role = member["role"]; valueArray.push_back("(" + std::to_string(groupID) + ", " + std::to_string(userID) + ", '" + role + "')"); } std::ostringstream oss; for(size_t i = 0; i < valueArray.size(); ++i) { oss << valueArray[i]; if(i != valueArray.size() - 1) { oss << ", "; } } query += oss.str(); std::shared_ptr<sql::PreparedStatement> stmt(connection->prepareStatement(query)); stmt->executeUpdate(); } catch (const sql::SQLException& sqlEx) { Logger::getInstance().logError(std::string("SQL Error in addMembersToGroup: ") + sqlEx.what(), Logger::logLevel::Error); } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in addMembersToGroup: ") + ex.what(), Logger::logLevel::Error); } } void DatabaseManager::updateListDialogsForUsers(const int participantID) { json response; try { bool isUserInCache = ServerCache::getInstance().isUserInServerCache(participantID); if(isUserInCache) { response["action"] = "updateChatList"; std::string jsonString = response.dump(); std::vector<char> jsonVector(jsonString.begin(), jsonString.end()); std::vector<char> compressedData = Utils::compressData(jsonVector); int socket = ServerCache::getInstance().getSocketByUserID(participantID); server->safeSend(socket, 0x01, compressedData); } } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in updateListDialogsForUsers: ") + ex.what(), Logger::logLevel::Error); } } void DatabaseManager::updateListGroupsForUsers(const int groupID, std::set<int>& membersID) { (void)groupID; json response; response["action"] = "updateChatList"; try { const auto& clientMap = ServerCache::getInstance().getSocketToID(); for(const auto& client : clientMap) { int clientSocket = client.first; int userID = client.second; if(membersID.contains(userID)) { std::string jsonString = response.dump(); std::vector<char> jsonArray(jsonString.begin(), jsonString.end()); std::vector<char> compressedData = Utils::compressData(jsonArray); server->safeSend(clientSocket, 0x01, compressedData); membersID.erase(userID); if(membersID.empty()) { break; } } } } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in updateListGroupsForUsers: ") + ex.what(), Logger::logLevel::Error); } } void DatabaseManager::updateMessagesInParticipantDialog(const MessageData& data, const int participantID) { json response; response["action"] = "updateMessages"; response["userID"] = data.getUserID(); response["chatID"] = data.getChatID(); response["userName"] = data.getUserName(); response["type"] = data.getMessageType(); response["timestamp"] = data.getTimestamp(); response["messageID"] = data.getMessageID(); response["status"] = "received"; if(data.getMessageType() == MessageData::MessageType::Text) { response["content"] = data.getContent<std::string>(); } else if(data.getMessageType() == MessageData::MessageType::File) { json filesBeforeProcessing = data.getContent<json>(); json fileArray; const std::vector<int>& filesID = data.getFilesID(); size_t index = 0; for(const auto& file : filesBeforeProcessing) { json fileJson; fileJson["fileID"] = filesID[index]; fileJson["fileName"] = file["fileName"]; fileJson["fileSize"] = file["fileSize"]; fileArray.push_back(fileJson); ++index; } response["files"] = fileArray; response["TextMessage"] = data.getAdditionalData<std::string>(); } try { const auto& clientMap = ServerCache::getInstance().getSocketToID(); for(const auto& client : clientMap) { int clientSocket = client.first; int userID = client.second; if(userID == participantID) { std::string jsonString = response.dump(); std::vector<char> jsonVector(jsonString.begin(), jsonString.end()); std::vector<char> compressedData = Utils::compressData(jsonVector); server->safeSend(clientSocket, 0x01, compressedData); break; } } } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in updateMessagesInParticipantDialog: ") + ex.what(), Logger::logLevel::Error); } } void DatabaseManager::updateMessagesInParticipantsGroup(const MessageData& data, std::set<int>& membersID) { json response; response["action"] = "updateMessages"; response["userID"] = data.getUserID(); response["chatID"] = data.getChatID(); response["userName"] = data.getUserName(); response["type"] = data.getMessageType(); response["timestamp"] = data.getTimestamp(); response["messageID"] = data.getMessageID(); if(data.getMessageType() == MessageData::MessageType::Text) { response["content"] = data.getContent<std::string>(); } else if(data.getMessageType() == MessageData::MessageType::File) { json filesBeforeProcessing = data.getContent<json>(); json fileArray; for(const auto& file : filesBeforeProcessing) { json fileJson; fileJson["fileName"] = file["fileName"]; fileJson["fileSize"] = file["fileSize"]; fileArray.push_back(fileJson); } response["files"] = fileArray; response["TextMessage"] = data.getAdditionalData<std::string>(); } try { const auto& clientMap = ServerCache::getInstance().getSocketToID(); for(const auto& client : clientMap) { int clientSocket = client.first; int userID = client.second; if(membersID.contains(userID)) { std::string jsonString = response.dump(); std::vector<char> jsonVector(jsonString.begin(), jsonString.end()); std::vector<char> compressedData = Utils::compressData(jsonVector); server->safeSend(clientSocket, 0x01, compressedData); membersID.erase(userID); if(membersID.empty()) { break; } } } } catch (const std::exception& ex) { Logger::getInstance().logError(std::string("General Error in updateMessagesInParticipantsGroup: ") + ex.what(), Logger::logLevel::Error); } }