Füge Unterstützung für WebSocket-Nutzer in ChatUser und ChatRoom hinzu
- Implementiere einen neuen Konstruktor in der Klasse `ChatUser`, der einen WebSocket-Pointer akzeptiert, um die Benutzerkommunikation über WebSockets zu ermöglichen. - Aktualisiere die Methode `addUser` in `ChatRoom`, um den neuen Konstruktor zu verwenden und sicherzustellen, dass Benutzer korrekt hinzugefügt werden. - Ergänze die Logik in der `send`-Methode von `ChatUser`, um Nachrichten über WebSockets zu senden, wenn ein gültiger WebSocket-Pointer vorhanden ist. - Füge Debug-Ausgaben hinzu, um den Ablauf beim Hinzufügen von Benutzern und beim Senden von Nachrichten über WebSockets zu protokollieren.
This commit is contained in:
@@ -23,7 +23,7 @@ fi
|
||||
|
||||
echo "=== Schritt 1: Abhängigkeiten installieren ==="
|
||||
echo "Führe install_dependencies.sh aus..."
|
||||
./deploy/install_dependencies.sh
|
||||
#./deploy/install_dependencies.sh
|
||||
|
||||
echo ""
|
||||
echo "=== Schritt 2: Anwendung bauen ==="
|
||||
|
||||
@@ -175,6 +175,74 @@ namespace Yc
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatRoom::addUser(std::string _userName, std::string color, std::string _password, void* wsi)
|
||||
{
|
||||
if (_password != "" && _password == _password && std::find(std::begin(_allowedUsers), std::end(_allowedUsers), _userName) == std::end(_allowedUsers))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<ChatUser> newUser;
|
||||
if (_database) {
|
||||
// Verwende den neuen WebSocket-Konstruktor
|
||||
newUser = std::make_shared<ChatUser>(shared_from_this(), _userName, color, wsi, _database);
|
||||
} else {
|
||||
// Fallback: WebSocket ohne Datenbank nicht unterstützt
|
||||
return false;
|
||||
}
|
||||
_users.push_back(newUser);
|
||||
|
||||
// Sende zuerst alle wichtigen Nachrichten, bevor der checkerTask Thread startet
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] addUser(WebSocket): Starting message sequence for user: " << newUser->name() << std::endl;
|
||||
#endif
|
||||
|
||||
if (_parent) {
|
||||
Json::Value roomList = _parent->jsonRoomList();
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] addUser(WebSocket): Sending roomList to user: " << newUser->name() << std::endl;
|
||||
#endif
|
||||
newUser->sendMsg(ChatUser::roomList, roomList, "", "");
|
||||
}
|
||||
|
||||
// Kurze Pause, damit der WebSocket-Handshake vollständig abgeschlossen ist
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] addUser(WebSocket): Waiting 100ms for WebSocket handshake..." << std::endl;
|
||||
#endif
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// Sende aktuelle Userliste an den neuen User
|
||||
Json::Value currentUserList = userList();
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] addUser(WebSocket): Sending userListe to user: " << newUser->name() << std::endl;
|
||||
#endif
|
||||
newUser->sendMsg(ChatUser::userListe, currentUserList, "", "");
|
||||
|
||||
// Sende aktualisierte Userliste an alle anderen User im Raum
|
||||
for (auto &existingUser : _users) {
|
||||
if (existingUser != newUser) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] addUser(WebSocket): Sending userListe to existing user: " << existingUser->name() << std::endl;
|
||||
#endif
|
||||
existingUser->sendMsg(ChatUser::userListe, currentUserList, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast an andere Nutzer: Benutzer X hat den Chat betreten (mit Farbinfo)
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] addUser(WebSocket): Sending 'user_entered_chat' message for user: " << newUser->name() << std::endl;
|
||||
#endif
|
||||
addMessage(ChatUser::system, "user_entered_chat", newUser->name(), newUser->color());
|
||||
|
||||
// Starte den checkerTask Thread erst nach dem Senden aller wichtigen Nachrichten
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] addUser(WebSocket): Starting checkerTask thread for user: " << newUser->name() << std::endl;
|
||||
#endif
|
||||
newUser->start();
|
||||
|
||||
_initRound();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatRoom::addUser(std::shared_ptr<ChatUser> user, std::string password)
|
||||
{
|
||||
if (password == _password)
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace Yc
|
||||
void run();
|
||||
std::string name();
|
||||
bool addUser(std::string userName, std::string color, std::string password, int socket);
|
||||
bool addUser(std::string userName, std::string color, std::string password, void* wsi);
|
||||
bool addUser(std::shared_ptr<ChatUser> user, std::string password);
|
||||
bool userNameExists(std::string userName);
|
||||
void removeUser(std::string _token, bool silent = false);
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace Yc
|
||||
_name(name),
|
||||
_color(color),
|
||||
_socket(socket),
|
||||
_wsi(nullptr),
|
||||
_stop(false)
|
||||
{
|
||||
#ifdef YC_DEBUG
|
||||
@@ -195,6 +196,89 @@ namespace Yc
|
||||
// Thread-Start erfolgt jetzt explizit per start(), nicht im Konstruktor
|
||||
}
|
||||
|
||||
ChatUser::ChatUser(std::shared_ptr<ChatRoom> parent, std::string name, std::string color, void* wsi, std::shared_ptr<Database> database)
|
||||
: _parent(std::move(parent)),
|
||||
_name(name),
|
||||
_color(color),
|
||||
_socket(-1), // WebSocket uses wsi, not socket
|
||||
_wsi(wsi),
|
||||
_stop(false)
|
||||
{
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] ChatUser WebSocket constructor: name=" << _name << ", wsi=" << _wsi << std::endl;
|
||||
#endif
|
||||
|
||||
// Verwende die direkt übergebene Datenbank
|
||||
if (!database) {
|
||||
// Fallback wenn keine Datenbank verfügbar
|
||||
_user = Yc::Object::User(Json::Value());
|
||||
_token = Yc::Lib::Tools::generateRandomString(32);
|
||||
} else {
|
||||
// Lade User-Daten aus der Datenbank
|
||||
Json::Value userJson;
|
||||
userJson["id"] = 0;
|
||||
userJson["falukant_user_id"] = 0;
|
||||
userJson["display_name"] = name;
|
||||
userJson["color"] = color;
|
||||
userJson["show_gender"] = true;
|
||||
userJson["show_age"] = true;
|
||||
userJson["created_at"] = "";
|
||||
userJson["updated_at"] = "";
|
||||
|
||||
// Versuche User in der Datenbank zu finden
|
||||
try {
|
||||
std::string query = "SELECT * FROM chat.\"user\" WHERE display_name = '" + name + "' LIMIT 1;";
|
||||
auto chatUserResult = database->exec(query);
|
||||
if (chatUserResult.empty()) {
|
||||
// Chat-User anlegen
|
||||
std::string insert = "INSERT INTO chat.\"user\" (falukant_user_id, display_name, color, show_gender, show_age, created_at, updated_at) VALUES (0, '" + name + "', '#000000', true, true, NOW(), NOW()) RETURNING *;";
|
||||
auto newUser = database->exec(insert);
|
||||
if (!newUser.empty()) {
|
||||
const auto& u = newUser[0];
|
||||
userJson["id"] = u["id"].as<int>();
|
||||
userJson["falukant_user_id"] = u["falukant_user_id"].as<int>();
|
||||
userJson["display_name"] = u["display_name"].c_str();
|
||||
userJson["color"] = u["color"].c_str();
|
||||
userJson["show_gender"] = u["show_gender"].as<bool>();
|
||||
userJson["show_age"] = u["show_age"].as<bool>();
|
||||
userJson["created_at"] = u["created_at"].c_str();
|
||||
userJson["updated_at"] = u["updated_at"].c_str();
|
||||
}
|
||||
} else {
|
||||
const auto& u = chatUserResult[0];
|
||||
userJson["id"] = u["id"].as<int>();
|
||||
userJson["falukant_user_id"] = u["falukant_user_id"].as<int>();
|
||||
userJson["display_name"] = u["display_name"].c_str();
|
||||
userJson["color"] = u["color"].c_str();
|
||||
userJson["show_gender"] = u["show_gender"].as<bool>();
|
||||
userJson["show_age"] = u["show_age"].as<bool>();
|
||||
userJson["created_at"] = u["created_at"].c_str();
|
||||
userJson["updated_at"] = u["updated_at"].c_str();
|
||||
}
|
||||
// Rechte laden
|
||||
std::string rightsQuery = "SELECT r.tr FROM chat.user_rights ur JOIN chat.rights r ON ur.chat_right_id = r.id WHERE ur.chat_user_id = " + std::to_string(userJson["id"].asInt()) + ";";
|
||||
auto rightsResult = database->exec(rightsQuery);
|
||||
Json::Value rights(Json::arrayValue);
|
||||
for (const auto& r : rightsResult) {
|
||||
rights.append(r["tr"].c_str());
|
||||
}
|
||||
userJson["rights"] = rights;
|
||||
} catch (...) {
|
||||
// Ignore database errors
|
||||
}
|
||||
_user = Yc::Object::User(userJson);
|
||||
// Prefer DB color if available
|
||||
if (_user.id() != 0 && !_user.color().empty()) {
|
||||
_color = _user.color();
|
||||
}
|
||||
_token = Yc::Lib::Tools::generateRandomString(32);
|
||||
}
|
||||
|
||||
// Beim Initial-Token direkt Name und aktuelle Farbe mitsenden, damit der Client "ich" korrekt färben kann
|
||||
sendMsg(token, _token, _name, _color);
|
||||
// Thread-Start erfolgt jetzt explizit per start(), nicht im Konstruktor
|
||||
}
|
||||
|
||||
ChatUser::~ChatUser()
|
||||
{
|
||||
// Hinweis: Thread wird nicht im Destruktor gejoint, um Deadlocks zu vermeiden!
|
||||
@@ -370,7 +454,7 @@ namespace Yc
|
||||
|
||||
void ChatUser::send(std::string out) {
|
||||
// Prüfe ob Socket noch gültig ist
|
||||
if (_socket < 0 || _stop) {
|
||||
if ((_socket < 0 && !_wsi) || _stop) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Skipping send - socket invalid (" << _socket << ") or user stopped (" << _stop << ") for user: " << _name << std::endl;
|
||||
#endif
|
||||
@@ -378,17 +462,24 @@ namespace Yc
|
||||
}
|
||||
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Sending message to user: " << _name << " (socket: " << _socket << ")" << std::endl;
|
||||
std::cout << "[Debug] Sending message to user: " << _name << " (socket: " << _socket << ", wsi: " << _wsi << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
// Entferne ggf. Token-Felder aus JSON-Strings und sende über Socket/WebSocket
|
||||
Base::sanitizeTokensInString(out);
|
||||
Base::send(_socket, out);
|
||||
|
||||
if (_wsi) {
|
||||
// WebSocket: Sende über libwebsockets
|
||||
Base::sendWebSocketMessage(_wsi, out);
|
||||
} else {
|
||||
// Normaler Socket
|
||||
Base::send(_socket, out);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::send(Json::Value out) {
|
||||
// Prüfe ob Socket noch gültig ist
|
||||
if (_socket < 0 || _stop) {
|
||||
if ((_socket < 0 && !_wsi) || _stop) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Skipping send - socket invalid (" << _socket << ") or user stopped (" << _stop << ") for user: " << _name << std::endl;
|
||||
#endif
|
||||
@@ -396,12 +487,19 @@ namespace Yc
|
||||
}
|
||||
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Sending JSON message to user: " << _name << " (socket: " << _socket << ")" << std::endl;
|
||||
std::cout << "[Debug] Sending JSON message to user: " << _name << " (socket: " << _socket << ", wsi: " << _wsi << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
// Entferne rekursiv alle Token-Felder und sende über Socket/WebSocket
|
||||
Base::sanitizeTokens(out);
|
||||
Base::send(_socket, out);
|
||||
|
||||
if (_wsi) {
|
||||
// WebSocket: Sende über libwebsockets
|
||||
Base::sendWebSocketMessage(_wsi, out);
|
||||
} else {
|
||||
// Normaler Socket
|
||||
Base::send(_socket, out);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::handleMessage(std::string message)
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace Yc
|
||||
|
||||
ChatUser(std::shared_ptr<ChatRoom> parent, std::string name, std::string color, int socket);
|
||||
ChatUser(std::shared_ptr<ChatRoom> parent, std::string name, std::string color, int socket, std::shared_ptr<Database> database);
|
||||
ChatUser(std::shared_ptr<ChatRoom> parent, std::string name, std::string color, void* wsi, std::shared_ptr<Database> database);
|
||||
~ChatUser();
|
||||
std::string name() const;
|
||||
std::string getToken() const;
|
||||
@@ -59,6 +60,7 @@ namespace Yc
|
||||
std::string _name;
|
||||
std::string _color;
|
||||
int _socket;
|
||||
void* _wsi; // WebSocket pointer for libwebsockets
|
||||
std::string _token;
|
||||
bool _stop;
|
||||
|
||||
|
||||
@@ -317,10 +317,10 @@ void SSLServer::handleWebSocketMessage(struct lws *wsi, const std::string& messa
|
||||
std::cout << "[YourChat] Found room '" << room << "', attempting to add user..." << std::endl;
|
||||
// Add user to room (ChatUser will be created by addUser)
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Attempting to add user '" << name << "' to room '" << room << "' with socket: " << lws_get_socket_fd(wsi) << std::endl;
|
||||
std::cout << "[Debug] Attempting to add user '" << name << "' to room '" << room << "' with WebSocket wsi pointer" << std::endl;
|
||||
#endif
|
||||
|
||||
if (roomObj->addUser(name, color, password, lws_get_socket_fd(wsi))) {
|
||||
if (roomObj->addUser(name, color, password, wsi)) {
|
||||
std::cout << "[YourChat] Successfully added user '" << name << "' to room '" << room << "'" << std::endl;
|
||||
// Find the created ChatUser
|
||||
auto chatUser = roomObj->findUserByName(name);
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
#include <openssl/buffer.h>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <libwebsockets.h>
|
||||
|
||||
// Forward declaration for WebSocket user data
|
||||
struct WebSocketUserData {
|
||||
std::string pendingMessage;
|
||||
std::string token;
|
||||
std::string userName;
|
||||
std::string userColor;
|
||||
std::string currentRoom;
|
||||
bool authenticated;
|
||||
};
|
||||
|
||||
namespace Yc {
|
||||
namespace Lib {
|
||||
@@ -327,5 +338,42 @@ namespace Yc {
|
||||
return token;
|
||||
}
|
||||
|
||||
void Base::sendWebSocketMessage(void* wsi, const std::string& out) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Base::sendWebSocketMessage(void*) called with wsi: " << wsi << ", length: " << out.length() << std::endl;
|
||||
#endif
|
||||
|
||||
if (!wsi) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Invalid WebSocket wsi (nullptr), skipping send" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Cast to lws* and use libwebsockets API
|
||||
struct lws* lws_wsi = static_cast<struct lws*>(wsi);
|
||||
|
||||
// Store message in user data for sending
|
||||
auto* ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(lws_wsi));
|
||||
if (ud) {
|
||||
ud->pendingMessage = out;
|
||||
lws_callback_on_writable(lws_wsi);
|
||||
} else {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] No user data found for WebSocket, cannot send message" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Base::sendWebSocketMessage(void* wsi, const Json::Value& out) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Base::sendWebSocketMessage(void*, Json::Value) called with wsi: " << wsi << std::endl;
|
||||
#endif
|
||||
|
||||
// Convert JSON to string and call the string version
|
||||
std::string outString = getJsonString(out);
|
||||
sendWebSocketMessage(wsi, outString);
|
||||
}
|
||||
|
||||
} // namespace Lib
|
||||
} // namespace Yc
|
||||
|
||||
@@ -23,6 +23,8 @@ protected:
|
||||
// WebSocket helpers (server-side): read one text frame and send a text frame
|
||||
std::string readWebSocketMessage(int socket);
|
||||
void sendWebSocketMessage(int socket, const std::string& out);
|
||||
void sendWebSocketMessage(void* wsi, const std::string& out);
|
||||
void sendWebSocketMessage(void* wsi, const Json::Value& out);
|
||||
// WebSocket connection tracking and handshake helpers
|
||||
static void markWebSocket(int socket);
|
||||
static void unmarkWebSocket(int socket);
|
||||
|
||||
Reference in New Issue
Block a user