|
|
@@ -11,6 +11,7 @@
|
|
|
#include "../system/metrics.h"
|
|
|
#include "../system/metrics_history.h"
|
|
|
#include "../system/process.h"
|
|
|
+#include "../schedule/schedule.h"
|
|
|
#include "../tasks/tasks.h"
|
|
|
#include "../utils.h"
|
|
|
#include "../website/website.h"
|
|
|
@@ -27,6 +28,7 @@
|
|
|
#include <atomic>
|
|
|
#include <chrono>
|
|
|
#include <cstdint>
|
|
|
+#include <cstdlib>
|
|
|
#include <csignal>
|
|
|
#include <filesystem>
|
|
|
#include <iostream>
|
|
|
@@ -46,10 +48,24 @@ using ylib::network::http::response;
|
|
|
using ylib::network::http::websocket_message;
|
|
|
|
|
|
std::atomic<bool> g_running{true};
|
|
|
+std::atomic<int> g_signal_count{0};
|
|
|
ylib::network::http::center* g_center = nullptr;
|
|
|
|
|
|
+void stop_background_services() {
|
|
|
+ schedule::stop();
|
|
|
+ weblog::stop();
|
|
|
+ system::history_stop();
|
|
|
+ tasks::stop();
|
|
|
+}
|
|
|
+
|
|
|
void on_signal(int) {
|
|
|
+ // First signal: request graceful shutdown. Second: force-exit in case
|
|
|
+ // close()/join is stuck (e.g. listen failed then center->close hangs).
|
|
|
+ const int n = g_signal_count.fetch_add(1, std::memory_order_relaxed) + 1;
|
|
|
g_running.store(false, std::memory_order_relaxed);
|
|
|
+ if (n >= 2) {
|
|
|
+ _Exit(1);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void install_signal_handlers() {
|
|
|
@@ -147,6 +163,45 @@ std::string url_param(request* req, const std::string& key,
|
|
|
return def;
|
|
|
}
|
|
|
|
|
|
+std::string trim_ascii(const std::string& s) {
|
|
|
+ size_t b = 0;
|
|
|
+ while (b < s.size() && (s[b] == ' ' || s[b] == '\t')) {
|
|
|
+ ++b;
|
|
|
+ }
|
|
|
+ size_t e = s.size();
|
|
|
+ while (e > b && (s[e - 1] == ' ' || s[e - 1] == '\t')) {
|
|
|
+ --e;
|
|
|
+ }
|
|
|
+ return s.substr(b, e - b);
|
|
|
+}
|
|
|
+
|
|
|
+// Prefer proxy headers when present; otherwise TCP peer address.
|
|
|
+std::string request_client_ip(request* req) {
|
|
|
+ if (!req) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ std::string v;
|
|
|
+ if (req->header("X-Real-IP", v) || req->header("x-real-ip", v)) {
|
|
|
+ v = trim_ascii(v);
|
|
|
+ if (!v.empty()) {
|
|
|
+ return v;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (req->header("X-Forwarded-For", v) ||
|
|
|
+ req->header("x-forwarded-for", v)) {
|
|
|
+ const auto comma = v.find(',');
|
|
|
+ v = trim_ascii(comma == std::string::npos ? v : v.substr(0, comma));
|
|
|
+ if (!v.empty()) {
|
|
|
+ return v;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return trim_ascii(req->remote().address);
|
|
|
+ } catch (...) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
std::string b64_decode(const std::string& in) {
|
|
|
static const int8_t kTable[256] = {
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
|
@@ -450,6 +505,161 @@ void h_tasks_clear(request* req, response* resp) {
|
|
|
reply_ok(resp, data, "cleared");
|
|
|
}
|
|
|
|
|
|
+ylib::json schedule_job_to_json(const schedule::Job& j) {
|
|
|
+ ylib::json item;
|
|
|
+ item["id"] = j.id;
|
|
|
+ item["name"] = j.name;
|
|
|
+ item["enabled"] = j.enabled;
|
|
|
+ item["url"] = j.url;
|
|
|
+ item["method"] = j.method;
|
|
|
+ item["interval"] = j.interval;
|
|
|
+ item["unit"] = j.unit;
|
|
|
+ item["timeout_sec"] = j.timeout_sec;
|
|
|
+ item["continue_when_busy"] = j.continue_when_busy;
|
|
|
+ item["created_ms"] = static_cast<double>(j.created_ms);
|
|
|
+ item["next_run_ms"] = static_cast<double>(j.next_run_ms);
|
|
|
+ item["last_run_ms"] = static_cast<double>(j.last_run_ms);
|
|
|
+ item["last_ok"] = j.last_ok;
|
|
|
+ item["last_status"] = j.last_status;
|
|
|
+ item["last_error"] = j.last_error;
|
|
|
+ item["inflight"] = j.inflight;
|
|
|
+ item["interval_ms"] =
|
|
|
+ static_cast<double>(schedule::interval_to_ms(j.interval, j.unit));
|
|
|
+ return item;
|
|
|
+}
|
|
|
+
|
|
|
+ylib::json schedule_log_to_json(const schedule::LogEntry& e) {
|
|
|
+ ylib::json item;
|
|
|
+ item["id"] = static_cast<double>(e.id);
|
|
|
+ item["job_id"] = e.job_id;
|
|
|
+ item["time_ms"] = static_cast<double>(e.time_ms);
|
|
|
+ item["ok"] = e.ok;
|
|
|
+ item["status_code"] = e.status_code;
|
|
|
+ item["duration_ms"] = static_cast<double>(e.duration_ms);
|
|
|
+ item["error"] = e.error;
|
|
|
+ item["response_preview"] = e.response_preview;
|
|
|
+ return item;
|
|
|
+}
|
|
|
+
|
|
|
+void h_schedule_jobs(request* req, response* resp) {
|
|
|
+ if (req->method() == "GET") {
|
|
|
+ ylib::json arr;
|
|
|
+ for (const auto& j : schedule::list_jobs()) {
|
|
|
+ arr.push_back(schedule_job_to_json(j));
|
|
|
+ }
|
|
|
+ ylib::json data;
|
|
|
+ data["jobs"] = arr;
|
|
|
+ reply_ok(resp, data);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!require_method(req, resp, "POST")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const ylib::json body = parse_body(req);
|
|
|
+ schedule::Job job;
|
|
|
+ job.id = json_str(body, "id");
|
|
|
+ job.name = json_str(body, "name");
|
|
|
+ job.enabled = json_bool(body, "enabled", true);
|
|
|
+ job.url = json_str(body, "url");
|
|
|
+ job.method = json_str(body, "method", "GET");
|
|
|
+ job.interval = json_int(body, "interval", 60);
|
|
|
+ job.unit = json_str(body, "unit", "sec");
|
|
|
+ job.timeout_sec = json_int(body, "timeout_sec", 30);
|
|
|
+ job.continue_when_busy = json_bool(body, "continue_when_busy", false);
|
|
|
+ std::string err;
|
|
|
+ if (!schedule::upsert_job(job, err)) {
|
|
|
+ reply_err(resp, err, 400);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_ok(resp, schedule_job_to_json(job), "saved");
|
|
|
+}
|
|
|
+
|
|
|
+void h_schedule_jobs_delete(request* req, response* resp) {
|
|
|
+ if (!require_method(req, resp, "POST")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const ylib::json body = parse_body(req);
|
|
|
+ const std::string id = json_str(body, "id");
|
|
|
+ std::string err;
|
|
|
+ if (!schedule::delete_job(id, err)) {
|
|
|
+ reply_err(resp, err, 400);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_ok(resp, ylib::json(), "deleted");
|
|
|
+}
|
|
|
+
|
|
|
+void h_schedule_jobs_enable(request* req, response* resp) {
|
|
|
+ if (!require_method(req, resp, "POST")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const ylib::json body = parse_body(req);
|
|
|
+ const std::string id = json_str(body, "id");
|
|
|
+ const bool enabled = json_bool(body, "enabled", true);
|
|
|
+ std::string err;
|
|
|
+ if (!schedule::set_enabled(id, enabled, err)) {
|
|
|
+ reply_err(resp, err, 400);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ schedule::Job job;
|
|
|
+ if (!schedule::get_job(id, job, err)) {
|
|
|
+ reply_err(resp, err, 400);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_ok(resp, schedule_job_to_json(job), enabled ? "enabled" : "disabled");
|
|
|
+}
|
|
|
+
|
|
|
+void h_schedule_jobs_run(request* req, response* resp) {
|
|
|
+ if (!require_method(req, resp, "POST")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const ylib::json body = parse_body(req);
|
|
|
+ const std::string id = json_str(body, "id");
|
|
|
+ std::string err;
|
|
|
+ if (!schedule::run_now(id, err)) {
|
|
|
+ reply_err(resp, err, 400);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_ok(resp, ylib::json(), "started");
|
|
|
+}
|
|
|
+
|
|
|
+void h_schedule_logs(request* req, response* resp) {
|
|
|
+ if (req->method() == "GET") {
|
|
|
+ const std::string job_id = url_param(req, "job_id");
|
|
|
+ int limit = 80;
|
|
|
+ try {
|
|
|
+ limit = std::stoi(url_param(req, "limit", "80"));
|
|
|
+ } catch (...) {
|
|
|
+ limit = 80;
|
|
|
+ }
|
|
|
+ std::vector<schedule::LogEntry> logs;
|
|
|
+ std::string err;
|
|
|
+ if (!schedule::list_logs(job_id, limit, logs, err)) {
|
|
|
+ reply_err(resp, err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ylib::json arr;
|
|
|
+ for (const auto& e : logs) {
|
|
|
+ arr.push_back(schedule_log_to_json(e));
|
|
|
+ }
|
|
|
+ ylib::json data;
|
|
|
+ data["logs"] = arr;
|
|
|
+ reply_ok(resp, data);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!require_method(req, resp, "POST")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // clear logs
|
|
|
+ const ylib::json body = parse_body(req);
|
|
|
+ const std::string job_id = json_str(body, "job_id");
|
|
|
+ std::string err;
|
|
|
+ if (!schedule::clear_logs(job_id, err)) {
|
|
|
+ reply_err(resp, err, 400);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_ok(resp, ylib::json(), "cleared");
|
|
|
+}
|
|
|
+
|
|
|
void h_status(request* req, response* resp) {
|
|
|
if (!require_method(req, resp, "GET")) {
|
|
|
return;
|
|
|
@@ -879,6 +1089,8 @@ ylib::json site_to_json(const website::SiteInfo& s) {
|
|
|
item["ssl_cert"] = s.ssl_cert;
|
|
|
item["ssl_key"] = s.ssl_key;
|
|
|
item["ssl_port"] = s.ssl_port;
|
|
|
+ item["group_name"] = s.group_name;
|
|
|
+ item["sort_order"] = s.sort_order;
|
|
|
item["ssl_not_before"] = "";
|
|
|
item["ssl_not_after"] = "";
|
|
|
item["ssl_days_left"] = -1;
|
|
|
@@ -937,6 +1149,7 @@ void h_websites(request* req, response* resp) {
|
|
|
creq.listen_port = json_int(body, "listen_port", 0);
|
|
|
creq.upstream.clear();
|
|
|
creq.root = json_str(body, "root");
|
|
|
+ creq.group_name = json_str(body, "group_name");
|
|
|
if (creq.name.empty()) {
|
|
|
reply_err(resp, "name required");
|
|
|
return;
|
|
|
@@ -1067,6 +1280,8 @@ void h_websites_update(request* req, response* resp) {
|
|
|
ureq.ssl_cert = json_str(body, "ssl_cert");
|
|
|
ureq.ssl_key = json_str(body, "ssl_key");
|
|
|
ureq.ssl_port = json_int(body, "ssl_port", 443);
|
|
|
+ ureq.update_group = body.exist("group_name");
|
|
|
+ ureq.group_name = json_str(body, "group_name");
|
|
|
std::string err;
|
|
|
if (!website::update_site(ureq, err)) {
|
|
|
reply_err(resp, err.empty() ? "update site failed" : err);
|
|
|
@@ -1077,6 +1292,47 @@ void h_websites_update(request* req, response* resp) {
|
|
|
reply_ok(resp, site_to_json(updated), "site updated");
|
|
|
}
|
|
|
|
|
|
+void h_websites_reorder(request* req, response* resp) {
|
|
|
+ if (!require_method(req, resp, "POST")) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ auto body = parse_body(req);
|
|
|
+ if (!body.exist("items") || !body["items"].is_array()) {
|
|
|
+ reply_err(resp, "items array required");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ std::vector<website::SiteOrderItem> items;
|
|
|
+ const auto& arr = body["items"];
|
|
|
+ for (uint32 i = 0; i < arr.size(); ++i) {
|
|
|
+ const auto& j = arr[i];
|
|
|
+ website::SiteOrderItem it;
|
|
|
+ it.name = j.exist("name") ? j["name"].to<std::string>() : "";
|
|
|
+ it.group_name =
|
|
|
+ j.exist("group_name") ? j["group_name"].to<std::string>() : "";
|
|
|
+ try {
|
|
|
+ it.sort_order =
|
|
|
+ j.exist("sort_order") ? j["sort_order"].to<int>() : 0;
|
|
|
+ } catch (...) {
|
|
|
+ it.sort_order = 0;
|
|
|
+ }
|
|
|
+ if (it.name.empty()) {
|
|
|
+ reply_err(resp, "item.name required");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ items.push_back(it);
|
|
|
+ }
|
|
|
+ std::string err;
|
|
|
+ if (!website::reorder_sites(items, err)) {
|
|
|
+ reply_err(resp, err.empty() ? "reorder failed" : err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ylib::json out;
|
|
|
+ for (const auto& s : website::list_sites()) {
|
|
|
+ out.push_back(site_to_json(s));
|
|
|
+ }
|
|
|
+ reply_ok(resp, out, "sites reordered");
|
|
|
+}
|
|
|
+
|
|
|
ylib::json url_proxy_to_json(const website::UrlProxyRule& r) {
|
|
|
ylib::json item;
|
|
|
item["id"] = r.id;
|
|
|
@@ -1186,6 +1442,45 @@ void h_websites_proxies_delete(request* req, response* resp) {
|
|
|
reply_ok(resp, ylib::json(), "url proxy deleted");
|
|
|
}
|
|
|
|
|
|
+void h_websites_custom(request* req, response* resp) {
|
|
|
+ const std::string method = req->method();
|
|
|
+ if (method == "GET") {
|
|
|
+ const std::string name = url_param(req, "name");
|
|
|
+ if (name.empty()) {
|
|
|
+ reply_err(resp, "name required");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ std::string content;
|
|
|
+ std::string err;
|
|
|
+ if (!website::get_custom_nginx(name, content, err)) {
|
|
|
+ reply_err(resp, err.empty() ? "get custom config failed" : err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ylib::json data;
|
|
|
+ data["name"] = name;
|
|
|
+ data["content"] = content;
|
|
|
+ reply_ok(resp, data);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (method == "POST") {
|
|
|
+ auto body = parse_body(req);
|
|
|
+ const std::string name = json_str(body, "name");
|
|
|
+ if (name.empty()) {
|
|
|
+ reply_err(resp, "name required");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const std::string content = json_str(body, "content");
|
|
|
+ std::string err;
|
|
|
+ if (!website::set_custom_nginx(name, content, err)) {
|
|
|
+ reply_err(resp, err.empty() ? "save custom config failed" : err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_ok(resp, ylib::json(), "custom config saved");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_err(resp, "method not allowed", 405);
|
|
|
+}
|
|
|
+
|
|
|
void h_websites_logs(request* req, response* resp) {
|
|
|
if (!require_method(req, resp, "GET")) {
|
|
|
return;
|
|
|
@@ -1319,6 +1614,7 @@ void h_websites_analytics(request* req, response* resp) {
|
|
|
reply_err(resp, "invalid analytics payload");
|
|
|
return;
|
|
|
}
|
|
|
+ data["self_ip"] = request_client_ip(req);
|
|
|
reply_ok(resp, data);
|
|
|
}
|
|
|
|
|
|
@@ -1448,6 +1744,7 @@ void h_system_processes(request* req, response* resp) {
|
|
|
item["state"] = p.state;
|
|
|
item["name"] = p.name;
|
|
|
item["cmdline"] = p.cmdline;
|
|
|
+ item["cwd"] = p.cwd;
|
|
|
item["threads"] = p.threads;
|
|
|
item["rss"] = static_cast<int64>(p.rss_bytes);
|
|
|
item["vms"] = static_cast<int64>(p.vms_bytes);
|
|
|
@@ -1603,6 +1900,57 @@ void h_redis_stop(request* req, response* resp) {
|
|
|
reply_ok(resp, ylib::json(), "redis stopped");
|
|
|
}
|
|
|
|
|
|
+void h_redis_config(request* req, response* resp) {
|
|
|
+ const std::string method = req->method();
|
|
|
+ if (method == "GET") {
|
|
|
+ redis::QuickConfig cfg;
|
|
|
+ std::string err;
|
|
|
+ if (!redis::read_quick_config(cfg, err)) {
|
|
|
+ reply_err(resp, err.empty() ? "read redis config failed" : err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ylib::json data;
|
|
|
+ data["bind"] = cfg.bind;
|
|
|
+ data["port"] = cfg.port;
|
|
|
+ data["requirepass"] = cfg.requirepass;
|
|
|
+ data["conf"] = redis::conf_file();
|
|
|
+ data["running"] = redis::is_running();
|
|
|
+ reply_ok(resp, data);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (method == "POST") {
|
|
|
+ auto body = parse_body(req);
|
|
|
+ redis::QuickConfig cfg;
|
|
|
+ std::string err;
|
|
|
+ if (!redis::read_quick_config(cfg, err)) {
|
|
|
+ reply_err(resp, err.empty() ? "read redis config failed" : err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (body.exist("bind")) {
|
|
|
+ cfg.bind = json_str(body, "bind", cfg.bind);
|
|
|
+ }
|
|
|
+ if (body.exist("port")) {
|
|
|
+ cfg.port = json_int(body, "port", cfg.port);
|
|
|
+ }
|
|
|
+ if (body.exist("requirepass")) {
|
|
|
+ cfg.requirepass = json_str(body, "requirepass");
|
|
|
+ }
|
|
|
+ if (!redis::apply_quick_config(cfg, err)) {
|
|
|
+ reply_err(resp, err.empty() ? "apply redis config failed" : err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ylib::json data;
|
|
|
+ data["bind"] = cfg.bind;
|
|
|
+ data["port"] = cfg.port;
|
|
|
+ data["requirepass"] = cfg.requirepass;
|
|
|
+ data["conf"] = redis::conf_file();
|
|
|
+ data["running"] = redis::is_running();
|
|
|
+ reply_ok(resp, data, "redis config updated");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ reply_err(resp, "method not allowed", 405);
|
|
|
+}
|
|
|
+
|
|
|
void h_files_list(request* req, response* resp) {
|
|
|
if (!require_method(req, resp, "GET")) {
|
|
|
return;
|
|
|
@@ -1817,6 +2165,12 @@ void register_routes(ylib::network::http::router* router) {
|
|
|
reg(router, "/api/tasks/log", h_tasks_log);
|
|
|
reg(router, "/api/tasks/clear", h_tasks_clear);
|
|
|
|
|
|
+ reg(router, "/api/schedule/jobs", h_schedule_jobs);
|
|
|
+ reg(router, "/api/schedule/jobs/delete", h_schedule_jobs_delete);
|
|
|
+ reg(router, "/api/schedule/jobs/enable", h_schedule_jobs_enable);
|
|
|
+ reg(router, "/api/schedule/jobs/run", h_schedule_jobs_run);
|
|
|
+ reg(router, "/api/schedule/logs", h_schedule_logs);
|
|
|
+
|
|
|
reg(router, "/api/nginx/status", h_nginx_status);
|
|
|
reg(router, "/api/nginx/install", h_nginx_install);
|
|
|
reg(router, "/api/nginx/uninstall", h_nginx_uninstall);
|
|
|
@@ -1846,6 +2200,7 @@ void register_routes(ylib::network::http::router* router) {
|
|
|
reg(router, "/api/redis/uninstall", h_redis_uninstall);
|
|
|
reg(router, "/api/redis/start", h_redis_start);
|
|
|
reg(router, "/api/redis/stop", h_redis_stop);
|
|
|
+ reg(router, "/api/redis/config", h_redis_config);
|
|
|
|
|
|
reg(router, "/api/websites", h_websites);
|
|
|
reg(router, "/api/websites/start", h_websites_start);
|
|
|
@@ -1853,9 +2208,11 @@ void register_routes(ylib::network::http::router* router) {
|
|
|
reg(router, "/api/websites/restart", h_websites_restart);
|
|
|
reg(router, "/api/websites/delete", h_websites_delete);
|
|
|
reg(router, "/api/websites/update", h_websites_update);
|
|
|
+ reg(router, "/api/websites/reorder", h_websites_reorder);
|
|
|
reg(router, "/api/websites/proxies", h_websites_proxies);
|
|
|
reg(router, "/api/websites/proxies/update", h_websites_proxies_update);
|
|
|
reg(router, "/api/websites/proxies/delete", h_websites_proxies_delete);
|
|
|
+ reg(router, "/api/websites/custom", h_websites_custom);
|
|
|
reg(router, "/api/websites/logs", h_websites_logs);
|
|
|
reg(router, "/api/websites/analytics", h_websites_analytics);
|
|
|
reg(router, "/api/websites/ssl/apply", h_websites_ssl_apply);
|
|
|
@@ -1947,6 +2304,7 @@ bool run(const std::string& listen_addr, uint16_t listen_port) {
|
|
|
tasks::start();
|
|
|
weblog::start();
|
|
|
system::history_start();
|
|
|
+ schedule::start();
|
|
|
log_info("ngs apiserver starting listen=" + listen_addr + ":" +
|
|
|
std::to_string(listen_port));
|
|
|
|
|
|
@@ -1955,6 +2313,17 @@ bool run(const std::string& listen_addr, uint16_t listen_port) {
|
|
|
auto* center = new ylib::network::http::center();
|
|
|
g_center = center;
|
|
|
|
|
|
+ auto fail_cleanup = [&]() {
|
|
|
+ // Stop workers first so metrics/tasks do not keep running if close hangs.
|
|
|
+ stop_background_services();
|
|
|
+ if (center) {
|
|
|
+ center->close();
|
|
|
+ delete center;
|
|
|
+ center = nullptr;
|
|
|
+ }
|
|
|
+ g_center = nullptr;
|
|
|
+ };
|
|
|
+
|
|
|
ylib::network::http::start_config config;
|
|
|
ylib::network::http::website_config ws_config;
|
|
|
ws_config.name = "ngs-api";
|
|
|
@@ -1973,8 +2342,7 @@ bool run(const std::string& listen_addr, uint16_t listen_port) {
|
|
|
if (!center->create(config)) {
|
|
|
std::cerr << "apiserver create failed: " << center->last_error() << "\n";
|
|
|
log_error("apiserver create failed: " + center->last_error());
|
|
|
- delete center;
|
|
|
- g_center = nullptr;
|
|
|
+ fail_cleanup();
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
@@ -1982,9 +2350,7 @@ bool run(const std::string& listen_addr, uint16_t listen_port) {
|
|
|
if (!website || !website->router()) {
|
|
|
std::cerr << "apiserver website/router missing\n";
|
|
|
log_error("apiserver website/router missing");
|
|
|
- center->close();
|
|
|
- delete center;
|
|
|
- g_center = nullptr;
|
|
|
+ fail_cleanup();
|
|
|
return false;
|
|
|
}
|
|
|
register_routes(website->router());
|
|
|
@@ -1992,9 +2358,7 @@ bool run(const std::string& listen_addr, uint16_t listen_port) {
|
|
|
if (!center->start()) {
|
|
|
std::cerr << "apiserver start failed: " << center->last_error() << "\n";
|
|
|
log_error("apiserver start failed: " + center->last_error());
|
|
|
- center->close();
|
|
|
- delete center;
|
|
|
- g_center = nullptr;
|
|
|
+ fail_cleanup();
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
@@ -2016,9 +2380,7 @@ bool run(const std::string& listen_addr, uint16_t listen_port) {
|
|
|
|
|
|
std::cout << "Shutting down...\n";
|
|
|
log_info("ngs apiserver stopping");
|
|
|
- weblog::stop();
|
|
|
- system::history_stop();
|
|
|
- tasks::stop();
|
|
|
+ stop_background_services();
|
|
|
center->close();
|
|
|
delete center;
|
|
|
g_center = nullptr;
|