异常抛出

This commit is contained in:
2026-07-13 18:10:19 +08:00
parent a0ef997c98
commit 39197a697c
10 changed files with 603 additions and 528 deletions
@@ -93,12 +93,29 @@ std::shared_ptr<SSR::Msg> Data_Source_Handler::create_msg(std::string_view packe
return nullptr;
}
extern bool data_source_debug;
size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data) {
// 先读取原始数据 注意多复制了一遍
std::string mode_data(origin_data);
std::string name = type + "_" + key;
if (data_source_debug) {
std::cout << "before origin_data_transform_mode_data " << VAR_STR_2(name, mode_data.size()) << std::endl;
}
origin_data_transform_mode_data(mode_data);
if (data_source_debug) {
std::cout << " after origin_data_transform_mode_data " << VAR_STR_2(name, mode_data.size()) <<
key << std::endl;
}
static Value_Growth_Multi_T mt;
auto t = key;
if (data_source_debug) {
std::cout << name << " 读取到:" << VAR_STR_2(key, mode_data.size()) << " " << std::endl;
}
if (mt.test(key, mode_data.size())) {
std::cout << VAR_STR_2(key, mode_data.size()) << " 数据增长过快,可能内存积压" << std::endl;
}
@@ -124,8 +141,6 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data)
}
SSR::Binary_Format_handle_buffer(
source->buffer, mode_data, [this, source, &ret](std::string &packet) {
ret++;
+8 -9
View File
@@ -9,9 +9,10 @@
std::string default_config_path;
std::string get_config_path() {
#ifdef WIN32
return "D:/ae/proj/projects/ECAP_Server/config/config.json";
#endif
// #ifdef WIN32
// get_exe_dir()
// return "D:/ae/proj/projects/ECAP_Server/config/config.json";
// #endif
static bool first = true;
if (!default_config_path.empty())
return default_config_path;
@@ -55,7 +56,7 @@ void Device_Config::server(Global *g) {
svr.Post(api + "get_net_adapter_list", [this](HTTP_Param) {
JSON ret = JSON::object();
auto arr = JSON::array();
for (auto &it : Psc::get_net_adapter_info_map()) {
for (auto &it: Psc::get_net_adapter_info_map()) {
arr.append(it.second.to_Json());
}
ret.append({"list", arr});
@@ -66,14 +67,13 @@ void Device_Config::server(Global *g) {
CHECK_JSON_PARAM
JSON old_device_config;
{
old_device_config = to_base_json();
}
HTTP_REQUIRE_PTR(net, params.get("net"))
Net_Config device, wifi;
bool has_device = false;
bool has_wifi = false;
for (const auto &it : net->children) {
for (const auto &it: net->children) {
HTTP_REQUIRE_VALUE(net_key, it.try_get_string("key"))
if (net_key == "device") {
device.from_base_json(&it);
@@ -94,7 +94,7 @@ void Device_Config::server(Global *g) {
if (!it1 || !it2 || !p1 || !p2) {
server_logger->error("", {},
LOG_POS + " ip port! " + device.ip + ":" +
std::to_string(device.port));
std::to_string(device.port));
res->setBody(warp(old_device_config).to_json_string());
return;
}
@@ -102,10 +102,9 @@ void Device_Config::server(Global *g) {
#if WIN32
bool use = false;
#else
bool use = true;
bool use = true;
#endif
{
auto &w = *this;
*w.list.get("device").value() = device;
*w.list.get("wifi").value() = wifi;
@@ -30,7 +30,7 @@ bool With_Loop_Coro::running() const {
void With_Loop_Coro::set_state(State state, std::string_view action) {
if (!action.empty()) {
std::string name = type + "_" + key;
std::cout << std::format("{} {} {} ==> {} \n", name, action, Psc::to_string(this->state),
std::cout << std::format("协程状态机 {} {} {} ==> {} \n", name, action, Psc::to_string(this->state),
Psc::to_string(state));
}
this->state = state;
+55 -27
View File
@@ -45,15 +45,28 @@ bool Coro::has_running_loop_tasks() {
return false;
}
bool data_feed_debug = false;
bool data_source_debug = false;
// 不开启wait 性能巨差 数据积压
bool wait = true;
void Coro::start() {
std::cout << "start_io_coro" << std::endl;
std::cout << "1================ start_io_coro" << std::endl;
running.store(true, std::memory_order_release);
io.restart();
io_work = std::make_unique<asio::executor_work_guard<asio::io_context::executor_type> >(io.get_executor());
io_thread = std::thread([this] {
std::cout << std::format("协程io:{} 协程启动!\n", thread_id_str());
io.run();
std::cout << std::format("协程io:{} 销毁!\n", thread_id_str());
try {
std::cout << std::format("协程io:{} 协程启动!\n", thread_id_str());
io.run();
std::cout << std::format("协程io:{} 销毁!\n", thread_id_str());
} catch (const std::exception &e) {
std::cerr << std::format("协程io异常: {}\n", e.what());
std::terminate();
} catch (...) {
std::cerr << "协程io未知异常\n";
std::terminate();
}
});
auto n = std::max<unsigned int>(1, std::thread::hardware_concurrency());
auto other_need = std::max<unsigned int>(2, n / 2);
@@ -65,7 +78,17 @@ void Coro::start() {
n = std::max<unsigned int>(1, n);
process_data = std::make_unique<asio::thread_pool>(n);
std::cout << std::format("协程已经启动 process_data concurrency: 在n个线程上{}\n", n);
data_feed_thread_task = std::make_unique<std::future<void> >(asio::co_spawn(io, coro_thread(), asio::use_future));
auto promise = std::make_shared<std::promise<void> >();
loop_task = std::make_unique<std::future<void> >(promise->get_future());
asio::co_spawn(io, coro_thread(), [promise](std::exception_ptr ep) {
if (ep) {
promise->set_exception(ep);
std::rethrow_exception(ep);
}
promise->set_value();
});
std::cout << std::flush;
}
asio::awaitable<void> Coro::sleep_for(std::chrono::milliseconds ms) {
@@ -92,10 +115,10 @@ void Coro::stop() {
running = false;
asio::post(io, [] {
});
if (data_feed_thread_task) {
if (loop_task) {
std::cout << "等待 coro_thread 退出" << std::endl;
data_feed_thread_task->get();
data_feed_thread_task.reset();
loop_task->get();
loop_task.reset();
std::cout << "coro_thread 已退出" << std::endl;
}
// 清理资源
@@ -125,7 +148,6 @@ asio::awaitable<void> Coro::coro_thread() {
co_return;
}
bool data_feed_debug = false;
asio::awaitable<void> Data_Feed::loop_coro() {
auto feed = this;
@@ -191,9 +213,6 @@ asio::awaitable<void> Data_Feed::loop_coro() {
co_return;
}
bool data_source_debug = false;
// 不开启wait 性能巨差 数据积压
bool wait = true;
asio::awaitable<void> Data_Source::loop_coro() {
auto source = this;
@@ -202,43 +221,49 @@ asio::awaitable<void> Data_Source::loop_coro() {
co->process_data->get_executor());
while (source->running()) {
if (data_source_debug) {
std::cout << std::format("{} source loop begin {}\n", key, thread_id_str());
std::cout << std::format("[1] ds:{} source loop begin {}\n", key, thread_id_str()) << std::flush;;
}
if (!source->running() || !source->registered()) {
break;
}
if (data_source_debug) {
std::cout << std::format("{} before handle {}\n", key, thread_id_str());
std::cout << std::format("[2] ds:{} before handle thread:{}\n", key, thread_id_str()) << std::flush;;
}
co_await source->handle_in_loop_coro();
if (!enable || !source->running() || !source->registered()) {
break;
}
if (data_source_debug) {
std::cout << std::format("{} before read {}\n", key, thread_id_str());
std::cout << std::format("[3] ds:{} before read thread:{}\n", key, thread_id_str()) << std::flush;;
}
auto mode_data = co_await source->read_coro();
if (data_source_debug) {
std::cout << std::format("{} after read {}\n", key, thread_id_str());
std::cout << std::format("[4] ds:{} after read thread:{}\n", key, thread_id_str()) << std::flush;;
}
if (wait) {
co_await resume_on(co->process_data->get_executor());
if (data_source_debug) {
std::cout << std::format("{} before process {}\n", key, thread_id_str());
std::cout << std::format("wait [5] ds:{} before process {} thread:{} \n", key, thread_id_str(),
mode_data.size()) <<
std::flush;;
}
auto num = source->process_mode_acs_data(mode_data);
if (data_source_debug) {
std::cout << std::format("{} after process {} {}\n", key, num, thread_id_str());
std::cout << std::format("[6] ds:{} after process {} {}\n", key, num, thread_id_str()) << std::flush;;
}
co_await resume_on(co->io.get_executor());
static Frequency_Limit_Multi mt(0.1);
auto name = type + ":" + key;
if (mt.test(name)) {
if (num == 0) {
std::cout << std::format("{} 没解析到数据睡眠10ms {} {}\n", name, num, thread_id_str());
if (data_source_debug)
std::cout << std::format("[7] {} 没解析到数据睡眠10ms {} {}\n", name, num,
thread_id_str()) << std::flush;;
ask_sleep = true;
} else {
std::cout << std::format("{} 解析到数据 {} {}\n", name, num, thread_id_str());
if (data_source_debug)
std::cout << std::format("[7] {} 解析到数据 {} {}\n", name, num,
thread_id_str()) << std::flush;;
}
}
if (num == 0) {
@@ -246,29 +271,31 @@ asio::awaitable<void> Data_Source::loop_coro() {
ask_sleep = false;
}
} else {
asio::post(*process_strand, [this, source, process_strand, mode_data = std::move(mode_data)]() mutable {
asio::post(*process_strand, [this, source, mode_data = std::move(mode_data)]() mutable {
try {
if (data_source_debug) {
std::cout << std::format("{} before process {}\n", source->key, thread_id_str());
std::cout << std::format("no wait [5] ds:{} before process {} size:{}\n", source->key,
thread_id_str(),
mode_data.size());
}
auto num = source->process_mode_acs_data(mode_data);
if (data_source_debug) {
std::cout << std::format("{} after process {} {}\n", source->key, num, thread_id_str());
std::cout << std::format("[6] ds:{} after process {} {}\n", source->key, num, thread_id_str());
}
static Frequency_Limit_Multi mt(0.1);
auto name = source->type + ":" + source->key;
if (mt.test(name)) {
if (num == 0) {
std::cout << std::format("{} 没解析到数据 {} {}\n", name, num, thread_id_str());
std::cout << std::format("[7] {} 没解析到数据 {} {}\n", name, num, thread_id_str());
ask_sleep = true;
} else {
std::cout << std::format("{} 解析到数据 {} {}\n", name, num, thread_id_str());
std::cout << std::format("[8] {} 解析到数据 {} {}\n", name, num, thread_id_str());
}
}
} catch (const std::exception &e) {
std::cout << std::format("{} process exception: {}\n", source->key, e.what());
std::cout << std::format("[9] {} process exception: {}\n", source->key, e.what());
} catch (...) {
std::cout << std::format("{} process unknown exception\n", source->key);
std::cout << std::format("[9] {} process unknown exception\n", source->key);
}
});
co_await asio::post(co->io, asio::use_awaitable);
@@ -277,6 +304,7 @@ asio::awaitable<void> Data_Source::loop_coro() {
ask_sleep = false;
}
}
std::cout << std::flush;
}
std::cout << std::format("{} source loop exit {}\n", key, thread_id_str());
co_return;
+15 -5
View File
@@ -13,29 +13,39 @@
#include <thread>
#include "asio/bind_executor.hpp"
#include "psc_global_include/Singleton.hpp"
class Coro : public Psc::Singleton<Coro> {
public:
void start();
void stop();
asio::awaitable<void> sleep_for(std::chrono::milliseconds ms);
asio::io_context io;
std::unique_ptr<asio::thread_pool> process_data;
template <typename Awaitable>
std::shared_ptr<std::future<void>> spawn(Awaitable&& awaitable) {
template<typename Awaitable>
std::shared_ptr<std::future<void> > spawn(Awaitable &&awaitable) {
auto future = asio::co_spawn(io, std::forward<Awaitable>(awaitable), asio::use_future);
return std::make_shared<std::future<void>>(std::move(future));
return std::make_shared<std::future<void> >(std::move(future));
}
private:
asio::awaitable<void> coro_thread();
bool has_running_loop_tasks();
std::unique_ptr<asio::executor_work_guard<asio::io_context::executor_type>> io_work;
std::unique_ptr<asio::executor_work_guard<asio::io_context::executor_type> > io_work;
std::thread io_thread;
std::unique_ptr<std::future<void>> data_feed_thread_task;
std::unique_ptr<std::future<void> > loop_task;
std::atomic<bool> running = false;
};
inline asio::awaitable<void> resume_on(asio::any_io_executor ex) {
co_await asio::post(asio::bind_executor(ex, asio::deferred));
}
inline asio::awaitable<void> dispatch_on(asio::any_io_executor ex) {
co_await asio::dispatch(asio::bind_executor(ex, asio::deferred));
}
+405 -402
View File
@@ -7,151 +7,153 @@
#include <string_view>
namespace {
constexpr std::size_t kMaxLogRequestBodySize = 8 * 1024;
constexpr std::size_t kMaxLogRequestBodySize = 8 * 1024;
void Log_Request_Body_If_Small(const drogon::HttpRequestPtr &request) {
const auto &body = request->getBody();
if (body.empty()) {
return;
}
void Log_Request_Body_If_Small(const drogon::HttpRequestPtr &request) {
const auto &body = request->getBody();
if (body.empty()) {
return;
}
if (body.size() <= kMaxLogRequestBodySize) {
LOG_INFO << "[request body] "
if (body.size() <= kMaxLogRequestBodySize) {
LOG_INFO << "[request body] "
<< "path=" << request->path() << ", size=" << body.size()
<< ", body=" << std::string(body.data(), body.size());
} else {
LOG_INFO << "[request body skipped] "
} else {
LOG_INFO << "[request body skipped] "
<< "path=" << request->path() << ", size=" << body.size()
<< ", max=" << kMaxLogRequestBodySize;
}
}
}
bool Is_Invalid_Http_Param(const std::exception &e) {
return dynamic_cast<const Invalid_Http_Param *>(&e) != nullptr ||
dynamic_cast<const Psc::json_assign_error *>(&e) != nullptr ||
dynamic_cast<const std::invalid_argument *>(&e) != nullptr;
}
bool Is_Safe_Version_Directory_Name(std::string_view name) {
if (name.empty() || name == "." || name == "..") {
return false;
bool Is_Invalid_Http_Param(const std::exception &e) {
return dynamic_cast<const Invalid_Http_Param *>(&e) != nullptr ||
dynamic_cast<const Psc::json_assign_error *>(&e) != nullptr ||
dynamic_cast<const std::invalid_argument *>(&e) != nullptr;
}
return std::ranges::all_of(name, [](unsigned char ch) {
return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-';
});
}
bool Is_Safe_Archive_Extension(std::string_view extension) {
if (extension.empty() || extension.front() == '.' ||
extension.back() == '.') {
return false;
bool Is_Safe_Version_Directory_Name(std::string_view name) {
if (name.empty() || name == "." || name == "..") {
return false;
}
return std::ranges::all_of(name, [](unsigned char ch) {
return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-';
});
}
return std::ranges::all_of(extension, [](unsigned char ch) {
return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-';
});
}
std::string Normalize_Archive_Extension(std::string_view extension,
std::string_view fallback = "zip") {
auto normalized = std::string(extension);
while (!normalized.empty() && normalized.front() == '.') {
normalized.erase(normalized.begin());
bool Is_Safe_Archive_Extension(std::string_view extension) {
if (extension.empty() || extension.front() == '.' ||
extension.back() == '.') {
return false;
}
return std::ranges::all_of(extension, [](unsigned char ch) {
return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-';
});
}
if (normalized.empty()) {
normalized = std::string(fallback);
}
if (!Is_Safe_Archive_Extension(normalized)) {
throw std::invalid_argument("invalid archive extension: " + normalized);
}
return normalized;
}
std::optional<std::string> Try_Get_String_Field(const JSON &object,
std::string_view key) {
const JSON *value = object.get(key);
if (value == nullptr) {
return std::nullopt;
std::string Normalize_Archive_Extension(std::string_view extension,
std::string_view fallback = "zip") {
auto normalized = std::string(extension);
while (!normalized.empty() && normalized.front() == '.') {
normalized.erase(normalized.begin());
}
if (normalized.empty()) {
normalized = std::string(fallback);
}
if (!Is_Safe_Archive_Extension(normalized)) {
throw std::invalid_argument("invalid archive extension: " + normalized);
}
return normalized;
}
if (value->valueType != Psc::String) {
throw std::invalid_argument(std::string(key) + " must be a string");
std::optional<std::string> Try_Get_String_Field(const JSON &object,
std::string_view key) {
const JSON *value = object.get(key);
if (value == nullptr) {
return std::nullopt;
}
if (value->valueType != Psc::String) {
throw std::invalid_argument(std::string(key) + " must be a string");
}
return value->val;
}
return value->val;
}
struct Archive_Export_Request {
std::string version;
Archive_Write_Options archive;
};
struct Archive_Export_Request {
std::string version;
Archive_Write_Options archive;
};
Archive_Export_Request
Parse_Archive_Export_Request(const drogon::HttpRequestPtr &req,
std::string_view default_version) {
Archive_Export_Request result;
result.version = std::move(default_version);
Archive_Export_Request
Parse_Archive_Export_Request(const drogon::HttpRequestPtr &req,
std::string_view default_version) {
Archive_Export_Request result;
result.version = std::move(default_version);
const auto &body = req->getBody();
if (body.empty()) {
return result;
}
auto parsed = Psc::try_parse_json(body.data());
if (!parsed.has_value()) {
throw_invalid_http_param("request body is not valid JSON");
}
const JSON &params = parsed.value();
if (auto version = Try_Get_String_Field(params, "version")) {
if (!version->empty()) {
result.version = *version;
}
}
if (!Is_Safe_Version_Directory_Name(result.version)) {
throw std::invalid_argument("invalid version name: " + result.version);
}
if (auto format = Try_Get_String_Field(params, "format")) {
if (!format->empty()) {
result.archive.format = *format;
}
}
if (auto filter = Try_Get_String_Field(params, "filter")) {
result.archive.filter = *filter;
}
if (auto extension = Try_Get_String_Field(params, "extension")) {
if (!extension->empty()) {
result.archive.extension = Normalize_Archive_Extension(*extension);
}
} else {
result.archive.extension =
Normalize_Archive_Extension(result.archive.extension);
}
const auto &body = req->getBody();
if (body.empty()) {
return result;
}
auto parsed = Psc::try_parse_json(body.data());
if (!parsed.has_value()) {
throw_invalid_http_param("request body is not valid JSON");
const Archive_Layout_Rules &Upgrade_Zip_Layout_Rules() {
static const Archive_Layout_Rules rules = [] {
Archive_Layout_Rules value;
value.directories = {
{
"",
{"adsb_cfg.ini", "config.json", "ecap_server", "lib9002config.json"},
{"lib9002", "wwwroot"},
{},
{}
},
{"lib9002", {"lib9002.so", "testserver"}, {"env"}, {}, {}}
};
return value;
}();
return rules;
}
const JSON &params = parsed.value();
if (auto version = Try_Get_String_Field(params, "version")) {
if (!version->empty()) {
result.version = *version;
}
}
if (!Is_Safe_Version_Directory_Name(result.version)) {
throw std::invalid_argument("invalid version name: " + result.version);
}
if (auto format = Try_Get_String_Field(params, "format")) {
if (!format->empty()) {
result.archive.format = *format;
}
}
if (auto filter = Try_Get_String_Field(params, "filter")) {
result.archive.filter = *filter;
}
if (auto extension = Try_Get_String_Field(params, "extension")) {
if (!extension->empty()) {
result.archive.extension = Normalize_Archive_Extension(*extension);
}
} else {
result.archive.extension =
Normalize_Archive_Extension(result.archive.extension);
}
return result;
}
const Archive_Layout_Rules &Upgrade_Zip_Layout_Rules() {
static const Archive_Layout_Rules rules = [] {
Archive_Layout_Rules value;
value.directories = {
{"",
{"adsb_cfg.ini", "config.json", "ecap_server", "lib9002config.json"},
{"lib9002", "wwwroot"},
{},
{}},
{"lib9002", {"lib9002.so", "testserver"}, {"env"}, {}, {}}};
return value;
}();
return rules;
}
} // namespace
void setupCors();
void Global::init_web_server() {
register_http_performance_monitor(
http_monitor_config.access_log,
http_monitor_config.slow_request_threshold_ms);
http_monitor_config.access_log,
http_monitor_config.slow_request_threshold_ms);
setupCors();
static auto &web_server = web_server_config;
Web_Server &svr = this->svr;
@@ -162,7 +164,7 @@ void Global::init_web_server() {
std::string content;
httplib::detail::read_file(path, content);
auto content_type = httplib::detail::find_content_type(
path, {}, "application/octet-stream");
path, {}, "application/octet-stream");
res->setContentTypeString(content_type);
res->setBody(content);
// res.set_file_content(path);
@@ -178,61 +180,61 @@ void Global::init_web_server() {
};
auto add_path = [return_file](std::string_view path) {
drogon::app().registerHandlerViaRegex(
std::string(path),
[return_file](
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
callback(return_file(web_server.get_true_webapp() + "/index.html"));
},
{drogon::Get});
};
drogon::app().registerHandlerViaRegex(
R"(^/tiles/.*$)",
std::string(path),
[return_file](
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
auto path = web_server.get_true_tiles() + req->getPath().substr(6);
callback(return_file(path));
},
{drogon::Get});
drogon::app().registerHandlerViaRegex(
"/",
[return_file](
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
callback(return_file(web_server.get_true_webapp() + "/index.html"));
},
{drogon::Get});
};
drogon::app().registerHandlerViaRegex(
R"(^/tiles/.*$)",
[return_file](
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
auto path = web_server.get_true_tiles() + req->getPath().substr(6);
callback(return_file(path));
},
{drogon::Get});
drogon::app().registerHandlerViaRegex(
"/",
[return_file](
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
callback(return_file(web_server.get_true_webapp() + "/index.html"));
},
{drogon::Get});
add_path(R"(^/settings)");
add_path(R"(^/map)");
add_path(R"(^/dsp)");
add_path(R"(^/aircraftlist)");
drogon::app().registerHandlerViaRegex(
R"(^/ui(/.*)?$)",
[return_file](
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
std::cout << req->path() << std::endl;
auto res = drogon::HttpResponse::newHttpResponse();
auto path = web_server.get_true_webapp();
auto rp = "/" + req->path().substr(3);
if (rp == "/") {
path += "/index.html";
} else {
path += rp;
}
callback(return_file(path));
});
R"(^/ui(/.*)?$)",
[return_file](
const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
std::cout << req->path() << std::endl;
auto res = drogon::HttpResponse::newHttpResponse();
auto path = web_server.get_true_webapp();
auto rp = "/" + req->path().substr(3);
if (rp == "/") {
path += "/index.html";
} else {
path += rp;
}
callback(return_file(path));
});
State_Report::instance();
svr.Get(R"(/get_debug_info)", [](HTTP_Param) {
JSON arr = JSON::array();
auto &sp = Global::instance()->mode_acs.data_feed_config.pool_;
arr.append(JSON::object(
{{"remain_size", sp.remain_size()}, {"free_size", sp.free_size()}}));
{{"remain_size", sp.remain_size()}, {"free_size", sp.free_size()}}));
res->setBody(arr.to_json_string("", "\n", ""));
});
@@ -243,97 +245,97 @@ void Global::init_web_server() {
mode_acs.server(this);
dsp_config.server(this);
// s.w.init();
drogon::app().registerHandler(
api + "export_logs",
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&cb) {
try {
auto export_request =
Parse_Archive_Export_Request(req, Global::instance()->version);
std::string name = export_request.version;
auto version_dir = std::filesystem::path(get_exe_dir() + "/..");
std::string data = archive_directory_to_memory(
version_dir / name / "logs", {}, export_request.archive);
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody(data);
resp->setContentTypeString("application/octet-stream");
resp->addHeader("Content-Disposition",
"attachment; filename*=UTF-8''" + name + "_logs." +
std::string(export_request.archive.extension));
resp->addHeader("ok", "true");
cb(resp);
}
catch (const std::exception &e) {
auto resp = drogon::HttpResponse::newHttpResponse();
resp->addHeader("ok", "false");
Psc::JSON json =
JSON::object({{"message", Psc::platform_2_utf8(e.what())}});
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
resp->setBody(json.to_json_string());
cb(resp);
}
});
api + "export_logs",
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&cb) {
try {
auto export_request =
Parse_Archive_Export_Request(req, Global::instance()->version);
std::string name = export_request.version;
auto version_dir = std::filesystem::path(get_exe_dir() + "/..");
std::string data = archive_directory_to_memory(
version_dir / name / "logs", {}, export_request.archive);
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody(data);
resp->setContentTypeString("application/octet-stream");
resp->addHeader("Content-Disposition",
"attachment; filename*=UTF-8''" + name + "_logs." +
std::string(export_request.archive.extension));
resp->addHeader("ok", "true");
cb(resp);
} catch (const std::exception &e) {
auto resp = drogon::HttpResponse::newHttpResponse();
resp->addHeader("ok", "false");
Psc::JSON json =
JSON::object({{"message", Psc::platform_2_utf8(e.what())}});
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
resp->setBody(json.to_json_string());
cb(resp);
}
});
drogon::app().registerHandler(
api + "export_version",
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&cb) {
try {
auto export_request =
Parse_Archive_Export_Request(req, Global::instance()->version);
std::string name = export_request.version;
auto version_dir = std::filesystem::path(get_exe_dir() + "/..");
api + "export_version",
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&cb) {
try {
auto export_request =
Parse_Archive_Export_Request(req, Global::instance()->version);
std::string name = export_request.version;
auto version_dir = std::filesystem::path(get_exe_dir() + "/..");
std::cout << "准备压缩:" << std::endl;
std::string data = archive_directory_to_memory(
version_dir / name,
{"logs/", "uploads/", "drogon_uploads/",
"backups/", // ghw的什么文件路径
"stack_trace.txt", "pt.log", "ecap_server.log", "Core", "core",
name + ".zip", name + "." + std::string(export_request.archive.extension)},
export_request.archive);
std::cout << "压缩成功:" << data.size() << std::endl;
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody(data);
resp->setContentTypeString("application/octet-stream");
resp->addHeader("Content-Disposition",
"attachment; filename*=UTF-8''" + name + "." +
std::string(export_request.archive.extension));
resp->addHeader("ok", "true");
cb(resp);
} catch (const std::exception &e) {
auto message = Psc::platform_2_utf8(e.what());
std::cout << "导出异常触发:" << message << " " << LOG_POS
<< std::endl;
auto resp = drogon::HttpResponse::newHttpResponse();
std::cout << "准备压缩:" << std::endl;
std::string data = archive_directory_to_memory(
version_dir / name,
{
"logs/", "uploads/", "drogon_uploads/",
"backups/", // ghw的什么文件路径
"stack_trace.txt", "pt.log", "ecap_server.log", "Core", "core",
name + ".zip", name + "." + std::string(export_request.archive.extension)
},
export_request.archive);
std::cout << "压缩成功:" << data.size() << std::endl;
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setBody(data);
resp->setContentTypeString("application/octet-stream");
resp->addHeader("Content-Disposition",
"attachment; filename*=UTF-8''" + name + "." +
std::string(export_request.archive.extension));
resp->addHeader("ok", "true");
cb(resp);
} catch (const std::exception &e) {
auto message = Psc::platform_2_utf8(e.what());
std::cout << "导出异常触发:" << message << " " << LOG_POS
<< std::endl;
auto resp = drogon::HttpResponse::newHttpResponse();
Psc::JSON json = JSON::object({{"message", message}});
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
resp->setBody(json.to_json_string());
resp->addHeader("ok", "false");
cb(resp);
}
});
Psc::JSON json = JSON::object({{"message", message}});
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
resp->setBody(json.to_json_string());
resp->addHeader("ok", "false");
cb(resp);
}
});
drogon::app().registerHandler(api + "restart_device", [](const drogon::
HttpRequestPtr
&req,
HttpRequestPtr
&req,
std::function<void(
const drogon::
HttpResponsePtr
&)>
&&cb) {
const drogon::
HttpResponsePtr
&)>
&&cb) {
#ifdef __linux__
std::string root_dir = Psc::get_exe_dir() + "/..";
std::string log_file = fmt::format("{}/restart_device.log", root_dir);
std::string cmd = fmt::format(
R"(systemd-run --scope --slice=system.slice sudo bash "{}/restart_device.bash" > {} 2>&1 &)",
root_dir, log_file);
R"(systemd-run --scope --slice=system.slice sudo bash "{}/restart_device.bash" > {} 2>&1 &)",
root_dir, log_file);
std::cout << "Generated Command: " << cmd << std::endl;
std::system(cmd.c_str());
#endif
@@ -344,111 +346,112 @@ void Global::init_web_server() {
});
drogon::app().registerHandler(
api + "import_version",
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&cb) {
try {
// ---------- 参数校验 ----------
auto dir = req->getHeader("dir");
if (!Is_Safe_Version_Directory_Name(dir)) {
throw std::invalid_argument("invalid or missing header: dir");
}
api + "import_version",
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&cb) {
try {
// ---------- 参数校验 ----------
auto dir = req->getHeader("dir");
if (!Is_Safe_Version_Directory_Name(dir)) {
throw std::invalid_argument("invalid or missing header: dir");
}
const auto &body = req->getBody();
auto archive_layout_result = validate_archive_layout_from_memory(
body.data(), body.size(), Upgrade_Zip_Layout_Rules());
if (!archive_layout_result) {
throw Invalid_Archive_Error(archive_layout_result.error());
}
const auto &body = req->getBody();
auto archive_layout_result = validate_archive_layout_from_memory(
body.data(), body.size(), Upgrade_Zip_Layout_Rules());
if (!archive_layout_result) {
throw Invalid_Archive_Error(archive_layout_result.error());
}
auto version_dir = std::filesystem::path(get_exe_dir() + "/..");
// ---------- 路径准备 ----------
auto targetDir = version_dir / dir;
auto version_dir = std::filesystem::path(get_exe_dir() + "/..");
// ---------- 路径准备 ----------
auto targetDir = version_dir / dir;
std::cout << "========dir====" << dir << std::endl;
std::cout << "========version_dir====" << version_dir << std::endl;
std::cout << "========targetDir====" << targetDir << std::endl;
std::cout << "========dir====" << dir << std::endl;
std::cout << "========version_dir====" << version_dir << std::endl;
std::cout << "========targetDir====" << targetDir << std::endl;
std::filesystem::create_directories(targetDir);
std::filesystem::create_directories(targetDir);
const auto archive_extension =
Normalize_Archive_Extension(req->getHeader("archive-extension"));
auto archivePath = targetDir / (dir + "." + archive_extension);
const auto archive_extension =
Normalize_Archive_Extension(req->getHeader("archive-extension"));
auto archivePath = targetDir / (dir + "." + archive_extension);
// ---------- 写 zip ----------
std::ofstream ofs(archivePath, std::ios::binary);
if (!ofs) {
throw std::runtime_error("open archive failed: " +
archivePath.string());
}
// ---------- 写 zip ----------
std::ofstream ofs(archivePath, std::ios::binary);
if (!ofs) {
throw std::runtime_error("open archive failed: " +
archivePath.string());
}
ofs.write(body.data(), body.size());
if (!ofs) {
throw std::runtime_error("write archive failed: " +
archivePath.string());
}
ofs.close();
if (!ofs) {
throw std::runtime_error("close archive failed: " +
archivePath.string());
}
ofs.write(body.data(), body.size());
if (!ofs) {
throw std::runtime_error("write archive failed: " +
archivePath.string());
}
ofs.close();
if (!ofs) {
throw std::runtime_error("close archive failed: " +
archivePath.string());
}
#ifdef _WIN32
std::string root_dir = version_dir.string();
std::string old_version =
Global::instance()->version; // Global version info
std::string new_version = dir;
std::string log_file = fmt::format(
"{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version);
std::string cmd = fmt::format(
R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {{ . '{}/upgrade.legacy' '{}' '{}' > '{}' 2>&1 }}" )",
root_dir, old_version, new_version, log_file);
std::string root_dir = version_dir.string();
std::string old_version =
Global::instance()->version; // Global version info
std::string new_version = dir;
std::string log_file = fmt::format(
"{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version);
std::string cmd = fmt::format(
R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {{ . '{}/upgrade.legacy' '{}' '{}' > '{}' 2>&1 }}" )",
root_dir, old_version, new_version, log_file);
#else
std::string root_dir = version_dir.string();
std::string old_version =
Global::instance()->version; // Global version info
std::string new_version = dir;
std::string log_file = fmt::format(
"{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version);
std::string cmd = fmt::format(
R"(systemd-run --scope --slice=system.slice sudo bash "{}/upgrade.bash" "{}" "{}" > {} 2>&1 &)",
root_dir, old_version, new_version, log_file);
std::string root_dir = version_dir.string();
std::string old_version =
Global::instance()->version; // Global version info
std::string new_version = dir;
std::string log_file = fmt::format(
"{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version);
std::string cmd = fmt::format(
R"(systemd-run --scope --slice=system.slice sudo bash "{}/upgrade.bash" "{}" "{}" > {} 2>&1 &)",
root_dir, old_version, new_version, log_file);
#endif
std::cout << "Generated Command: " << cmd << std::endl;
std::system(cmd.c_str());
// ---------- 成功响应 ----------
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
resp->setBody(R"({"ok":true})");
cb(resp);
} catch (const std::exception &e) {
auto resp = drogon::HttpResponse::newHttpResponse();
const bool invalid_input =
dynamic_cast<const Invalid_Archive_Error *>(&e) != nullptr ||
dynamic_cast<const std::invalid_argument *>(&e) != nullptr;
resp->setStatusCode(invalid_input ? drogon::k400BadRequest
: drogon::k500InternalServerError);
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
auto reason = Psc::platform_2_utf8(e.what());
std::cout << "异常" << reason << std::endl;
Psc::JSON json = JSON::object({{"ok", false}, {"error", reason}});
resp->setBody(json.to_json_string());
cb(resp);
}
},
{drogon::Post});
std::cout << "Generated Command: " << cmd << std::endl;
std::system(cmd.c_str());
// ---------- 成功响应 ----------
auto resp = drogon::HttpResponse::newHttpResponse();
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
resp->setBody(R"({"ok":true})");
cb(resp);
} catch (const std::exception &e) {
auto resp = drogon::HttpResponse::newHttpResponse();
const bool invalid_input =
dynamic_cast<const Invalid_Archive_Error *>(&e) != nullptr ||
dynamic_cast<const std::invalid_argument *>(&e) != nullptr;
resp->setStatusCode(invalid_input
? drogon::k400BadRequest
: drogon::k500InternalServerError);
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
auto reason = Psc::platform_2_utf8(e.what());
std::cout << "异常" << reason << std::endl;
Psc::JSON json = JSON::object({{"ok", false}, {"error", reason}});
resp->setBody(json.to_json_string());
cb(resp);
}
},
{drogon::Post});
svr.Post(api + "get_debug_log", [](HTTP_Param) {
CHECK_JSON_PARAM
HTTP_REQUIRE_VALUE(
start_serial_number,
params.try_get_number<Serial_Number_Type>("serial_number"))
start_serial_number,
params.try_get_number<Serial_Number_Type>("serial_number"))
auto log =
base64_encode(debug_logger_info_manager.get_info(start_serial_number));
JSON ret = JSON::object();
ret.append({"log", log});
ret.append(
{"serial_number", debug_logger_info_manager.get_cur_serial_number()});
{"serial_number", debug_logger_info_manager.get_cur_serial_number()});
ret.append({"start_serial_number", start_serial_number});
res->setBody(warp(ret).to_json_string());
});
@@ -461,7 +464,7 @@ void Global::init_web_server() {
// ret.append({"飞机总数", db.get_aircraft_num()});
// ret.append({"解出位置的数量", db.have_pos_aircraft_num});
JSON arr = JSON::object();
for (auto &ds : g->mode_acs.data_source_config.map.list()) {
for (auto &ds: g->mode_acs.data_source_config.map.list()) {
if (!ds->enable)
continue;
arr.append({ds->key, ds->statistic_json()});
@@ -486,60 +489,60 @@ void Global::init_web_server() {
});
da.setDefaultHandler(
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
LOG_WARN << "[http route not found] "
<< "path=" << req->path();
[](const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
LOG_WARN << "[http route not found] "
<< "path=" << req->path();
auto res = drogon::HttpResponse::newHttpResponse();
auto res = drogon::HttpResponse::newHttpResponse();
res->setStatusCode(drogon::k404NotFound);
res->setBody("not found");
res->addHeader("Content-Type", "text/plain; charset=utf-8");
res->setStatusCode(drogon::k404NotFound);
res->setBody("not found");
res->addHeader("Content-Type", "text/plain; charset=utf-8");
callback(res);
});
callback(res);
});
da.setCustomErrorHandler(
[](drogon::HttpStatusCode status,
const drogon::HttpRequestPtr &req) -> drogon::HttpResponsePtr {
LOG_WARN << "[http error] "
<< "path=" << req->path()
<< ", status=" << static_cast<int>(status);
[](drogon::HttpStatusCode status,
const drogon::HttpRequestPtr &req) -> drogon::HttpResponsePtr {
LOG_WARN << "[http error] "
<< "path=" << req->path()
<< ", status=" << static_cast<int>(status);
auto res = drogon::HttpResponse::newHttpResponse();
auto res = drogon::HttpResponse::newHttpResponse();
res->setStatusCode(status);
res->setBody("http error");
res->addHeader("Content-Type", "text/plain; charset=utf-8");
res->setStatusCode(status);
res->setBody("http error");
res->addHeader("Content-Type", "text/plain; charset=utf-8");
return res;
});
return res;
});
da.setExceptionHandler(
[](const std::exception &e, const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
auto res = drogon::HttpResponse::newHttpResponse();
[](const std::exception &e, const drogon::HttpRequestPtr &req,
std::function<void(const drogon::HttpResponsePtr &)> &&callback) {
auto res = drogon::HttpResponse::newHttpResponse();
if (Is_Invalid_Http_Param(e)) {
LOG_WARN << "[invalid http parameters] "
<< "path=" << req->path() << ", detail=" << e.what();
Log_Request_Body_If_Small(req);
if (Is_Invalid_Http_Param(e)) {
LOG_WARN << "[invalid http parameters] "
<< "path=" << req->path() << ", detail=" << e.what();
Log_Request_Body_If_Small(req);
res->setStatusCode(drogon::k400BadRequest);
res->setContentTypeCode(drogon::CT_APPLICATION_JSON);
res->setBody(R"({"code":400,"message":"invalid http parameters"})");
} else {
LOG_ERROR << "[handler exception] "
<< "path=" << req->path() << ", what=" << e.what();
res->setStatusCode(drogon::k400BadRequest);
res->setContentTypeCode(drogon::CT_APPLICATION_JSON);
res->setBody(R"({"code":400,"message":"invalid http parameters"})");
} else {
LOG_ERROR << "[handler exception] "
<< "path=" << req->path() << ", what=" << e.what();
res->setStatusCode(drogon::k500InternalServerError);
res->setContentTypeCode(drogon::CT_APPLICATION_JSON);
res->setBody(R"({"code":500,"message":"internal server error"})");
}
res->setStatusCode(drogon::k500InternalServerError);
res->setContentTypeCode(drogon::CT_APPLICATION_JSON);
res->setBody(R"({"code":500,"message":"internal server error"})");
}
callback(res);
});
callback(res);
});
da.setUploadPath(get_exe_dir() + "/drogon_uploads");
std::cout << "上传路径: " << da.getUploadPath() << std::endl;
@@ -550,53 +553,18 @@ void Global::init_web_server() {
void setupCors() {
// Register sync advice to handle CORS preflight (OPTIONS) requests
drogon::app().registerSyncAdvice(
[](const drogon::HttpRequestPtr &req) -> drogon::HttpResponsePtr {
if (req->method() == drogon::HttpMethod::Options) {
auto resp = drogon::HttpResponse::newHttpResponse();
[](const drogon::HttpRequestPtr &req) -> drogon::HttpResponsePtr {
if (req->method() == drogon::HttpMethod::Options) {
auto resp = drogon::HttpResponse::newHttpResponse();
// Set Access-Control-Allow-Origin header based on the Origin
// request header
const auto &origin = req->getHeader("Origin");
if (!origin.empty()) {
resp->addHeader("Access-Control-Allow-Origin", origin);
}
// Set Access-Control-Allow-Methods based on the requested method
const auto &requestMethod =
req->getHeader("Access-Control-Request-Method");
if (!requestMethod.empty()) {
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
}
// Allow credentials to be included in cross-origin requests
resp->addHeader("Access-Control-Allow-Credentials", "true");
// Set allowed headers from the Access-Control-Request-Headers
// header
const auto &requestHeaders =
req->getHeader("Access-Control-Request-Headers");
if (!requestHeaders.empty()) {
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
}
return std::move(resp);
}
return {};
});
// Register post-handling advice to add CORS headers to all responses
drogon::app().registerPostHandlingAdvice(
[](const drogon::HttpRequestPtr &req,
const drogon::HttpResponsePtr &resp) -> void {
// Set Access-Control-Allow-Origin based on the Origin request
// header
// Set Access-Control-Allow-Origin header based on the Origin
// request header
const auto &origin = req->getHeader("Origin");
if (!origin.empty()) {
resp->addHeader("Access-Control-Allow-Origin", origin);
}
// Reflect the requested Access-Control-Request-Method back in the
// response
// Set Access-Control-Allow-Methods based on the requested method
const auto &requestMethod =
req->getHeader("Access-Control-Request-Method");
if (!requestMethod.empty()) {
@@ -606,11 +574,46 @@ void setupCors() {
// Allow credentials to be included in cross-origin requests
resp->addHeader("Access-Control-Allow-Credentials", "true");
// Reflect the requested Access-Control-Request-Headers back
// Set allowed headers from the Access-Control-Request-Headers
// header
const auto &requestHeaders =
req->getHeader("Access-Control-Request-Headers");
if (!requestHeaders.empty()) {
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
}
});
return std::move(resp);
}
return {};
});
// Register post-handling advice to add CORS headers to all responses
drogon::app().registerPostHandlingAdvice(
[](const drogon::HttpRequestPtr &req,
const drogon::HttpResponsePtr &resp) -> void {
// Set Access-Control-Allow-Origin based on the Origin request
// header
const auto &origin = req->getHeader("Origin");
if (!origin.empty()) {
resp->addHeader("Access-Control-Allow-Origin", origin);
}
// Reflect the requested Access-Control-Request-Method back in the
// response
const auto &requestMethod =
req->getHeader("Access-Control-Request-Method");
if (!requestMethod.empty()) {
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
}
// Allow credentials to be included in cross-origin requests
resp->addHeader("Access-Control-Allow-Credentials", "true");
// Reflect the requested Access-Control-Request-Headers back
const auto &requestHeaders =
req->getHeader("Access-Control-Request-Headers");
if (!requestHeaders.empty()) {
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
}
});
}