#include #include #include "services/RedisMessageStore.h" #include "services/AuthService.h" #include "services/ChatService.h" #include "services/StickerService.h" #include "services/CensorService.h" #include "controllers/ChatController.h" #include "controllers/ChatWebSocketController.h" #include "controllers/ModerationController.h" #include "controllers/ChatAdminController.h" #include "controllers/WatchSyncController.h" using namespace drogon; int main() { // Load configuration app().loadConfigFile("config.json"); LOG_INFO << "Starting Chat Service..."; // Get configuration values auto config = app().getCustomConfig(); auto redisConfig = config.get("redis", Json::Value::null); auto jwtConfig = config.get("jwt", Json::Value::null); // Initialize Redis auto& redis = services::RedisMessageStore::getInstance(); // Get Redis host from environment or config std::string redisHost = redisConfig.get("host", "localhost").asString(); const char* envRedisHost = std::getenv("REDIS_HOST"); if (envRedisHost) { redisHost = envRedisHost; } // Get Redis password from environment std::string redisPass = ""; const char* envRedisPass = std::getenv("REDIS_PASS"); if (envRedisPass && strlen(envRedisPass) > 0) { redisPass = envRedisPass; } redis.initialize( redisHost, redisConfig.get("port", 6379).asInt(), redisConfig.get("db", 1).asInt(), redisPass ); // Initialize Auth Service std::string jwtSecret = jwtConfig.get("secret", "").asString(); if (jwtSecret.empty()) { // Try environment variable const char* envSecret = std::getenv("JWT_SECRET"); if (envSecret) { jwtSecret = envSecret; } else { LOG_ERROR << "JWT_SECRET not configured!"; return 1; } } auto& authService = services::AuthService::getInstance(); authService.initialize(jwtSecret); // Initialize Chat Service auto& chatService = services::ChatService::getInstance(); chatService.initialize(); // Initialize Sticker Service (for :roll: and :rtd: processing) auto& stickerService = services::StickerService::getInstance(); stickerService.initialize(); // Initialize Censor Service (for word filtering) auto& censorService = services::CensorService::getInstance(); censorService.initialize(); LOG_INFO << "Chat initialization complete"; // Set CORS app().registerPostHandlingAdvice([](const HttpRequestPtr& req, const HttpResponsePtr& resp) { resp->addHeader("Access-Control-Allow-Origin", "*"); resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); resp->addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); resp->addHeader("Access-Control-Max-Age", "3600"); }); // Handle OPTIONS requests app().registerPreHandlingAdvice([](const HttpRequestPtr& req, AdviceCallback&& acb, AdviceChainCallback&& accb) { if (req->method() == Options) { auto resp = HttpResponse::newHttpResponse(); resp->setStatusCode(k200OK); resp->addHeader("Access-Control-Allow-Origin", "*"); resp->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); resp->addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); resp->addHeader("Access-Control-Max-Age", "3600"); acb(resp); return; } accb(); }); // Ensure WebSocket controllers are loaded ChatWebSocketController::ensureLoaded(); WatchSyncController::ensureLoaded(); // Explicitly register WebSocket paths (workaround for custom CMake builds) app().registerWebSocketController("/chat/ws", "ChatWebSocketController"); app().registerWebSocketController("/chat/stream/{1}", "ChatWebSocketController"); app().registerWebSocketController("/watch/ws", "WatchSyncController"); LOG_INFO << "WebSocket paths explicitly registered"; LOG_INFO << "Chat Service initialized successfully"; LOG_INFO << "WebSocket endpoint: ws://localhost:8081/chat/stream/{realmId}"; LOG_INFO << "REST API: http://localhost:8081/api/chat/*"; // Register guest session timeout checker (runs every 5 minutes) app().getLoop()->runEvery(300.0, []() { ChatWebSocketController::checkGuestTimeouts(); }); LOG_INFO << "Guest session timeout checker registered (45-123 minute random timeout)"; // Schedule sticker fetch (must be done here, after event loop is set up) stickerService.scheduleFetch(); // Schedule censored words fetch (must be done here, after event loop is set up) censorService.scheduleFetch(); // Run the application app().run(); return 0; }