// 文件名: src/web/web_server.cc #include "web_server.h" #include #include #include "config/config_manager.h" #include "spdlog/spdlog.h" WebServer::WebServer(SystemMonitor::SystemMonitor& monitor, DeviceManager& deviceManager, LiveDataCache& liveDataCache, AlarmService& alarm_service, uint16_t port) : crow::Crow(), m_monitor(monitor), m_device_manager(deviceManager), m_live_data_cache(liveDataCache), m_alarm_service(alarm_service), m_port(port) { auto& cors = this->get_middleware(); cors.global() .origin("*") .headers("Content-Type", "Authorization") .methods("GET"_method, "POST"_method, "OPTIONS"_method); this->loglevel(crow::LogLevel::Warning); setup_routes(); } WebServer::~WebServer() { stop(); } void WebServer::start() { if (m_thread.joinable()) { spdlog::warn("Web server is already running."); return; } m_thread = std::thread([this]() { spdlog::info("Starting Web server on port {}", m_port); this->bindaddr("0.0.0.0").port(m_port).run(); spdlog::info("Web server has stopped."); }); } void WebServer::stop() { crow::Crow::stop(); if (m_thread.joinable()) { m_thread.join(); } } void WebServer::setup_routes() { CROW_ROUTE((*this), "/api/system/id").methods("GET"_method)([this] { auto deviceID = ConfigManager::getInstance().getDeviceID(); crow::json::wvalue response; response["deviceID"] = deviceID; return response; }); CROW_ROUTE((*this), "/api/system/status").methods("GET"_method)([this] { auto cpu_util = m_monitor.getCpuUtilization(); auto mem_info = m_monitor.getMemoryInfo(); crow::json::wvalue response; response["cpu_usage_percentage"] = cpu_util.totalUsagePercentage; response["memory_total_kb"] = mem_info.total_kb; response["memory_free_kb"] = mem_info.available_kb; response["memory_usage_percentage"] = (mem_info.total_kb > 0) ? (1.0 - static_cast(mem_info.available_kb) / mem_info.total_kb) * 100.0 : 0.0; return response; }); CROW_ROUTE((*this), "/api/devices").methods("GET"_method)([this] { auto devices_info = m_device_manager.get_all_device_info(); std::vector devices_json; for (const auto& info : devices_info) { crow::json::wvalue device_obj; device_obj["id"] = info.id; device_obj["type"] = info.type; device_obj["is_running"] = info.is_running; crow::json::wvalue details_obj; for (const auto& pair : info.connection_details) { details_obj[pair.first] = pair.second; } device_obj["connection_details"] = std::move(details_obj); devices_json.push_back(std::move(device_obj)); } return crow::json::wvalue(devices_json); }); CROW_ROUTE((*this), "/api/data/latest").methods("GET"_method)([this] { auto latest_data_map = m_live_data_cache.get_all_data(); crow::json::wvalue response; for (const auto& pair : latest_data_map) { response[pair.first] = crow::json::load(pair.second); } return response; }); CROW_ROUTE((*this), "/api/alarms/active").methods("GET"_method)([this] { try { auto json_string = m_alarm_service.getActiveAlarmsJson().dump(); auto res = crow::response(200, json_string); res.set_header("Content-Type", "application/json"); return res; } catch (const std::exception& e) { spdlog::error("Error processing /api/alarms/active: {}", e.what()); crow::json::wvalue error_resp; error_resp["error"] = "Failed to retrieve active alarms."; auto res = crow::response(500, error_resp.dump()); res.set_header("Content-Type", "application/json"); return res; } }); CROW_ROUTE((*this), "/api/alarms/history") .methods("GET"_method)([this](const crow::request& req) { int limit = 100; if (req.url_params.get("limit")) { try { limit = std::stoi(req.url_params.get("limit")); } catch (const std::exception&) { /* ignore invalid */ } } if (limit <= 0) limit = 100; try { auto json_string = m_alarm_service.getAlarmHistoryJson(limit).dump(); auto res = crow::response(200, json_string); res.set_header("Content-Type", "application/json"); return res; } catch (const std::exception& e) { spdlog::error("Error processing /api/alarms/history: {}", e.what()); crow::json::wvalue error_resp; error_resp["error"] = "Failed to retrieve alarm history."; auto res = crow::response(500, error_resp.dump()); res.set_header("Content-Type", "application/json"); return res; } }); CROW_ROUTE((*this), "/api/alarms/reload").methods("POST"_method)([this]() { spdlog::info("Web API: Received request to reload alarm rules..."); bool success = m_alarm_service.reload_rules(); if (success) { crow::json::wvalue response_json; response_json["status"] = "success"; response_json["message"] = "Alarm rules reloaded successfully."; auto res = crow::response(200, response_json.dump()); res.set_header("Content-Type", "application/json"); return res; } else { crow::json::wvalue error_json; error_json["status"] = "error"; error_json["message"] = "Failed to reload alarm rules. Check service logs for details."; auto res = crow::response(500, error_json.dump()); res.set_header("Content-Type", "application/json"); return res; } }); CROW_ROUTE((*this), "/api/alarms/clear") .methods("POST"_method)([this](const crow::request& req) { crow::json::rvalue j_body; try { j_body = crow::json::load(req.body); } catch (const std::exception& e) { spdlog::warn("Failed to parse request body for /api/alarms/clear: {}", e.what()); auto res = crow::response(400, "{\"error\":\"Invalid JSON body.\"}"); res.set_header("Content-Type", "application/json"); return res; } if (!j_body.has("rule_id") || !j_body.has("device_id")) { auto res = crow::response( 400, "{\"error\":\"Missing 'rule_id' or 'device_id'.\"}"); res.set_header("Content-Type", "application/json"); return res; } std::string rule_id = j_body["rule_id"].s(); std::string device_id = j_body["device_id"].s(); bool success = m_alarm_service.manually_clear_alarm(rule_id, device_id); if (success) { auto res = crow::response( 200, "{\"status\":\"success\", \"message\":\"Alarm cleared.\"}"); res.set_header("Content-Type", "application/json"); return res; } else { auto res = crow::response(404, "{\"status\":\"error\", \"message\":\"Alarm not " "found or not active.\"}"); res.set_header("Content-Type", "application/json"); return res; } }); }