5#include <freertos/FreeRTOS.h>
6#include <freertos/task.h>
23String strip_port(
const String& authority) {
24 int colon = authority.lastIndexOf(
':');
25 return colon < 0 ? authority : authority.substring(0, colon);
28bool is_own_interface_ip(
const IPAddress& ip) {
31 if (ip == IPAddress(0, 0, 0, 0)) {
34 return ip == WiFi.localIP() || ip == WiFi.softAPIP() || ip == ETH.localIP();
37bool is_own_host(
const String& host) {
39 if (hostname.length() > 0 && host.equalsIgnoreCase(hostname +
".local")) {
43 return ip.fromString(host) && is_own_interface_ip(ip);
53 if (httpd_req_get_hdr_value_len(req,
"Origin") == 0) {
57 char origin[128] = {0};
59 if (httpd_req_get_hdr_value_str(req,
"Origin", origin,
sizeof(origin)) !=
61 httpd_req_get_hdr_value_str(req,
"Host", host,
sizeof(host)) != ESP_OK) {
62 httpd_resp_send_err(req, HTTPD_403_FORBIDDEN,
63 "Cross-origin request rejected");
67 String origin_str(origin);
68 int scheme_end = origin_str.indexOf(
"://");
69 String origin_authority =
70 scheme_end < 0 ? origin_str : origin_str.substring(scheme_end + 3);
72 String host_str(host);
81 if (origin_authority == host_str && is_own_host(strip_port(host_str))) {
85 httpd_resp_send_err(req, HTTPD_403_FORBIDDEN,
86 "Cross-origin request rejected");
91 auto reset_handler = std::make_shared<HTTPRequestHandler>(
92 1 << HTTP_POST,
"/api/device/reset", [](httpd_req_t* req) {
96 httpd_resp_sendstr(req,
97 "Resetting device back to factory defaults. "
98 "You may have to reconfigure the WiFi settings.");
102 server->add_handler(reset_handler);
106 auto restart_handler = std::make_shared<HTTPRequestHandler>(
107 1 << HTTP_POST,
"/api/device/restart", [](httpd_req_t* req) {
111 httpd_resp_sendstr(req,
"Restarting device");
112 event_loop()->onDelay(500, []() { ESP.restart(); });
115 server->add_handler(restart_handler);
119 auto info_handler = std::make_shared<HTTPRequestHandler>(
120 1 << HTTP_GET,
"/api/info", [](httpd_req_t* req) {
123 JsonDocument json_doc;
124 JsonArray info_items = json_doc.to<JsonArray>();
126 for (
auto info_item = status_page_items->begin();
127 info_item != status_page_items->end(); ++info_item) {
128 info_items.add(info_item->second->as_json());
135#if defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) && \
136 CONFIG_FREERTOS_USE_TRACE_FACILITY
140 UBaseType_t task_slots = uxTaskGetNumberOfTasks() + 4;
141 std::unique_ptr<TaskStatus_t[]> tasks(
142 new (std::nothrow) TaskStatus_t[task_slots]);
144 UBaseType_t n = uxTaskGetSystemState(tasks.get(), task_slots,
nullptr);
145 for (UBaseType_t i = 0; i < n; i++) {
147 item[
"name"] = tasks[i].pcTaskName;
148 item[
"value"] =
static_cast<uint32_t
>(
149 tasks[i].usStackHighWaterMark *
sizeof(StackType_t));
150 item[
"group"] =
"Task stack free (bytes)";
152 info_items.add(item);
158 serializeJson(json_doc, response);
159 httpd_resp_set_type(req,
"application/json");
160 httpd_resp_sendstr(req, response.c_str());
163 server->add_handler(info_handler);
167 auto log_handler = std::make_shared<HTTPRequestHandler>(
168 1 << HTTP_GET,
"/api/log", [](httpd_req_t* req) {
170 httpd_resp_set_type(req,
"application/json; charset=utf-8");
171 if (log_buffer ==
nullptr) {
173 req,
"{\"session\":0,\"next\":0,\"gap\":false,\"lines\":[]}");
183 bool has_since =
false;
184 size_t query_len = httpd_req_get_url_query_len(req) + 1;
185 if (query_len > 1 && query_len <= 64) {
187 if (httpd_req_get_url_query_str(req, query,
sizeof(query)) == ESP_OK) {
189 if (httpd_query_key_value(query,
"since", value,
sizeof(value)) ==
195 unsigned long parsed = strtoul(value, &end, 10);
196 if (end != value && *end ==
'\0' && errno == 0 &&
197 parsed <= UINT32_MAX) {
198 since =
static_cast<uint32_t
>(parsed);
207 JsonDocument json_doc;
209 json_doc[
"next"] = snapshot.
next;
210 json_doc[
"gap"] = snapshot.
gap;
211 JsonArray lines = json_doc[
"lines"].to<JsonArray>();
212 for (
const auto& line : snapshot.
lines) {
213 lines.add(line.c_str());
217 serializeJson(json_doc, response);
218 httpd_resp_sendstr(req, response.c_str());
221 server->add_handler(log_handler);
225 std::vector<RouteDefinition> routes;
231 routes.push_back(
RouteDefinition(
"Signal K",
"/signalk",
"SignalKPage"));
233 RouteDefinition(
"Configuration",
"/configuration",
"ConfigurationPage"));
234 routes.push_back(
RouteDefinition(
"Control",
"/control",
"ControlPage"));
237 JsonDocument json_doc;
238 JsonArray routes_json = json_doc.to<JsonArray>();
240 for (
auto it = routes.begin(); it != routes.end(); ++it) {
241 routes_json.add(it->as_json());
246 serializeJson(routes_json, response);
248 auto routes_handler = std::make_shared<HTTPRequestHandler>(
249 1 << HTTP_GET,
"/api/routes", [response](httpd_req_t* req) {
250 httpd_resp_set_type(req,
"application/json");
251 httpd_resp_sendstr(req, response.c_str());
254 server->add_handler(routes_handler);
265 if (root_page ==
nullptr) {
266 ESP_LOGE(__FILENAME__,
"Root page not found in kWebUIFiles");
272 for (
auto it = routes.begin(); it != routes.end(); ++it) {
273 String path = it->get_path();
274 auto route_handler = std::make_shared<HTTPRequestHandler>(
275 1 << HTTP_GET, path.c_str(), [root_page](httpd_req_t* req) {
276 httpd_resp_set_type(req, root_page->content_type);
277 if (root_page->content_encoding != nullptr) {
278 httpd_resp_set_hdr(req, kContentEncoding,
279 root_page->content_encoding);
284 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 String get_hostname()
Get the current hostname.
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)
bool check_origin(httpd_req_t *req)
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