beeta/backend/src/services/DatabaseService.cpp
2026-01-05 22:54:27 -05:00

123 lines
No EOL
5.1 KiB
C++

#include "DatabaseService.h"
#include "../services/RedisHelper.h"
#include <drogon/orm/DbClient.h>
#include <sstream>
#include <iomanip>
#include <openssl/rand.h>
using namespace drogon;
using namespace drogon::orm;
namespace {
void storeKeyInRedis(const std::string& streamKey) {
// Store the stream key in Redis for validation (24 hour TTL)
bool stored = RedisHelper::storeKey("stream_key:" + streamKey, "1", 86400);
if (stored) {
LOG_INFO << "Stored stream key in Redis: " << streamKey;
} else {
LOG_ERROR << "Failed to store key in Redis: " << streamKey;
}
}
// SECURITY FIX: Use cryptographically secure random bytes instead of mt19937
std::string generateStreamKey() {
unsigned char bytes[32]; // 32 bytes = 64 hex characters
if (RAND_bytes(bytes, sizeof(bytes)) != 1) {
LOG_ERROR << "Failed to generate cryptographically secure random bytes";
throw std::runtime_error("Failed to generate secure stream key");
}
std::stringstream ss;
for (size_t i = 0; i < sizeof(bytes); ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(bytes[i]);
}
return ss.str();
}
}
void DatabaseService::initialize() {
LOG_INFO << "Initializing Database Service...";
}
void DatabaseService::getUserStreamKey(int64_t userId,
std::function<void(bool, const std::string&)> callback) {
auto dbClient = drogon::app().getDbClient();
*dbClient << "SELECT key FROM stream_keys WHERE user_id = $1 AND is_active = true"
<< userId
>> [callback, userId, dbClient](const Result &r) {
if (!r.empty()) {
std::string key = r[0]["key"].as<std::string>();
// Also store in Redis when retrieved
storeKeyInRedis(key);
callback(true, key);
} else {
// Generate new key for user
std::string newKey = generateStreamKey();
*dbClient << "INSERT INTO stream_keys (user_id, key, is_active) VALUES ($1, $2, true)"
<< userId << newKey
>> [callback, newKey](const Result &) {
storeKeyInRedis(newKey);
callback(true, newKey);
}
>> [callback](const DrogonDbException &e) {
LOG_ERROR << "Failed to create stream key: " << e.base().what();
callback(false, "");
};
}
}
>> [callback](const DrogonDbException &e) {
LOG_ERROR << "Database error: " << e.base().what();
callback(false, "");
};
}
void DatabaseService::updateUserStreamKey(int64_t userId,
const std::string& newKey,
std::function<void(bool)> callback) {
auto dbClient = drogon::app().getDbClient();
// Execute as separate queries instead of transaction for simplicity
*dbClient << "UPDATE stream_keys SET is_active = false WHERE user_id = $1"
<< userId
>> [dbClient, userId, newKey, callback](const Result &) {
// Insert new key
*dbClient << "INSERT INTO stream_keys (user_id, key, is_active) VALUES ($1, $2, true)"
<< userId << newKey
>> [callback, newKey](const Result &) {
// Store new key in Redis
storeKeyInRedis(newKey);
callback(true);
}
>> [callback](const DrogonDbException &e) {
LOG_ERROR << "Failed to insert new key: " << e.base().what();
callback(false);
};
}
>> [callback](const DrogonDbException &e) {
LOG_ERROR << "Failed to deactivate old keys: " << e.base().what();
callback(false);
};
}
void DatabaseService::validateStreamKey(const std::string& key,
std::function<void(bool)> callback) {
auto dbClient = drogon::app().getDbClient();
*dbClient << "SELECT 1 FROM stream_keys WHERE key = $1 AND is_active = true"
<< key
>> [callback, key](const Result &r) {
bool valid = !r.empty();
if (valid) {
// Also store in Redis when validated
storeKeyInRedis(key);
}
callback(valid);
}
>> [callback](const DrogonDbException &e) {
LOG_ERROR << "Database error: " << e.base().what();
callback(false);
};
}