Füge Unterstützung für Token in der Benutzeranmeldung im ChatRoom hinzu

- Implementiere eine neue Methode `addUser`, die einen Token als Parameter akzeptiert, um die Benutzeranmeldung über WebSockets zu verbessern.
- Aktualisiere den Konstruktor von `ChatUser`, um den Token zu verarbeiten und sicherzustellen, dass Benutzer korrekt initialisiert werden.
- Ergänze Debug-Ausgaben, um den Ablauf beim Hinzufügen von Benutzern und beim Senden von Nachrichten zu protokollieren, um die Nachverfolgbarkeit zu erhöhen.
This commit is contained in:
Torsten Schulz (local)
2025-09-05 14:37:28 +02:00
parent 189e3b342c
commit 97456d99e7
5 changed files with 156 additions and 5 deletions

View File

@@ -243,6 +243,74 @@ namespace Yc
return true; return true;
} }
bool ChatRoom::addUser(std::string _userName, std::string color, std::string _password, void* wsi, std::string token)
{
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 mit Token
newUser = std::make_shared<ChatUser>(shared_from_this(), _userName, color, wsi, _database, token);
} 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 with token): 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 with token): 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 with token): 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 with token): 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 with token): 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 with token): 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 with token): 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) bool ChatRoom::addUser(std::shared_ptr<ChatUser> user, std::string password)
{ {
if (password == _password) if (password == _password)

View File

@@ -43,6 +43,7 @@ namespace Yc
std::string name(); 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, int socket);
bool addUser(std::string userName, std::string color, std::string password, void* wsi); bool addUser(std::string userName, std::string color, std::string password, void* wsi);
bool addUser(std::string userName, std::string color, std::string password, void* wsi, std::string token);
bool addUser(std::shared_ptr<ChatUser> user, std::string password); bool addUser(std::shared_ptr<ChatUser> user, std::string password);
bool userNameExists(std::string userName); bool userNameExists(std::string userName);
void removeUser(std::string _token, bool silent = false); void removeUser(std::string _token, bool silent = false);

View File

@@ -42,7 +42,7 @@ namespace Yc
// Fallback für SSL-Server ohne Parent // Fallback für SSL-Server ohne Parent
_user = Yc::Object::User(Json::Value()); _user = Yc::Object::User(Json::Value());
_token = Yc::Lib::Tools::generateRandomString(32); _token = Yc::Lib::Tools::generateRandomString(32);
sendMsg(token, _token, _name, _color); sendMsg(ChatUser::token, _token, _name, _color);
return; return;
} }
auto server = _parent->getServer(); auto server = _parent->getServer();
@@ -50,7 +50,7 @@ namespace Yc
// Fallback wenn Server nicht verfügbar // Fallback wenn Server nicht verfügbar
_user = Yc::Object::User(Json::Value()); _user = Yc::Object::User(Json::Value());
_token = Yc::Lib::Tools::generateRandomString(32); _token = Yc::Lib::Tools::generateRandomString(32);
sendMsg(token, _token, _name, _color); sendMsg(ChatUser::token, _token, _name, _color);
return; return;
} }
auto db = server->_database; auto db = server->_database;
@@ -131,7 +131,7 @@ namespace Yc
// Fallback wenn keine Datenbank verfügbar // Fallback wenn keine Datenbank verfügbar
_user = Yc::Object::User(Json::Value()); _user = Yc::Object::User(Json::Value());
_token = Yc::Lib::Tools::generateRandomString(32); _token = Yc::Lib::Tools::generateRandomString(32);
sendMsg(token, _token, _name, _color); sendMsg(ChatUser::token, _token, _name, _color);
return; return;
} }
auto db = database; auto db = database;
@@ -279,6 +279,86 @@ namespace Yc
// Thread-Start erfolgt jetzt explizit per start(), nicht im Konstruktor // 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, std::string token)
: _parent(std::move(parent)),
_name(name),
_color(color),
_socket(-1),
_wsi(wsi),
_token(token),
_stop(false)
{
#ifdef YC_DEBUG
std::cout << "[Debug] ChatUser constructor with token: name=" << _name << ", token=" << _token << std::endl;
#endif
// Verwende die direkt übergebene Datenbank
if (!database) {
// Fallback wenn keine Datenbank verfügbar
_user = Yc::Object::User(Json::Value());
sendMsg(ChatUser::token, _token, _name, _color);
return;
}
auto db = database;
// Suche Community-User
std::string query = "SELECT * FROM community.\"user\" WHERE username = '" + name + "' LIMIT 1;";
auto result = db->exec(query);
Json::Value userJson;
if (result.empty()) {
// Kein Community-User, lege Dummy an
userJson["display_name"] = name;
userJson["color"] = "#000000";
} else {
const auto& row = result[0];
int falukant_user_id = row["id"].as<int>();
// Suche Chat-User
std::string chatUserQuery = "SELECT * FROM chat.\"user\" WHERE falukant_user_id = " + std::to_string(falukant_user_id) + " LIMIT 1;";
auto chatUserResult = db->exec(chatUserQuery);
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 (" +
std::to_string(falukant_user_id) + ", '" + name + "', '#000000', true, true, NOW(), NOW()) RETURNING *;";
auto newUser = db->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 = db->exec(rightsQuery);
Json::Value rights(Json::arrayValue);
for (const auto& r : rightsResult) {
rights.append(r["tr"].c_str());
}
userJson["rights"] = rights;
}
_user = Yc::Object::User(userJson);
// Prefer DB color if available
if (_user.id() != 0 && !_user.color().empty()) {
_color = _user.color();
}
// Beim Initial-Token direkt Name und aktuelle Farbe mitsenden, damit der Client "ich" korrekt färben kann
sendMsg(ChatUser::token, _token, _name, _color);
// Thread-Start erfolgt jetzt explizit per start(), nicht im Konstruktor
}
ChatUser::~ChatUser() ChatUser::~ChatUser()
{ {
// Hinweis: Thread wird nicht im Destruktor gejoint, um Deadlocks zu vermeiden! // Hinweis: Thread wird nicht im Destruktor gejoint, um Deadlocks zu vermeiden!

View File

@@ -37,6 +37,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);
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, 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::shared_ptr<ChatRoom> parent, std::string name, std::string color, void* wsi, std::shared_ptr<Database> database);
ChatUser(std::shared_ptr<ChatRoom> parent, std::string name, std::string color, void* wsi, std::shared_ptr<Database> database, std::string token);
~ChatUser(); ~ChatUser();
std::string name() const; std::string name() const;
std::string getToken() const; std::string getToken() const;

View File

@@ -1,6 +1,7 @@
#include "ssl_server.h" #include "ssl_server.h"
#include "config.h" #include "config.h"
#include "lib/database.h" #include "lib/database.h"
#include "lib/tools.h"
#include "chat_room.h" #include "chat_room.h"
#include "chat_user.h" #include "chat_user.h"
#include "lib/base.h" #include "lib/base.h"
@@ -265,7 +266,7 @@ void SSLServer::handleWebSocketMessage(struct lws *wsi, const std::string& messa
// Generate token if not provided // Generate token if not provided
if (token.empty()) { if (token.empty()) {
token = Base::generateToken(); token = Yc::Lib::Tools::generateRandomString(32);
} }
// Store user data in connections map instead // Store user data in connections map instead
@@ -300,7 +301,7 @@ void SSLServer::handleWebSocketMessage(struct lws *wsi, const std::string& messa
std::cout << "[Debug] Attempting to add user '" << name << "' to room '" << room << "' with WebSocket wsi pointer" << std::endl; std::cout << "[Debug] Attempting to add user '" << name << "' to room '" << room << "' with WebSocket wsi pointer" << std::endl;
#endif #endif
if (roomObj->addUser(name, color, password, wsi)) { if (roomObj->addUser(name, color, password, wsi, token)) {
std::cout << "[YourChat] Successfully added user '" << name << "' to room '" << room << "'" << std::endl; std::cout << "[YourChat] Successfully added user '" << name << "' to room '" << room << "'" << std::endl;
// Find the created ChatUser // Find the created ChatUser
auto chatUser = roomObj->findUserByName(name); auto chatUser = roomObj->findUserByName(name);