#include "server_app.hpp" #include namespace adminive::example { namespace { Radio_State make_radio_state(Radio_Mode mode, int port, int buffer_count, int low_watermark, int high_watermark) { Radio_State result; result.mode = mode; result.port = port; result.buffer_count = buffer_count; result.low_watermark = low_watermark; result.high_watermark = high_watermark; return result; } } Server_App::Server_App(int port, std::filesystem::path frontend_dist, std::filesystem::path config_file) : port_(port), frontend_dist_(std::move(frontend_dist)), config_store_(std::move(config_file)), state_resource_("/admin/radio_states", make_radio_states()), service_config_resource_(config_store_.data().radio_service, "/admin/config/radio", adminive::make_resource_transaction([this](const Radio_Service_Config& value, const Request_Context&) { config_store_.write_radio_service_candidate(value); }), &config_store_.mutex()), alert_config_resource_(config_store_.data().alerts, "/admin/config/alerts", adminive::make_resource_transaction([this](const Alert_Config& value, const Request_Context&) { config_store_.write_alerts_candidate(value); }), &config_store_.mutex()), device_tables_(config_store_) { state_resource_.register_overview_status("/admin/status", 2000); state_resource_.register_status<&Radio_State::status>(2000); bind_routes(); } int Server_App::run() { if(!server_.set_mount_point("/", frontend_dist_.string())) { std::cerr << "frontend build directory does not exist: " << frontend_dist_.string() << '\n'; std::cerr << "run npm run build from the project root before starting the backend\n"; return 3; } std::cout << "Adminive server listening on http://127.0.0.1:" << port_ << '\n'; std::cout << "Serving frontend from " << frontend_dist_.string() << '\n'; std::cout << "Configuration file " << config_store_.path().string() << '\n'; std::cout << std::flush; if(!server_.listen("0.0.0.0", port_)) { std::cerr << "failed to start server\n"; return 1; } return 0; } Json Server_App::amis_schema() const { Json tabs = Json::array(); const Json states_schema = state_resource_.amis_schema(); tabs.push_back(Json{{"title", "无线电状态"}, {"body", states_schema.at("body")}}); tabs.push_back(Json{{"title", "无线电服务配置"}, {"body", service_config_resource_.amis_schema()}}); tabs.push_back(Json{{"title", "告警配置"}, {"body", alert_config_resource_.amis_schema()}}); tabs.push_back(Json{{"title", "多类型设备"}, {"body", device_tables_.amis_schema()}}); return Json{{"type", "page"}, {"title", "Adminive 完整示例"}, {"subTitle", "列表中文映射、后端排序、拖拽顺序、多态类型表格和持久化配置均由后端定义"}, {"body", Json{{"type", "tabs"}, {"tabs", std::move(tabs)}}}}; } std::vector Server_App::make_radio_states() { return { make_radio_state(Radio_Mode::receive, 10001, 2, 16, 64), make_radio_state(Radio_Mode::transmit, 10002, 4, 32, 128), make_radio_state(Radio_Mode::duplex, 10003, 8, 64, 256) }; } void Server_App::bind_routes() { state_resource_.bind(server_); service_config_resource_.bind(server_); alert_config_resource_.bind(server_); device_tables_.bind(server_); server_.Get("/admin/amis", [this](const httplib::Request&, httplib::Response& response) { write_http_json(response, Json{{"status", 0}, {"msg", ""}, {"data", amis_schema()}}); }); server_.Get("/admin/status", [this](const httplib::Request&, httplib::Response& response) { const auto now = std::chrono::system_clock::now(); const auto uptime = std::chrono::duration_cast(std::chrono::steady_clock::now() - started_at_).count(); Server_Status status; status.server_time = format_utc_time(now); status.uptime_seconds = static_cast(uptime); status.poll_sequence = poll_sequence_.fetch_add(1, std::memory_order_relaxed) + 1; status.radio_state_count = state_resource_.size(); status.listen_port = port_; write_http_json(response, Json{{"status", 0}, {"msg", ""}, {"data", to_status_json(status)}}); }); } }