570 lines
26 KiB
C++
570 lines
26 KiB
C++
#include <iostream>
|
||
#include <string>
|
||
#include <sys/prctl.h>
|
||
#include <boost/asio.hpp>
|
||
#include "server/server.hpp"
|
||
#include <boost/log/trivial.hpp>
|
||
#include <boost/log/expressions.hpp>
|
||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||
#include <boost/log/utility/setup/console.hpp>
|
||
#include <boost/log/utility/setup/file.hpp>
|
||
#include <boost/log/utility/setup/formatter_parser.hpp>
|
||
#include <boost/asio/buffer.hpp>
|
||
#include <boost/asio/ssl/context.hpp>
|
||
#include <boost/property_tree/json_parser.hpp>
|
||
#include <cstddef>
|
||
#include <memory>
|
||
#include <fstream>
|
||
|
||
#include "terminal_api_driver.h"
|
||
#include "auth/resources.h"
|
||
#include "auth/jwt.h"
|
||
#include "auth/utils.h"
|
||
|
||
|
||
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
|
||
|
||
constexpr const char* REBOOT_COMMAND = "web-action reboot";
|
||
constexpr const char* UPGRADE_COMMAND = "web-action upgrade";
|
||
|
||
|
||
static std::vector<char> loadFile(const std::string& path) {
|
||
std::ifstream is(path, std::ios::in | std::ios::binary);
|
||
if (!is) {
|
||
throw std::runtime_error("File not found");
|
||
}
|
||
|
||
std::vector<char> content;
|
||
for (;;) {
|
||
char buf[512];
|
||
auto len = is.read(buf, sizeof(buf)).gcount();
|
||
if (len <= 0) {
|
||
break;
|
||
}
|
||
content.insert(content.end(), buf, buf + len);
|
||
}
|
||
return content;
|
||
}
|
||
|
||
|
||
namespace mime_types = http::server::mime_types;
|
||
|
||
void init_logging() {
|
||
namespace log = boost::log;
|
||
namespace keywords = log::keywords;
|
||
namespace expressions = log::expressions;
|
||
namespace attributes = log::attributes;
|
||
|
||
log::register_simple_formatter_factory<log::trivial::severity_level, char>("Severity");
|
||
|
||
// #ifdef USE_DEBUG
|
||
// log::add_console_log(std::clog, keywords::format = "%TimeStamp%: [%Severity%] %Message% [%ThreadID%]");
|
||
// #else
|
||
// log::add_file_log(
|
||
// keywords::file_name = "/home/root/manager_orlik_%N.log",
|
||
// keywords::rotation_size = 10 * 1024 * 1024,
|
||
// keywords::time_based_rotation = log::sinks::file::rotation_at_time_point(0, 0, 0),
|
||
// keywords::format = expressions::format("%1% [%2%] [%3%] <%4%> [%5%]")
|
||
// % expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d, %H:%M:%S.%f")
|
||
// % expressions::format_named_scope("Scope", keywords::format = "%n (%f:%l)")
|
||
// % expressions::attr<log::trivial::severity_level>("Severity")
|
||
// % expressions::message % expressions::attr<attributes::current_thread_id::value_type>("ThreadID"),
|
||
// keywords::open_mode = std::ios_base::app,
|
||
// keywords::auto_flush = true
|
||
// );
|
||
// #endif
|
||
log::add_console_log(std::clog, keywords::format = "%TimeStamp%: [%Severity%] %Message% [%ThreadID%]");
|
||
log::core::get()->set_filter(log::trivial::severity >= log::trivial::info);
|
||
|
||
log::add_common_attributes();
|
||
}
|
||
|
||
class ServerResources {
|
||
std::unique_ptr<http::resource::StaticFileFactory> sf;
|
||
std::unique_ptr<api_driver::ApiDriver> api;
|
||
http::auth::AuthProvider auth{};
|
||
|
||
bool upgradeOrRebootRunning = false;
|
||
|
||
static void onUploadFirmware(const http::server::Request& req) {
|
||
std::ofstream f("/tmp/firmware.zip", std::ios::binary);
|
||
|
||
if (f.is_open()) {
|
||
f.write(req.payload.data(), static_cast<long>(req.payload.size()));
|
||
f.close();
|
||
} else {
|
||
throw std::runtime_error("File is not open");
|
||
}
|
||
}
|
||
|
||
void doTerminalUpgrade() const {
|
||
api->executeInApi([](TSID sid) {
|
||
CP_SetDmaDebug(sid, "begin_save_config", "");
|
||
system(UPGRADE_COMMAND);
|
||
CP_SetDmaDebug(sid, "save_config", "");
|
||
});
|
||
}
|
||
|
||
public:
|
||
#if defined(MODEM_IS_TDMA)
|
||
static constexpr const char* INDEX_HTML = "/main-tdma.html";
|
||
#elif defined(MODEM_IS_SCPC)
|
||
static constexpr const char* INDEX_HTML = "/main-scpc.html";
|
||
#else
|
||
#error "Modem type not defined!"
|
||
#endif
|
||
static constexpr const char* LOGIN_HTML = "/login.html";
|
||
|
||
// картинки, их даже можно кешировать
|
||
static constexpr const char* FAVICON_ICO = "/favicon.ico";
|
||
static constexpr const char* VUE_JS = "/js/vue.js"; // это тоже можно кешировать
|
||
|
||
// а эти стили нельзя кешировать в отладочной версии
|
||
static constexpr const char* STYLE_CSS = "/style.css";
|
||
static constexpr const char* FIELDS_CSS = "/fields.css";
|
||
static constexpr const char* INTERNET_JPG = "/internet.jpg";
|
||
|
||
ServerResources(const ServerResources&) = delete;
|
||
|
||
explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
|
||
api->startDaemon();
|
||
auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "", http::auth::User::SUPERUSER));
|
||
|
||
sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true);
|
||
sf->registerFile(staticFilesPath + VUE_JS, VUE_JS, mime_types::javascript, true);
|
||
sf->registerFile(staticFilesPath + STYLE_CSS, STYLE_CSS, mime_types::text_css, true);
|
||
sf->registerFile(staticFilesPath + FIELDS_CSS, FIELDS_CSS, mime_types::text_css, true);
|
||
sf->registerFile(staticFilesPath + INDEX_HTML, INDEX_HTML, mime_types::text_html, false);
|
||
sf->registerFile(staticFilesPath + LOGIN_HTML, LOGIN_HTML, mime_types::text_html, true);
|
||
sf->registerFile(staticFilesPath + INTERNET_JPG, INTERNET_JPG, mime_types::image_jpeg, true);
|
||
}
|
||
|
||
void registerResources(http::server::Server& s) {
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/", [this](const auto& req, auto& rep) {
|
||
auto user = auth.getSession(req);
|
||
if (user == nullptr) {
|
||
http::server::httpRedirect(rep, "/login");
|
||
} else {
|
||
sf->serve(INDEX_HTML, rep);
|
||
}
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/login", [this](const auto& req, auto& rep) {
|
||
if (req.method == "GET") {
|
||
auto user = auth.getSession(req);
|
||
if (user == nullptr) {
|
||
sf->serve(LOGIN_HTML, rep);
|
||
} else {
|
||
http::server::httpRedirect(rep, "/");
|
||
}
|
||
} else if (req.method == "POST") {
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
try {
|
||
std::istringstream is(std::string(req.payload.data(), req.payload.size()));
|
||
boost::property_tree::ptree pt;
|
||
read_json(is, pt);
|
||
|
||
auto u = auth.doAuth(pt.get<std::string>("username"), pt.get<std::string>("password"), rep);
|
||
if (u == nullptr) {
|
||
throw std::runtime_error("invalid session");
|
||
}
|
||
std::string result = R"({"redirect":"/"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
} catch (std::exception &e) {
|
||
BOOST_LOG_TRIVIAL(error) << e.what() << std::endl;
|
||
std::string result = R"({"error":"Неверный логин или пароль"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}
|
||
} else {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
}));
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/logout", [](const auto& req, auto& rep) {
|
||
if (req.method == "GET") {
|
||
http::server::httpRedirect(rep, "/login");
|
||
rep.headers.push_back({.name = "Set-Cookie", .value = http::auth::jwt::EMPTY_AUTH_COOKIE});
|
||
} else {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(FAVICON_ICO, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FAVICON_ICO, rep); }));
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(STYLE_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_CSS, rep); }));
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(FIELDS_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); }));
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(VUE_JS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); }));
|
||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(INTERNET_JPG, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(INTERNET_JPG, rep); }));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "GET") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
std::string result = R"({"mainState":)";
|
||
result += api->loadTerminalState();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/settings", this->auth, http::auth::User::WATCH_SETTINGS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "GET") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
std::string result = R"({"settings":)";
|
||
result += api->loadSettings();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/aboutFirmware", this->auth, 0, [this](const auto& req, auto& rep) {
|
||
if (req.method != "GET") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
const auto result = api->loadFirmwareVersion();
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetPacketStatistics", this->auth, http::auth::User::RESET_PACKET_STATISTICS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
api->resetPacketStatistics();
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
const std::string result = R"({"status":"ok")";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/qos", this->auth, http::auth::User::SETUP_QOS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
|
||
try {
|
||
std::stringstream ss;
|
||
ss.str(std::string(req.payload.begin(), req.payload.end()));
|
||
boost::property_tree::ptree pt;
|
||
read_json(ss, pt);
|
||
|
||
api->setQosSettings(pt);
|
||
|
||
std::string result = R"({"status":"ok","settings":)";
|
||
result += api->loadSettings();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/qos): Can't set QoS settings: " << e.what();
|
||
const std::string result = R"({"status":"error"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/bucLnb", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
|
||
try {
|
||
std::stringstream ss;
|
||
ss.str(std::string(req.payload.begin(), req.payload.end()));
|
||
boost::property_tree::ptree pt;
|
||
read_json(ss, pt);
|
||
|
||
api->setBucLnbSettings(pt);
|
||
|
||
std::string result = R"({"status":"ok","settings":)";
|
||
result += api->loadSettings();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/bucLnb): Can't set settings: " << e.what();
|
||
const std::string result = R"({"status":"error"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}
|
||
}));
|
||
#ifdef MODEM_IS_SCPC
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/cinc", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
|
||
try {
|
||
std::stringstream ss;
|
||
ss.str(std::string(req.payload.begin(), req.payload.end()));
|
||
boost::property_tree::ptree pt;
|
||
read_json(ss, pt);
|
||
|
||
api->setCincSettings(pt);
|
||
|
||
std::string result = R"({"status":"ok","settings":)";
|
||
result += api->loadSettings();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/cinc): Can't set CinC settings: " << e.what();
|
||
const std::string result = R"({"status":"error"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}
|
||
}));
|
||
#endif
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/rxtx", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
|
||
try {
|
||
std::stringstream ss;
|
||
ss.str(std::string(req.payload.begin(), req.payload.end()));
|
||
boost::property_tree::ptree pt;
|
||
read_json(ss, pt);
|
||
|
||
api->setRxTxSettings(pt);
|
||
|
||
std::string result = R"({"status":"ok","settings":)";
|
||
result += api->loadSettings();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
|
||
const std::string result = R"({"status":"error"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/network", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
|
||
try {
|
||
std::stringstream ss;
|
||
ss.str(std::string(req.payload.begin(), req.payload.end()));
|
||
boost::property_tree::ptree pt;
|
||
read_json(ss, pt);
|
||
|
||
api->setNetworkSettings(pt);
|
||
|
||
std::string result = R"({"status":"ok","settings":)";
|
||
result += api->loadSettings();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
|
||
const std::string result = R"({"status":"error"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/debugSend", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
|
||
try {
|
||
std::stringstream ss;
|
||
ss.str(std::string(req.payload.begin(), req.payload.end()));
|
||
boost::property_tree::ptree pt;
|
||
read_json(ss, pt);
|
||
|
||
api->setDebugSendSettings(pt);
|
||
|
||
std::string result = R"({"status":"ok","settings":)";
|
||
result += api->loadSettings();
|
||
result += "}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
|
||
const std::string result = R"({"status":"error"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/reboot", this->auth, 0, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
const std::string result = R"({"status":"ok"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
this->upgradeOrRebootRunning = true;
|
||
system(REBOOT_COMMAND);
|
||
}));
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
const std::string result = R"({"status":"ok"})";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
api->resetDefaultSettings();
|
||
system(REBOOT_COMMAND);
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/firmwareUpdate", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) {
|
||
if (req.method != "PUT") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
this->upgradeOrRebootRunning = true;
|
||
onUploadFirmware(req);
|
||
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
std::string result = R"({"status":"ok","fwsize":)";
|
||
result += std::to_string(req.payload.size());
|
||
result += R"(,"sha256":")";
|
||
result += http::utils::sha256(req.payload.data(), req.payload.size());
|
||
result += "\"}";
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
this->upgradeOrRebootRunning = false;
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/doFirmwareUpgrade", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) {
|
||
if (req.method != "POST") {
|
||
http::server::stockReply(http::server::bad_request, rep);
|
||
}
|
||
this->upgradeOrRebootRunning = true;
|
||
doTerminalUpgrade();
|
||
rep.status = http::server::ok;
|
||
rep.headers.clear();
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
const auto result = api->loadFirmwareVersion();
|
||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
|
||
boost::ignore_unused(req);
|
||
sf->serve(INTERNET_JPG, rep);
|
||
}));
|
||
|
||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/fetchParams", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
|
||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||
std::string result = R"({"status":"ok","fwsize":)";
|
||
result += std::to_string(req.payload.size());
|
||
result += R"(,"sha256":")";
|
||
result += "\"}";
|
||
}));
|
||
}
|
||
|
||
~ServerResources() = default;
|
||
};
|
||
|
||
|
||
int main(int argc, char *argv[]) {
|
||
try {
|
||
prctl(PR_SET_NAME, "main", 0, 0, 0);
|
||
// Check command line arguments.
|
||
if (argc != 4 && argc != 5) {
|
||
std::cerr << "Usage: http_server <ssl|nossl> <address> <port> [static files directory]\n";
|
||
std::cerr << " For IPv4, try:\n";
|
||
std::cerr << " receiver nossl 0.0.0.0 80 .\n";
|
||
std::cerr << " For IPv6, try:\n";
|
||
std::cerr << " receiver nossl 0::0 80 .\n";
|
||
return 1;
|
||
}
|
||
|
||
init_logging();
|
||
boost::log::core::get()->add_thread_attribute("Scope", boost::log::attributes::named_scope());
|
||
|
||
#ifdef USE_DEBUG
|
||
BOOST_LOG_TRIVIAL(info) << "Starting DEBUG " << argv[0];
|
||
#else
|
||
BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0];
|
||
#endif
|
||
|
||
#ifdef USE_DEBUG
|
||
http::auth::jwt::secretKey = "^}u'ZKyQ%;+:lnh^GS7!=G~nRK?7[{``";
|
||
BOOST_LOG_TRIVIAL(info) << "DEBUG build use pre-created key " << http::auth::jwt::secretKey;
|
||
#else
|
||
http::auth::jwt::generateSecretKey();
|
||
BOOST_LOG_TRIVIAL(info) << "Generated new secret key " << http::auth::jwt::secretKey;
|
||
#endif
|
||
|
||
const std::string staticFilesPath = (argc == 5 ? argv[4]: ".");
|
||
BOOST_LOG_TRIVIAL(info) << "Use static files path: " << staticFilesPath << "/";
|
||
ServerResources resources(staticFilesPath);
|
||
|
||
// Initialise the server.
|
||
std::unique_ptr<http::server::Server> s;
|
||
|
||
if (strcmp(argv[1], "nossl") == 0) {
|
||
s = std::make_unique<http::server::Server>(argv[2], argv[3]);
|
||
resources.registerResources(*s);
|
||
s->run();
|
||
|
||
} else if (strcmp(argv[1], "ssl") == 0) {
|
||
const auto cert = loadFile("cert.pem");
|
||
const auto key = loadFile("key.pem");
|
||
const auto dh = loadFile("dh.pem");
|
||
|
||
auto ctx = std::make_shared<ssl::context>(ssl::context::tlsv12);
|
||
|
||
ctx->set_password_callback(
|
||
[](std::size_t, ssl::context_base::password_purpose) {
|
||
return "test";
|
||
});
|
||
|
||
ctx->set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::single_dh_use);
|
||
ctx->use_certificate_chain(boost::asio::buffer(cert));
|
||
ctx->use_private_key(boost::asio::buffer(key), ssl::context::file_format::pem);
|
||
ctx->use_tmp_dh(boost::asio::buffer(dh));
|
||
|
||
s = std::make_unique<http::server::Server>(argv[2], argv[3], ctx);
|
||
resources.registerResources(*s);
|
||
s->run();
|
||
} else {
|
||
std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl;
|
||
return 1;
|
||
}
|
||
} catch (std::exception &e) {
|
||
BOOST_LOG_TRIVIAL(error) << e.what() << std::endl;
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|