This commit is contained in:
Torsten (PC)
2026-01-14 14:36:57 +01:00
parent cd739fb52e
commit 1fe77c0905
21 changed files with 1267 additions and 0 deletions

149
src/websocket_server.cpp Normal file
View File

@@ -0,0 +1,149 @@
#include "websocket_server.h"
#include "connection_guard.h"
#include <iostream>
#include <nlohmann/json.hpp>
#include <thread>
#include <chrono>
#include <queue>
#include <condition_variable>
using json = nlohmann::json;
WebSocketServer::WebSocketServer(int port, ConnectionPool &pool, MessageBroker &broker)
: port(port), pool(pool), broker(broker) {}
void WebSocketServer::run() {
running = true;
broker.subscribe([this](const std::string &message) {
std::lock_guard<std::mutex> lock(queueMutex);
messageQueue.push(message);
queueCV.notify_one();
});
serverThread = std::thread([this]() { startServer(); });
messageProcessingThread = std::thread([this]() { processMessageQueue(); });
pingThread = std::thread([this]() { pingClients(); });
}
void WebSocketServer::stop() {
running = false;
if (serverThread.joinable()) serverThread.join();
if (messageProcessingThread.joinable()) messageProcessingThread.join();
if (pingThread.joinable()) pingThread.join();
}
void WebSocketServer::startServer() {
uWS::App()
.ws<WebSocketUserData>("/*", {
.open = [this](uWS::WebSocket<false, true, WebSocketUserData> *ws) {
ws->getUserData()->pongReceived = true;
},
.message = [this](uWS::WebSocket<false, true, WebSocketUserData> *ws, std::string_view message, uWS::OpCode opCode) {
handleWebSocketMessage(ws, message, opCode);
},
.close = [this](uWS::WebSocket<false, true, WebSocketUserData> *ws, int /*code*/, std::string_view /*message*/) {
handleWebSocketClose(ws);
}
})
.listen(port, [this](auto *token) {
if (token) {
std::cout << "WebSocket-Server läuft auf Port " << port << std::endl;
} else {
std::cerr << "WebSocket-Server konnte nicht gestartet werden!\n";
running = false;
}
})
.run();
}
void WebSocketServer::pingClients() {
while (running) {
std::this_thread::sleep_for(std::chrono::seconds(30));
std::unique_lock lock(connectionsMutex);
for (auto &[userId, ws] : connections) {
if (!ws->getUserData()->pongReceived) {
ws->close();
} else {
ws->getUserData()->pongReceived = false;
ws->send("ping", uWS::OpCode::TEXT);
}
}
}
}
void WebSocketServer::processMessageQueue() {
while (running) {
std::unique_lock lock(queueMutex);
queueCV.wait(lock, [this]() { return !messageQueue.empty() || !running; });
while (!messageQueue.empty()) {
std::string message = std::move(messageQueue.front());
messageQueue.pop();
lock.unlock();
handleBrokerMessage(message);
lock.lock();
}
}
}
void WebSocketServer::handleBrokerMessage(const std::string &message) {
try {
json parsedMessage = json::parse(message);
if (parsedMessage.contains("user_id")) {
int falukantUserId = parsedMessage["user_id"];
std::shared_lock lock(connectionsMutex);
auto userId = getUserIdFromFalukantUserId(falukantUserId);
auto it = connections.find(userId);
if (it != connections.end()) {
it->second->send(message, uWS::OpCode::TEXT);
std::cout << "[WebSocketServer] Nachricht an User-ID: " << userId << " gesendet.\n";
} else {
std::cerr << "[WebSocketServer] Keine Verbindung für User-ID: " << userId << "\n";
}
} else {
std::cerr << "[WebSocketServer] Ungültige Nachricht: " << message << "\n";
}
} catch (const std::exception &e) {
std::cerr << "[WebSocketServer] Fehler beim Verarbeiten der Nachricht: " << e.what() << "\n";
}
}
void WebSocketServer::handleWebSocketMessage(uWS::WebSocket<false, true, WebSocketUserData> *ws, std::string_view message, uWS::OpCode opCode) {
if (message == "pong") {
ws->getUserData()->pongReceived = true;
return;
}
json parsedMessage = json::parse(message);
if (parsedMessage.contains("event") && parsedMessage["event"] == "setUserId") {
std::string userId = parsedMessage["data"]["userId"];
std::unique_lock lock(connectionsMutex);
connections[userId] = ws;
ws->getUserData()->userId = userId;
}
}
void WebSocketServer::handleWebSocketClose(uWS::WebSocket<false, true, WebSocketUserData> *ws) {
std::unique_lock lock(connectionsMutex);
auto userId = ws->getUserData()->userId;
if (!userId.empty()) {
connections.erase(userId);
}
}
std::string WebSocketServer::getUserIdFromFalukantUserId(int & userId) {
ConnectionGuard guard(pool);
auto &db = guard.get();
std::string query = R"(
SELECT u.hashed_id
FROM community.user u
JOIN falukant_data.falukant_user fu ON u.id = fu.user_id
WHERE fu.id = $1
)";
db.prepare("get_user_id", query);
auto users = db.execute("get_user_id", {std::to_string(userId)});
if (!users.empty()) {
return users[0]["hashed_id"];
} else {
return "";
}
}