#include "Web_Server.hpp" #include "Graph_Metadata.hpp" /* 按图请求描述数据。 */ #include "Graph_Session.hpp" #include "Graph_WebSocket.hpp" #include #include #include #include #include #include namespace aethera::web { namespace { drogon::HttpResponsePtr json_response(nlohmann::json value) { auto response = drogon::HttpResponse::newHttpResponse(); response->setContentTypeCode(drogon::CT_APPLICATION_JSON); response->setBody(value.dump()); return response; } drogon::HttpResponsePtr error_response(drogon::HttpStatusCode status, std::string message) { auto response = json_response({{"error", std::move(message)}}); response->setStatusCode(status); return response; } } int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root) { const auto hardware_threads = std::max(2U, std::thread::hardware_concurrency()); auto graph_pool = std::make_shared(std::min(8U, hardware_threads)); auto registry = std::make_shared(graph_pool->get_executor()); auto websocket = std::make_shared(registry); auto& app = drogon::app(); app.registerHandler("/api/graphs", [](const drogon::HttpRequestPtr&, std::function&& callback) { callback(json_response(graph_catalog_json())); }, {drogon::Get}); app.registerHandler("/api/graphs/{1}/descriptor", [](const drogon::HttpRequestPtr&, std::function&& callback, std::string graph_id) { if (!find_graph(graph_id)) { callback(error_response(drogon::k404NotFound, "unknown graph")); return; } callback(json_response({{"prop", graph_prop_descriptor_json(graph_id)}, {"state", graph_state_descriptor_json(graph_id)}, {"state_api", "/api/graphs/" + graph_id + "/state"}, {"prop_api", "/api/graphs/" + graph_id + "/prop"}})); }, {drogon::Get}); app.registerHandler("/api/graphs/{1}/state", [registry](const drogon::HttpRequestPtr&, std::function&& callback, std::string graph_id) { auto session = registry->acquire(graph_id); if (!session) { callback(error_response(drogon::k404NotFound, "unknown graph")); return; } auto output = std::make_shared>(std::move(callback)); session->async_state([output](nlohmann::json state) { (*output)(json_response(std::move(state))); }); }, {drogon::Get}); app.registerHandler("/api/graphs/{1}/prop", [registry](const drogon::HttpRequestPtr&, std::function&& callback, std::string graph_id) { auto session = registry->acquire(graph_id); if (!session) { callback(error_response(drogon::k404NotFound, "unknown graph")); return; } auto output = std::make_shared>(std::move(callback)); session->async_prop([output](nlohmann::json prop) { (*output)(json_response(std::move(prop))); }); }, {drogon::Get}); app.registerHandler("/api/graphs/{1}/prop", [registry](const drogon::HttpRequestPtr& request, std::function&& callback, std::string graph_id) { auto session = registry->acquire(graph_id); if (!session) { callback(error_response(drogon::k404NotFound, "unknown graph")); return; } nlohmann::json patch; try { patch = nlohmann::json::parse(request->body()); } catch (const nlohmann::json::exception&) { callback(error_response(drogon::k400BadRequest, "invalid JSON patch")); return; } auto output = std::make_shared>(std::move(callback)); session->async_patch_prop(std::move(patch), [output](nlohmann::json result) { (*output)(json_response(std::move(result))); }); }, {drogon::Patch}); app.registerController(websocket).setDocumentRoot(asset_root.string()).setHomePage("index.html").setStaticFileHeaders({{"Cache-Control", "no-store"}}).addListener("127.0.0.1", port).setThreadNum(std::min(8U, hardware_threads)).setIdleConnectionTimeout(90).run(); graph_pool->stop(); graph_pool->join(); return 0; } }