From 1e5660512e7d146b20a2e49a031e570e048a128b Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 29 Sep 2026 19:09:02 -0500 Subject: [PATCH] refactor(chat): remove the chat server's web API The dashboard's API covers what it offered (online players, teams, announcements) with API keys, so the chat server no longer runs a web server; its settings show as unused on the settings page. Co-Authored-By: Claude Opus 5.5 --- README.md | 5 +- dChatServer/CMakeLists.txt | 6 +- dChatServer/ChatJSONUtils.cpp | 49 ------- dChatServer/ChatJSONUtils.h | 18 --- dChatServer/ChatServer.cpp | 16 --- dChatServer/ChatWeb.cpp | 137 ------------------ dChatServer/ChatWeb.h | 19 --- dChatServer/PlayerContainer.cpp | 3 - dDashboardServer/routes/SettingsCatalog.cpp | 6 +- docs/ChatWebAPI.yaml | 151 -------------------- resources/chatconfig.ini | 6 - 11 files changed, 7 insertions(+), 409 deletions(-) delete mode 100644 dChatServer/ChatJSONUtils.cpp delete mode 100644 dChatServer/ChatJSONUtils.h delete mode 100644 dChatServer/ChatWeb.cpp delete mode 100644 dChatServer/ChatWeb.h delete mode 100644 docs/ChatWebAPI.yaml diff --git a/README.md b/README.md index 2ca849022..20f4e4a34 100644 --- a/README.md +++ b/README.md @@ -437,9 +437,8 @@ Now follow the [build](#build-the-server) section for your system and your serve ## In-game commands * A list of all in-game commands can be found [here](./docs/Commands.md). -## Chat Web API -* The Chat server has an API that can be enabled via `chatconfig.ini`. -* You can view the OpenAPI doc for the API here [here](./docs/ChatWebAPI.yaml). +## Web API +* The dashboard has the API (online players, teams, announcements and the rest), with API keys; see [the dashboard docs](./docs/Dashboard.md). The chat server no longer has its own web API. ## Verifying your client files diff --git a/dChatServer/CMakeLists.txt b/dChatServer/CMakeLists.txt index 5f196a933..02add3843 100644 --- a/dChatServer/CMakeLists.txt +++ b/dChatServer/CMakeLists.txt @@ -1,21 +1,19 @@ set(DCHATSERVER_SOURCES "ChatIgnoreList.cpp" "ChatPacketHandler.cpp" - "ChatJSONUtils.cpp" "ChatGuilds.cpp" - "ChatWeb.cpp" "GuildManager.cpp" "PlayerContainer.cpp" "TeamContainer.cpp" ) add_executable(ChatServer "ChatServer.cpp") -target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter" "${PROJECT_SOURCE_DIR}/dWeb") +target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter") add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") add_library(dChatServer ${DCHATSERVER_SOURCES}) target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer" "${PROJECT_SOURCE_DIR}/dChatFilter") target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter glm::glm) -target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose dWeb) +target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer) diff --git a/dChatServer/ChatJSONUtils.cpp b/dChatServer/ChatJSONUtils.cpp deleted file mode 100644 index a38e34257..000000000 --- a/dChatServer/ChatJSONUtils.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "ChatJSONUtils.h" - -#include "json.hpp" - -using json = nlohmann::json; - -void to_json(json& data, const PlayerData& playerData) { - data["id"] = playerData.playerID; - data["name"] = playerData.playerName; - data["gm_level"] = playerData.gmLevel; - data["muted"] = playerData.GetIsMuted(); - - auto& zoneID = data["zone_id"]; - zoneID["map_id"] = playerData.zoneID.GetMapID(); - zoneID["instance_id"] = playerData.zoneID.GetInstanceID(); - zoneID["clone_id"] = playerData.zoneID.GetCloneID(); -} - -void to_json(json& data, const PlayerContainer& playerContainer) { - data = json::array(); - for (auto& playerData : playerContainer.GetAllPlayers()) { - if (playerData.first == LWOOBJID_EMPTY) continue; - data.push_back(playerData.second); - } -} - -void to_json(json& data, const TeamData& teamData) { - data["id"] = teamData.teamID; - data["loot_flag"] = teamData.lootFlag; - data["local"] = teamData.local; - - auto& leader = Game::playerContainer.GetPlayerData(teamData.leaderID); - data["leader"] = leader.playerName; - - auto& members = data["members"]; - for (auto& member : teamData.memberIDs) { - auto& playerData = Game::playerContainer.GetPlayerData(member); - - if (!playerData) continue; - members.push_back(playerData); - } -} - -void TeamContainer::to_json(json& data, const TeamContainer::Data& teamContainer) { - for (auto& teamData : TeamContainer::GetTeams()) { - if (!teamData) continue; - data.push_back(*teamData); - } -} diff --git a/dChatServer/ChatJSONUtils.h b/dChatServer/ChatJSONUtils.h deleted file mode 100644 index e3120bb31..000000000 --- a/dChatServer/ChatJSONUtils.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __CHATJSONUTILS_H__ -#define __CHATJSONUTILS_H__ - -#include "json_fwd.hpp" -#include "PlayerContainer.h" -#include "TeamContainer.h" - -/* Remember, to_json needs to be in the same namespace as the class its located in */ - -void to_json(nlohmann::json& data, const PlayerData& playerData); -void to_json(nlohmann::json& data, const PlayerContainer& playerContainer); -void to_json(nlohmann::json& data, const TeamData& teamData); - -namespace TeamContainer { - void to_json(nlohmann::json& data, const TeamContainer::Data& teamData); -}; - -#endif // !__CHATJSONUTILS_H__ diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 127955261..b1613a46b 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -38,7 +38,6 @@ #include "RakNetDefines.h" #include "MessageIdentifiers.h" -#include "ChatWeb.h" #include "BuildInfo.h" namespace Game { @@ -110,19 +109,6 @@ int main(int argc, char** argv) { // Settings edited on the dashboard (server_config table) are layered over the files from here on Game::config->SetDatabaseSync(ConfigSync::Sync); - // setup the chat api web server - const uint32_t web_server_port = GeneralUtils::TryParse(Game::config->GetValue("web_server_port")).value_or(2005); - if (Game::config->GetValue("web_server_enabled") == "1" && !Game::web.Startup("localhost", web_server_port)) { - // if we want the web server and it fails to start, exit - LOG("Failed to start web server, shutting down."); - Database::Destroy(serviceName); - delete Game::logger; - delete Game::config; - return EXIT_FAILURE; - } - - if (Game::web.IsEnabled()) ChatWeb::RegisterRoutes(); - //Find out the master's IP: std::string masterIP; uint32_t masterPort = 1000; @@ -190,8 +176,6 @@ int main(int argc, char** argv) { packet = nullptr; } - // Check and handle web requests: - if (Game::web.IsEnabled()) Game::web.ReceiveRequests(); //Push our log every 30s: if (framesSinceLastFlush >= logFlushTime) { diff --git a/dChatServer/ChatWeb.cpp b/dChatServer/ChatWeb.cpp deleted file mode 100644 index 51bf00bfc..000000000 --- a/dChatServer/ChatWeb.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "ChatWeb.h" - -#include "Logger.h" -#include "Game.h" -#include "json.hpp" -#include "dCommonVars.h" -#include "MessageType/Chat.h" -#include "dServer.h" -#include "dConfig.h" -#include "PlayerContainer.h" -#include "GeneralUtils.h" -#include "eHTTPMethod.h" -#include "magic_enum.hpp" -#include "ChatPackets.h" -#include "StringifiedEnum.h" -#include "Database.h" -#include "ChatJSONUtils.h" -#include "JSONUtils.h" -#include "eGameMasterLevel.h" -#include "dChatFilter.h" -#include "TeamContainer.h" -#include "HTTPContext.h" - -using json = nlohmann::json; - -void HandleHTTPPlayersRequest(HTTPReply& reply, const HTTPContext& context) { - const json data = Game::playerContainer; - reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK; - reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump(); -} - -void HandleHTTPTeamsRequest(HTTPReply& reply, const HTTPContext& context) { - const json data = TeamContainer::GetTeamContainer(); - reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK; - reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump(); -} - -void HandleHTTPAnnounceRequest(HTTPReply& reply, const HTTPContext& context) { - auto data = GeneralUtils::TryParse(context.body); - if (!data) { - reply.status = eHTTPStatusCode::BAD_REQUEST; - reply.message = "{\"error\":\"Invalid JSON\"}"; - 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.Broadcast(); - - reply.status = eHTTPStatusCode::OK; - reply.message = "{\"status\":\"Announcement Sent\"}"; - } -} - -void HandleWSChat(mg_connection* connection, json data) { - auto check = JSONUtils::CheckRequiredData(data, { "user", "message", "gmlevel", "zone" }); - if (!check.empty()) { - LOG_DEBUG("Received invalid websocket message: %s", check.c_str()); - } else { - const auto user = data["user"].get(); - const auto message = data["message"].get(); - const auto gmlevel = GeneralUtils::TryParse(data["gmlevel"].get()).value_or(eGameMasterLevel::CIVILIAN); - const auto zone = data["zone"].get(); - - const auto filter_check = Game::chatFilter->IsSentenceOkay(message, gmlevel); - if (!filter_check.empty()) { - LOG_DEBUG("Chat message \"%s\" from %s was not allowed", message.c_str(), user.c_str()); - data["error"] = "Chat message blocked by filter"; - data["filtered"] = json::array(); - for (const auto& [start, len] : filter_check) { - data["filtered"].push_back(message.substr(start, len)); - } - mg_ws_send(connection, data.dump().c_str(), data.dump().size(), WEBSOCKET_OP_TEXT); - return; - } - LOG("%s: %s", user.c_str(), message.c_str()); - - // TODO: Implement chat message handling from websocket message - - } -} - -namespace ChatWeb { - void RegisterRoutes() { - - // REST API v1 routes - - std::string v1_route = "/api/v1/"; - Game::web.RegisterHTTPRoute({ - .path = v1_route + "players", - .method = eHTTPMethod::GET, - .middleware = {}, - .handle = HandleHTTPPlayersRequest - }); - - Game::web.RegisterHTTPRoute({ - .path = v1_route + "teams", - .method = eHTTPMethod::GET, - .middleware = {}, - .handle = HandleHTTPTeamsRequest - }); - - Game::web.RegisterHTTPRoute({ - .path = v1_route + "announce", - .method = eHTTPMethod::POST, - .middleware = {}, - .handle = HandleHTTPAnnounceRequest - }); - - // WebSocket Events Handlers - - // Game::web.RegisterWSEvent({ - // .name = "chat", - // .handle = HandleWSChat - // }); - - // WebSocket subscriptions - - Game::web.RegisterWSSubscription("player"); - } - - void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType) { - json data; - data["player_data"] = player; - data["update_type"] = magic_enum::enum_name(activityType); - Game::web.SendWSMessage("player", data); - } -} - diff --git a/dChatServer/ChatWeb.h b/dChatServer/ChatWeb.h deleted file mode 100644 index dc389f586..000000000 --- a/dChatServer/ChatWeb.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __CHATWEB_H__ -#define __CHATWEB_H__ - -#include -#include - -#include "Web.h" -#include "PlayerContainer.h" -#include "IActivityLog.h" -#include "ChatPacketHandler.h" - -namespace ChatWeb { - void RegisterRoutes(); - void SendWSPlayerUpdate(const PlayerData& player, eActivityType activityType); -}; - - -#endif // __CHATWEB_H__ - diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index 7da79bdbe..367485d4d 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -12,7 +12,6 @@ #include "ChatPackets.h" #include "dConfig.h" #include "MessageType/Chat.h" -#include "ChatWeb.h" #include "TeamContainer.h" #include "ChatGuilds.h" #include "GuildManager.h" @@ -75,7 +74,6 @@ void PlayerContainer::InsertPlayer(const ChatPackets::LoginSessionNotify& notify m_PlayerCount++; LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID()); - ChatWeb::SendWSPlayerUpdate(data, isLogin ? eActivityType::PlayerLoggedIn : eActivityType::PlayerChangedZone); Database::Get()->UpdateActivityLog(data.playerID, isLogin ? eActivityType::PlayerLoggedIn : eActivityType::PlayerChangedZone, data.zoneID.GetMapID()); m_PlayersToRemove.erase(playerId); @@ -124,7 +122,6 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) { ChatGuilds::Get().PlayerOffline(playerID); - ChatWeb::SendWSPlayerUpdate(player, eActivityType::PlayerLoggedOut); m_PlayerCount--; LOG("Removed user: %llu", playerID); diff --git a/dDashboardServer/routes/SettingsCatalog.cpp b/dDashboardServer/routes/SettingsCatalog.cpp index 9b9bc8002..fee2eb9a7 100644 --- a/dDashboardServer/routes/SettingsCatalog.cpp +++ b/dDashboardServer/routes/SettingsCatalog.cpp @@ -167,9 +167,9 @@ namespace { c.Add(Bool(SHARED, "dont_generate_dcf", "Don't build the chat filter file", "Skip compiling the chat word list to a file.", false, true)); c.AddSection("Chat web API", "A small HTTP API on the chat server, on localhost only."); - c.Add(Bool(CHAT, "web_server_enabled", "Chat web API", "", false, true)); - c.Add(When(Port(CHAT, "web_server_port", "Port", "", "2005"), CHAT, "web_server_enabled", { "1" })); - c.Add(Unused(Port(CHAT, "web_server_listen_port", "Port (old name)", "Not read: the chat server uses web_server_port.", "2005"))); + c.Add(Unused(Bool(CHAT, "web_server_enabled", "Chat web API", "Not read: the chat server's web API was removed; the dashboard's API covers players, teams and announcements.", false, true))); + c.Add(Unused(Port(CHAT, "web_server_port", "Chat web API port", "Not read: the chat server's web API was removed.", "2005"))); + c.Add(Unused(Port(CHAT, "web_server_listen_port", "Port (old name)", "Not read.", "2005"))); c.AddSection("Physics", "How world servers split zones for collision checks."); c.Add(Bool(WORLD, "phys_spatial_partitioning", "Spatial partitioning", "Faster collision checks. Leave on unless debugging physics.", true, true)); diff --git a/docs/ChatWebAPI.yaml b/docs/ChatWebAPI.yaml deleted file mode 100644 index e319adcb7..000000000 --- a/docs/ChatWebAPI.yaml +++ /dev/null @@ -1,151 +0,0 @@ -openapi: 3.0.3 -info: - title: DLU Chat Server API - description: |- - This documents the available api endpoints for the DLU Chat Server Web API - contact: - name: DarkflameUniverse Github - url: https://github.com/DarkflameUniverse/DarkflameServer/issues - license: - name: GNU AGPL v3.0 - url: https://github.com/DarkflameUniverse/DarkflameServer/blob/main/LICENSE - version: 1.0.0 - -externalDocs: - description: Find out more about Swagger - url: http://swagger.io - -servers: - - url: http://localhost:2005/api/v1/ - description: localhost - -tags: - - name: management - description: Server Management Utilities - - name: user - description: User Data Utilities - -paths: - /announce: - post: - tags: - - management - summary: Send an announcement to the game server - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/Announce" - required: true - responses: - "200": - description: Successful operation - "400": - description: Missing Parameter - - /players: - get: - tags: - - user - summary: Get all online Players - responses: - "200": - description: Successful operation - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Player" - "204": - description: No Data - - /teams: - get: - tags: - - user - summary: Get all active Teams - responses: - "200": - description: Successful operation - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Team" - "204": - description: No Data - -components: - schemas: - Player: - type: object - properties: - id: - type: integer - format: int64 - example: 1152921508901824000 - gm_level: - type: integer - format: uint8 - example: 0 - name: - type: string - example: thisisatestname - muted: - type: boolean - example: false - zone_id: - $ref: "#/components/schemas/ZoneID" - - ZoneID: - type: object - properties: - map_id: - type: integer - format: uint16 - example: 1200 - instance_id: - type: integer - format: uint16 - example: 2 - clone_id: - type: integer - format: uint32 - example: 0 - - Team: - type: object - properties: - id: - type: integer - format: int64 - example: 1152921508901824000 - loot_flag: - type: integer - format: uint8 - example: 1 - local: - type: boolean - example: false - leader: - type: string - example: thisisatestname - members: - type: array - items: - $ref: "#/components/schemas/Player" - - Announce: - required: - - title - - message - type: object - properties: - title: - type: string - example: A Mythran has taken Action against you! - message: - type: string - example: Check your mailbox for details! diff --git a/resources/chatconfig.ini b/resources/chatconfig.ini index 76d2315ef..934f891cb 100644 --- a/resources/chatconfig.ini +++ b/resources/chatconfig.ini @@ -12,10 +12,4 @@ guild_max_members=100 # Seconds a guild invite can be answered guild_invite_timeout=600 -web_server_enabled=0 - -# Unused for now -# web_server_listen_ip=127.0.0.1 -web_server_port=2005 - max_ignores=32