cleanup and make the web routes cleaner

This commit is contained in:
Aaron Kimbre
2025-01-06 01:23:29 -06:00
parent f3b4143698
commit 88376c233c
12 changed files with 337 additions and 112 deletions

View File

@@ -9,8 +9,13 @@
#include "dConfig.h"
#include "PlayerContainer.h"
#include "GeneralUtils.h"
#include "JSONUtils.h"
#include "eHTTPMethod.h"
#include "eHTTPStatusCode.h"
#include "magic_enum.hpp"
#include "ChatPackets.h"
#include "json.hpp"
#include "StringifiedEnum.h"
using json = nlohmann::json;
@@ -22,75 +27,148 @@ namespace {
const char* json_content_type = "Content-Type: application/json\r\n";
}
struct HttpReply {
uint32_t status = 404;
struct HTTPReply {
eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
std::string message = "{\"error\":\"Not Found\"}";
};
void HandleRequests(mg_connection* connection, int request, void* request_data) {
if (request == MG_EV_HTTP_MSG) {
HttpReply reply;
const mg_http_message* const http_msg = static_cast<mg_http_message*>(request_data);
if (!http_msg) {
reply.status = 400;
reply.message = "{\"error\":\"Invalid Request\"}";
} else {
// Handle Post requests
if (mg_strcmp(http_msg->method, mg_str("POST")) == 0) {
auto data = GeneralUtils::TryParse<json>(http_msg->body.buf);
if (!data) {
reply.status = 400;
reply.message = "{\"error\":\"Invalid JSON\"}";
} else if (mg_match(http_msg->uri, mg_str((root_path + "announce").c_str()), NULL)) {
auto& jsonBuffer = data.value();
// handle announcements
bool CheckValidJSON(std::optional<json> data, HTTPReply& reply) {
if (!data) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid JSON\"}";
return false;
}
return true;
}
if (!jsonBuffer.contains("title")) {
reply.status = 400;
reply.message = "{\"error\":\"Missing paramater: title\"}";
} else if (!jsonBuffer.contains("message")) {
reply.status = 400;
reply.message = "{\"error\":\"Missing paramater: message\"}";
} else {
std::string title = jsonBuffer["title"];
std::string message = jsonBuffer["message"];
void HandlePlayersRequest(HTTPReply& reply) {
const json data = Game::playerContainer;
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump();
}
// build and send the packet to all world servers
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_ANNOUNCE);
bitStream.Write<uint32_t>(title.size());
bitStream.Write(title);
bitStream.Write<uint32_t>(message.size());
bitStream.Write(message);
SEND_PACKET_BROADCAST;
void HandleTeamsRequest(HTTPReply& reply) {
const json data = Game::playerContainer.GetTeamContainer();
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
}
reply.status = 200;
reply.message = "{\"status\":\"Announcement Sent\"}";
}
}
// Handle GET Requests
} else if (mg_strcmp(http_msg->method, mg_str("GET")) == 0) {
// Get All Online players
if (mg_match(http_msg->uri, mg_str((root_path + "players").c_str()), NULL)) {
const json data = Game::playerContainer;
void HandleInvalidRoute(HTTPReply& reply) {
reply.status = eHTTPStatusCode::NOT_FOUND;
reply.message = "{\"error\":\"Invalid Route\"}";
}
reply.status = data.empty() ? 204 : 200;
reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump();
} else if (mg_match(http_msg->uri, mg_str((root_path + "teams").c_str()), NULL)) {
// Get Teams
const json data = Game::playerContainer.GetTeamContainer();
reply.status = data.empty() ? 204 : 200;
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
}
}
}
LOG_DEBUG("Replying with status %d: %s", reply.status, reply.message.c_str());
mg_http_reply(connection, reply.status, json_content_type, reply.message.c_str());
void HandleGET(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
switch (route) {
case ChatWebAPI::eRoute::PLAYERS:
HandlePlayersRequest(reply);
break;
case ChatWebAPI::eRoute::TEAMS:
HandleTeamsRequest(reply);
break;
case ChatWebAPI::eRoute::INVALID:
default:
HandleInvalidRoute(reply);
break;
}
}
void HandleAnnounceRequest(HTTPReply& reply, const std::optional<json>& data) {
if (!CheckValidJSON(data, reply)) return;
const auto& good_data = data.value();
auto check = JSONUtils::CheckRequiredData(good_data, { "title", "message" });
if (!check.empty()) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = check;
} else {
ChatPackets::Announcement announcement;
announcement.title = good_data["title"];
announcement.message = good_data["message"];
announcement.Send();
reply.status = eHTTPStatusCode::OK;
reply.message = "{\"status\":\"Announcement Sent\"}";
}
}
void HandlePOST(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
auto data = GeneralUtils::TryParse<json>(body);
switch (route) {
case ChatWebAPI::eRoute::ANNOUNCE:{
HandleAnnounceRequest(reply, data.value());
break;
}
case ChatWebAPI::eRoute::INVALID:
default:
HandleInvalidRoute(reply);
break;
}
}
void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_msg) {
HTTPReply reply;
if (!http_msg) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid Request\"}";
} else {
// convert method from cstring to std string
std::string method_string(http_msg->method.buf, http_msg->method.len);
// get mehtod from mg to enum
const eHTTPMethod method = magic_enum::enum_cast<eHTTPMethod>(method_string).value_or(eHTTPMethod::INVALID);
// convert uri from cstring to std string
std::string uri(http_msg->uri.buf, http_msg->uri.len);
// check for root path
if (uri.find(root_path) == 0) {
// remove root path from uri
uri.erase(0, root_path.length());
// convert uri to uppercase
std::transform(uri.begin(), uri.end(), uri.begin(), ::toupper);
// convert uri string to route enum
ChatWebAPI::eRoute route = magic_enum::enum_cast<ChatWebAPI::eRoute>(uri).value_or(ChatWebAPI::eRoute::INVALID);
// convert body from cstring to std string
std::string body(http_msg->body.buf, http_msg->body.len);
switch (method) {
case eHTTPMethod::GET:
HandleGET(reply, route, body);
break;
case eHTTPMethod::POST:
HandlePOST(reply, route, body);
break;
case eHTTPMethod::PUT:
case eHTTPMethod::DELETE:
case eHTTPMethod::HEAD:
case eHTTPMethod::CONNECT:
case eHTTPMethod::OPTIONS:
case eHTTPMethod::TRACE:
case eHTTPMethod::PATCH:
case eHTTPMethod::INVALID:
default:
reply.status = eHTTPStatusCode::METHOD_NOT_ALLOWED;
reply.message = "{\"error\":\"Invalid Method\"}";
break;
}
}
}
mg_http_reply(connection, static_cast<int>(reply.status), json_content_type, reply.message.c_str());
}
void HandleRequests(mg_connection* connection, int request, void* request_data) {
switch (request) {
case MG_EV_HTTP_MSG:
HandleHTTPMessage(connection, static_cast<mg_http_message*>(request_data));
break;
default:
break;
}
}
ChatWebAPI::ChatWebAPI() {
mg_log_set(MG_LL_NONE);