181 lines
7.2 KiB
C++
181 lines
7.2 KiB
C++
|
|
#include "AdminController.h"
|
||
|
|
#include "../services/OmeClient.h"
|
||
|
|
#include "../services/RedisHelper.h"
|
||
|
|
|
||
|
|
using namespace drogon::orm;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
HttpResponsePtr jsonResp(const Json::Value& j, HttpStatusCode c = k200OK) {
|
||
|
|
auto r = HttpResponse::newHttpJsonResponse(j);
|
||
|
|
r->setStatusCode(c);
|
||
|
|
return r;
|
||
|
|
}
|
||
|
|
|
||
|
|
HttpResponsePtr jsonError(const std::string& error, HttpStatusCode code = k400BadRequest) {
|
||
|
|
Json::Value j;
|
||
|
|
j["success"] = false;
|
||
|
|
j["error"] = error;
|
||
|
|
return jsonResp(j, code);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
UserInfo AdminController::getUserFromRequest(const HttpRequestPtr &req) {
|
||
|
|
UserInfo user;
|
||
|
|
std::string auth = req->getHeader("Authorization");
|
||
|
|
|
||
|
|
if (auth.empty() || auth.substr(0, 7) != "Bearer ") {
|
||
|
|
return user;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string token = auth.substr(7);
|
||
|
|
AuthService::getInstance().validateToken(token, user);
|
||
|
|
return user;
|
||
|
|
}
|
||
|
|
|
||
|
|
void AdminController::getUsers(const HttpRequestPtr &req,
|
||
|
|
std::function<void(const HttpResponsePtr &)> &&callback) {
|
||
|
|
UserInfo user = getUserFromRequest(req);
|
||
|
|
if (user.id == 0 || !user.isAdmin) {
|
||
|
|
callback(jsonError("Unauthorized", k403Forbidden));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
auto dbClient = app().getDbClient();
|
||
|
|
*dbClient << "SELECT u.id, u.username, u.is_admin, u.is_streamer, u.created_at, "
|
||
|
|
"(SELECT COUNT(*) FROM realms WHERE user_id = u.id) as realm_count "
|
||
|
|
"FROM users u ORDER BY u.created_at DESC"
|
||
|
|
>> [callback](const Result& r) {
|
||
|
|
Json::Value resp;
|
||
|
|
resp["success"] = true;
|
||
|
|
Json::Value users(Json::arrayValue);
|
||
|
|
|
||
|
|
for (const auto& row : r) {
|
||
|
|
Json::Value user;
|
||
|
|
user["id"] = static_cast<Json::Int64>(row["id"].as<int64_t>());
|
||
|
|
user["username"] = row["username"].as<std::string>();
|
||
|
|
user["isAdmin"] = row["is_admin"].as<bool>();
|
||
|
|
user["isStreamer"] = row["is_streamer"].as<bool>();
|
||
|
|
user["createdAt"] = row["created_at"].as<std::string>();
|
||
|
|
user["realmCount"] = static_cast<Json::Int64>(row["realm_count"].as<int64_t>());
|
||
|
|
users.append(user);
|
||
|
|
}
|
||
|
|
|
||
|
|
resp["users"] = users;
|
||
|
|
callback(jsonResp(resp));
|
||
|
|
}
|
||
|
|
>> [callback](const DrogonDbException& e) {
|
||
|
|
LOG_ERROR << "Failed to get users: " << e.base().what();
|
||
|
|
callback(jsonError("Failed to get users"));
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
void AdminController::getActiveStreams(const HttpRequestPtr &req,
|
||
|
|
std::function<void(const HttpResponsePtr &)> &&callback) {
|
||
|
|
UserInfo user = getUserFromRequest(req);
|
||
|
|
if (user.id == 0 || !user.isAdmin) {
|
||
|
|
callback(jsonError("Unauthorized", k403Forbidden));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get live realms from database
|
||
|
|
auto dbClient = app().getDbClient();
|
||
|
|
*dbClient << "SELECT r.id, r.name, r.stream_key, r.viewer_count, "
|
||
|
|
"u.username FROM realms r "
|
||
|
|
"JOIN users u ON r.user_id = u.id "
|
||
|
|
"WHERE r.is_live = true"
|
||
|
|
>> [callback](const Result& r) {
|
||
|
|
Json::Value resp;
|
||
|
|
resp["success"] = true;
|
||
|
|
Json::Value streams(Json::arrayValue);
|
||
|
|
|
||
|
|
for (const auto& row : r) {
|
||
|
|
Json::Value stream;
|
||
|
|
stream["id"] = static_cast<Json::Int64>(row["id"].as<int64_t>());
|
||
|
|
stream["name"] = row["name"].as<std::string>();
|
||
|
|
stream["streamKey"] = row["stream_key"].as<std::string>();
|
||
|
|
stream["viewerCount"] = static_cast<Json::Int64>(row["viewer_count"].as<int64_t>());
|
||
|
|
stream["username"] = row["username"].as<std::string>();
|
||
|
|
streams.append(stream);
|
||
|
|
}
|
||
|
|
|
||
|
|
resp["streams"] = streams;
|
||
|
|
callback(jsonResp(resp));
|
||
|
|
}
|
||
|
|
>> [callback](const DrogonDbException& e) {
|
||
|
|
LOG_ERROR << "Failed to get active streams: " << e.base().what();
|
||
|
|
callback(jsonError("Failed to get active streams"));
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
void AdminController::disconnectStream(const HttpRequestPtr &req,
|
||
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
||
|
|
const std::string &streamKey) {
|
||
|
|
UserInfo user = getUserFromRequest(req);
|
||
|
|
if (user.id == 0 || !user.isAdmin) {
|
||
|
|
callback(jsonError("Unauthorized", k403Forbidden));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add to Redis set for OpenResty to disconnect
|
||
|
|
RedisHelper::addToSet("streams_to_disconnect", streamKey);
|
||
|
|
|
||
|
|
// Also try direct disconnect
|
||
|
|
OmeClient::getInstance().disconnectStream(streamKey, [callback](bool) {
|
||
|
|
Json::Value resp;
|
||
|
|
resp["success"] = true;
|
||
|
|
resp["message"] = "Stream disconnect initiated";
|
||
|
|
callback(jsonResp(resp));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void AdminController::promoteToStreamer(const HttpRequestPtr &req,
|
||
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
||
|
|
const std::string &userId) {
|
||
|
|
UserInfo user = getUserFromRequest(req);
|
||
|
|
if (user.id == 0 || !user.isAdmin) {
|
||
|
|
callback(jsonError("Unauthorized", k403Forbidden));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
int64_t targetUserId = std::stoll(userId);
|
||
|
|
|
||
|
|
auto dbClient = app().getDbClient();
|
||
|
|
*dbClient << "UPDATE users SET is_streamer = true WHERE id = $1"
|
||
|
|
<< targetUserId
|
||
|
|
>> [callback](const Result&) {
|
||
|
|
Json::Value resp;
|
||
|
|
resp["success"] = true;
|
||
|
|
resp["message"] = "User promoted to streamer";
|
||
|
|
callback(jsonResp(resp));
|
||
|
|
}
|
||
|
|
>> [callback](const DrogonDbException& e) {
|
||
|
|
LOG_ERROR << "Failed to promote user: " << e.base().what();
|
||
|
|
callback(jsonError("Failed to promote user"));
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
void AdminController::demoteFromStreamer(const HttpRequestPtr &req,
|
||
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
||
|
|
const std::string &userId) {
|
||
|
|
UserInfo user = getUserFromRequest(req);
|
||
|
|
if (user.id == 0 || !user.isAdmin) {
|
||
|
|
callback(jsonError("Unauthorized", k403Forbidden));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
int64_t targetUserId = std::stoll(userId);
|
||
|
|
|
||
|
|
auto dbClient = app().getDbClient();
|
||
|
|
*dbClient << "UPDATE users SET is_streamer = false WHERE id = $1"
|
||
|
|
<< targetUserId
|
||
|
|
>> [callback](const Result&) {
|
||
|
|
Json::Value resp;
|
||
|
|
resp["success"] = true;
|
||
|
|
resp["message"] = "User demoted from streamer";
|
||
|
|
callback(jsonResp(resp));
|
||
|
|
}
|
||
|
|
>> [callback](const DrogonDbException& e) {
|
||
|
|
LOG_ERROR << "Failed to demote user: " << e.base().what();
|
||
|
|
callback(jsonError("Failed to demote user"));
|
||
|
|
};
|
||
|
|
}
|