135 lines
4.8 KiB
C++
135 lines
4.8 KiB
C++
|
|
#include <drogon/drogon.h>
|
||
|
|
#include <iostream>
|
||
|
|
#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;
|
||
|
|
}
|