3#include <freertos/FreeRTOS.h>
4#include <freertos/task.h>
33bool check_origin(httpd_req_t* req) {
34 if (httpd_req_get_hdr_value_len(req,
"Origin") == 0) {
38 char origin[128] = {0};
40 if (httpd_req_get_hdr_value_str(req,
"Origin", origin,
sizeof(origin)) !=
42 httpd_req_get_hdr_value_str(req,
"Host", host,
sizeof(host)) != ESP_OK) {
43 httpd_resp_send_err(req, HTTPD_403_FORBIDDEN,
44 "Cross-origin request rejected");
48 String origin_str(origin);
49 int scheme_end = origin_str.indexOf(
"://");
50 String origin_authority =
51 scheme_end < 0 ? origin_str : origin_str.substring(scheme_end + 3);
53 if (origin_authority == host) {
57 httpd_resp_send_err(req, HTTPD_403_FORBIDDEN,
58 "Cross-origin request rejected");
65 auto reset_handler = std::make_shared<HTTPRequestHandler>(
66 1 << HTTP_POST,
"/api/device/reset", [](httpd_req_t* req) {
67 if (!check_origin(req)) {
71 "Resetting device back to factory defaults. "
72 "You may have to reconfigure the WiFi settings.",
77 server->add_handler(reset_handler);
81 auto restart_handler = std::make_shared<HTTPRequestHandler>(
82 1 << HTTP_POST,
"/api/device/restart", [](httpd_req_t* req) {
83 if (!check_origin(req)) {
86 httpd_resp_send(req,
"Restarting device", 0);
87 event_loop()->onDelay(500, []() { ESP.restart(); });
90 server->add_handler(restart_handler);
94 auto info_handler = std::make_shared<HTTPRequestHandler>(
95 1 << HTTP_GET,
"/api/info", [](httpd_req_t* req) {
98 JsonDocument json_doc;
99 JsonArray info_items = json_doc.to<JsonArray>();
101 for (
auto info_item = status_page_items->begin();
102 info_item != status_page_items->end(); ++info_item) {
103 info_items.add(info_item->second->as_json());
110#if defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) && \
111 CONFIG_FREERTOS_USE_TRACE_FACILITY
115 UBaseType_t task_slots = uxTaskGetNumberOfTasks() + 4;
116 std::unique_ptr<TaskStatus_t[]> tasks(
117 new (std::nothrow) TaskStatus_t[task_slots]);
119 UBaseType_t n = uxTaskGetSystemState(tasks.get(), task_slots,
nullptr);
120 for (UBaseType_t i = 0; i < n; i++) {
122 item[
"name"] = tasks[i].pcTaskName;
123 item[
"value"] =
static_cast<uint32_t
>(
124 tasks[i].usStackHighWaterMark *
sizeof(StackType_t));
125 item[
"group"] =
"Task stack free (bytes)";
127 info_items.add(item);
133 serializeJson(json_doc, response);
134 httpd_resp_set_type(req,
"application/json");
135 httpd_resp_sendstr(req, response.c_str());
138 server->add_handler(info_handler);
142 auto log_handler = std::make_shared<HTTPRequestHandler>(
143 1 << HTTP_GET,
"/api/log", [](httpd_req_t* req) {
145 httpd_resp_set_type(req,
"application/json; charset=utf-8");
146 if (log_buffer ==
nullptr) {
148 req,
"{\"session\":0,\"next\":0,\"gap\":false,\"lines\":[]}");
158 bool has_since =
false;
159 size_t query_len = httpd_req_get_url_query_len(req) + 1;
160 if (query_len > 1 && query_len <= 64) {
162 if (httpd_req_get_url_query_str(req, query,
sizeof(query)) == ESP_OK) {
164 if (httpd_query_key_value(query,
"since", value,
sizeof(value)) ==
170 unsigned long parsed = strtoul(value, &end, 10);
171 if (end != value && *end ==
'\0' && errno == 0 &&
172 parsed <= UINT32_MAX) {
173 since =
static_cast<uint32_t
>(parsed);
182 JsonDocument json_doc;
184 json_doc[
"next"] = snapshot.
next;
185 json_doc[
"gap"] = snapshot.
gap;
186 JsonArray lines = json_doc[
"lines"].to<JsonArray>();
187 for (
const auto& line : snapshot.
lines) {
188 lines.add(line.c_str());
192 serializeJson(json_doc, response);
193 httpd_resp_sendstr(req, response.c_str());
196 server->add_handler(log_handler);
200 std::vector<RouteDefinition> routes;
206 routes.push_back(
RouteDefinition(
"Signal K",
"/signalk",
"SignalKPage"));
208 RouteDefinition(
"Configuration",
"/configuration",
"ConfigurationPage"));
211 JsonDocument json_doc;
212 JsonArray routes_json = json_doc.to<JsonArray>();
214 for (
auto it = routes.begin(); it != routes.end(); ++it) {
215 routes_json.add(it->as_json());
220 serializeJson(routes_json, response);
222 auto routes_handler = std::make_shared<HTTPRequestHandler>(
223 1 << HTTP_GET,
"/api/routes", [response](httpd_req_t* req) {
224 httpd_resp_set_type(req,
"application/json");
225 httpd_resp_sendstr(req, response.c_str());
228 server->add_handler(routes_handler);
239 if (root_page ==
nullptr) {
240 ESP_LOGE(__FILENAME__,
"Root page not found in kWebUIFiles");
246 for (
auto it = routes.begin(); it != routes.end(); ++it) {
247 String path = it->get_path();
248 auto route_handler = std::make_shared<HTTPRequestHandler>(
249 1 << HTTP_GET, path.c_str(), [root_page](httpd_req_t* req) {
250 httpd_resp_set_type(req, root_page->content_type);
251 if (root_page->content_encoding != nullptr) {
252 httpd_resp_set_hdr(req, kContentEncoding,
253 root_page->content_encoding);
258 server->add_handler(route_handler);
Captures ESP_LOGx output into a bounded RAM buffer for the web UI.
LogSnapshot snapshot_since(uint32_t since, bool has_since, uint32_t now_ms)
Return retained lines newer than since.
static LogBuffer * instance()
Accessor used by the static vprintf trampoline.
static const std::shared_ptr< SensESPBaseApp > & get()
Get the singleton instance of the SensESPBaseApp.
static const std::map< String, StatusPageItemBase * > * get_status_page_items()
std::shared_ptr< reactesp::EventLoop > event_loop()
void add_http_info_handler(std::shared_ptr< HTTPServer > &server)
constexpr int kUIOutputDefaultOrder
const StaticFileData kFrontendFiles[]
void add_http_restart_handler(std::shared_ptr< HTTPServer > &server)
void add_http_reset_handler(std::shared_ptr< HTTPServer > &server)
void add_http_log_handler(std::shared_ptr< HTTPServer > &server)
void add_base_app_http_command_handlers(std::shared_ptr< HTTPServer > &server)
void add_routes_handlers(std::shared_ptr< HTTPServer > &server)
Result of a snapshot_since() query, ready to serialize for the web UI.
std::vector< std::string > lines
const unsigned int content_length