рефактор статических файлов, добавлено удобное API и поддержка кеширования

This commit is contained in:
2024-11-04 14:17:34 +03:00
parent 82b433c447
commit 0b794fac40
6 changed files with 217 additions and 117 deletions

View File

@@ -16,6 +16,7 @@
#include <fstream>
#include "terminal_api_driver.h"
#include "auth/resources.h"
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
@@ -72,70 +73,124 @@ void init_logging() {
log::add_common_attributes();
}
static void initResources(http::server::Server& s, std::shared_ptr<api_driver::ApiDriver>& api) {
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/", "static/main.html", mime_types::text_html));
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/login", "static/login.html", mime_types::text_html));
class ServerResources {
std::unique_ptr<http::resource::StaticFileFactory> sf;
std::unique_ptr<api_driver::ApiDriver> api;
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/favicon.ico", "static/favicon.png", mime_types::image_png));
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/images/krokodil_vzryvaetsya_hd.gif", "static/krokodil.gif", mime_types::image_gif));
public:
static constexpr const char* INDEX_HTML = "static/main.html";
static constexpr const char* LOGIN_HTML = "static/login.html";
static constexpr const char* LOGIN_FAILED_HTML = "static/login-failed.html";
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/style.css", "static/style.css", mime_types::text_css));
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/fields.css", "static/fields.css", mime_types::text_css));
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/js/vue.js", "static/js/vue.js", mime_types::javascript));
// картинки, их даже можно кешировать
static constexpr const char* FAVICON_ICO = "static/favicon.png";
static constexpr const char* KROKODIL_GIF = "static/krokodil.gif";
static constexpr const char* VUE_JS = "static/js/vue.js"; // это тоже можно кешировать
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/statistics", [](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
// а эти стили нельзя кешировать в отладочной версии
static constexpr const char* STYLE_CSS = "static/style.css";
static constexpr const char* FIELDS_CSS = "static/fields.css";
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const char* json = R"({"key":"value"})";
rep.content.insert(rep.content.end(), json, json + strlen(json));
}));
ServerResources(const ServerResources&) = delete;
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/statistics", [api](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
ServerResources(): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
sf->registerFile(INDEX_HTML, mime_types::text_html, false);
sf->registerFile(LOGIN_HTML, mime_types::text_html, false);
sf->registerFile(LOGIN_FAILED_HTML, mime_types::text_html, false);
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());
}));
sf->registerFile(FAVICON_ICO, mime_types::image_png, true);
sf->registerFile(KROKODIL_GIF, mime_types::image_gif, true);
sf->registerFile(VUE_JS, mime_types::javascript, true);
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/resetPacketStatistics", [api](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
api->resetPacketStatistics();
#if USE_DEBUG
constexpr bool allowCacheCss = false;
#else
constexpr bool allowCacheCss = true;
#endif
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());
}));
sf->registerFile(STYLE_CSS, mime_types::text_css, allowCacheCss);
sf->registerFile(FIELDS_CSS, mime_types::text_css, allowCacheCss);
}
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/settings", [api](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
void registerResources(http::server::Server& s) {
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/", [this](const auto& req, auto& rep) {
boost::ignore_unused(req);
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") {
sf->serve(LOGIN_HTML, rep);
} else if (req.method == "POST") {
sf->serve(LOGIN_FAILED_HTML, rep);
} 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>("/images/krokodil_vzryvaetsya_hd.gif", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(KROKODIL_GIF, 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>("/js/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>("/api/statistics", [](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 char* json = R"({"key":"value"})";
rep.content.insert(rep.content.end(), json, json + strlen(json));
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/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::resource::GenericResource>("/api/resetPacketStatistics", [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::resource::GenericResource>("/api/get/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());
}));
}
~ServerResources() = default;
};
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());
}));
}
int main(int argc, char *argv[]) {
try {
@@ -159,14 +214,14 @@ int main(int argc, char *argv[]) {
BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0];
#endif
auto api = std::make_shared<api_driver::ApiDriver>();
ServerResources resources;
// 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]);
initResources(*s, api);
resources.registerResources(*s);
s->run();
} else if (strcmp(argv[1], "ssl") == 0) {
@@ -187,7 +242,7 @@ int main(int argc, char *argv[]) {
ctx->use_tmp_dh(boost::asio::buffer(dh));
s = std::make_unique<http::server::Server>(argv[2], argv[3], ctx);
initResources(*s, api);
resources.registerResources(*s);
s->run();
} else {
std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl;