131 lines
5.6 KiB
C++
131 lines
5.6 KiB
C++
#pragma once
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <optional>
|
|
#include <sw/redis++/redis++.h>
|
|
#include "../models/ChatMessage.h"
|
|
|
|
namespace services {
|
|
|
|
class RedisMessageStore {
|
|
public:
|
|
static RedisMessageStore& getInstance();
|
|
|
|
void initialize(const std::string& host, int port, int db = 1, const std::string& password = "");
|
|
bool isInitialized() const { return redis_ != nullptr; }
|
|
|
|
// Track active realms for cleanup
|
|
void trackActiveRealm(const std::string& realmId);
|
|
std::vector<std::string> getActiveRealms();
|
|
|
|
// Message operations
|
|
bool addMessage(const models::ChatMessage& message);
|
|
std::vector<models::ChatMessage> getMessages(const std::string& realmId,
|
|
int limit = 100,
|
|
int64_t beforeTimestamp = 0);
|
|
// Get messages from ALL active realms (for global chat history)
|
|
std::vector<models::ChatMessage> getGlobalMessages(int limit = 100);
|
|
bool deleteMessage(const std::string& realmId, const std::string& messageId);
|
|
void cleanupOldMessages(const std::string& realmId, int retentionHours);
|
|
void cleanupExpiredSelfDestruct(const std::string& realmId);
|
|
|
|
// Settings operations
|
|
void setGlobalSettings(const models::GlobalChatSettings& settings);
|
|
models::GlobalChatSettings getGlobalSettings();
|
|
void setRealmSettings(const std::string& realmId, const models::ChatSettings& settings);
|
|
models::ChatSettings getRealmSettings(const std::string& realmId);
|
|
|
|
// Moderation operations (per-realm)
|
|
bool addBan(const std::string& realmId, const std::string& userId);
|
|
bool removeBan(const std::string& realmId, const std::string& userId);
|
|
bool isBanned(const std::string& realmId, const std::string& userId);
|
|
std::vector<std::string> getBannedUsers(const std::string& realmId);
|
|
|
|
// durationSeconds: 0 = permanent, >0 = temporary with TTL
|
|
bool addMute(const std::string& realmId, const std::string& userId, int durationSeconds = 0);
|
|
bool removeMute(const std::string& realmId, const std::string& userId);
|
|
bool isMuted(const std::string& realmId, const std::string& userId);
|
|
|
|
// Fingerprint bans - site-wide "uberban" (for guests and registered users)
|
|
bool addFingerprintBan(const std::string& fingerprint);
|
|
bool removeFingerprintBan(const std::string& fingerprint);
|
|
bool isFingerprintBanned(const std::string& fingerprint);
|
|
std::vector<std::string> getBannedFingerprints();
|
|
|
|
// Realm-specific bans (supports both user IDs and fingerprints)
|
|
// identifier format: "user:{userId}" or "fp:{fingerprint}"
|
|
bool addRealmBan(const std::string& realmId, const std::string& identifier);
|
|
bool removeRealmBan(const std::string& realmId, const std::string& identifier);
|
|
bool isRealmBanned(const std::string& realmId, const std::string& userId, const std::string& fingerprint = "");
|
|
std::vector<std::string> getRealmBannedIdentifiers(const std::string& realmId);
|
|
|
|
// Kicks (temporary disconnect + rejoin block with TTL)
|
|
bool addKick(const std::string& realmId, const std::string& userId, int durationSeconds = 60);
|
|
bool isKicked(const std::string& realmId, const std::string& userId);
|
|
|
|
// Slow mode
|
|
bool canSendMessage(const std::string& realmId, const std::string& userId, int slowModeSeconds);
|
|
void recordMessageSent(const std::string& realmId, const std::string& userId);
|
|
|
|
// Guest ID generation
|
|
std::string generateGuestId(const std::string& pattern);
|
|
|
|
// SECURITY FIX #31: Server-side guest name persistence (by fingerprint)
|
|
// Stores guest's chosen name server-side so it persists across sessions
|
|
// More secure than localStorage which is accessible to same-origin XSS
|
|
bool setGuestName(const std::string& fingerprint, const std::string& name);
|
|
std::optional<std::string> getGuestName(const std::string& fingerprint);
|
|
bool clearGuestName(const std::string& fingerprint);
|
|
|
|
// Active users
|
|
void recordUserActivity(const std::string& realmId, const std::string& userId);
|
|
int getActiveUserCount(const std::string& realmId);
|
|
|
|
// Pending uberban check (set by backend or chat-service, checked on connect)
|
|
bool setPendingUberban(const std::string& userId);
|
|
bool hasPendingUberban(const std::string& userId);
|
|
bool clearPendingUberban(const std::string& userId);
|
|
|
|
private:
|
|
RedisMessageStore() = default;
|
|
~RedisMessageStore() = default;
|
|
RedisMessageStore(const RedisMessageStore&) = delete;
|
|
RedisMessageStore& operator=(const RedisMessageStore&) = delete;
|
|
|
|
std::unique_ptr<sw::redis::Redis> redis_;
|
|
|
|
std::string getMessagesKey(const std::string& realmId) const {
|
|
return "chat:messages:" + realmId;
|
|
}
|
|
|
|
std::string getBanKey(const std::string& realmId) const {
|
|
return "chat:banned:" + realmId;
|
|
}
|
|
|
|
std::string getMuteKey(const std::string& realmId, const std::string& userId) const {
|
|
return "chat:muted:" + realmId + ":" + userId;
|
|
}
|
|
|
|
std::string getSlowModeKey(const std::string& realmId, const std::string& userId) const {
|
|
return "chat:slowmode:" + realmId + ":" + userId;
|
|
}
|
|
|
|
std::string getActiveUsersKey(const std::string& realmId) const {
|
|
return "chat:active:" + realmId;
|
|
}
|
|
|
|
std::string getRealmBanKey(const std::string& realmId) const {
|
|
return "chat:realm_banned:" + realmId;
|
|
}
|
|
|
|
std::string getKickKey(const std::string& realmId, const std::string& userId) const {
|
|
return "chat:kicked:" + realmId + ":" + userId;
|
|
}
|
|
|
|
std::string getMessageIndexKey(const std::string& realmId) const {
|
|
return "chat:msg_index:" + realmId;
|
|
}
|
|
};
|
|
|
|
} // namespace services
|