mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-01 13:10:43 +00:00
Event log
This commit is contained in:
1 parent
90051642ef
commit
8534053e57
9 files changed
+182
-96
No files matched your search
+19
-17
@@ -1831,9 +1831,6 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
|
||||
int diff = 0;
|
||||
if (len > 0) do {
|
||||
@@ -2078,11 +2075,14 @@ static bool mg_http_parse_headers(const char *s, const char *end,
|
||||
v.len--; // Trim spaces
|
||||
}
|
||||
// detect duplicated headers -> discard
|
||||
if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) && (++cl_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) && (++te_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Authorization")) == 0) && (++auth_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
|
||||
if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) &&
|
||||
(++cl_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) &&
|
||||
(++te_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Authorization")) == 0) &&
|
||||
(++auth_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
|
||||
return false;
|
||||
// MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
|
||||
h[i].name = k, h[i].value = v; // Success. Assign values
|
||||
@@ -2119,7 +2119,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
|
||||
version_prefix_valid =
|
||||
hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0);
|
||||
if (!is_response && !version_prefix_valid)
|
||||
return -1; // no version detected in request
|
||||
return -1; // no version detected in request
|
||||
if (!is_response && hm->proto.len > 0 &&
|
||||
(!version_prefix_valid || hm->proto.len != 8 ||
|
||||
(hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') ||
|
||||
@@ -2402,12 +2402,14 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
const char *path,
|
||||
const struct mg_http_serve_opts *opts) {
|
||||
char etag[64], tmp[MG_PATH_MAX];
|
||||
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
||||
struct mg_fs *fs = opts && opts->fs ? opts->fs : &mg_fs_posix;
|
||||
struct mg_fd *fd = NULL;
|
||||
size_t size = 0;
|
||||
time_t mtime = 0;
|
||||
const char *mime_types = opts && opts->mime_types ? opts->mime_types : NULL;
|
||||
const char *hdrs = opts && opts->extra_headers ? opts->extra_headers : "";
|
||||
struct mg_str *inm = NULL;
|
||||
struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
|
||||
struct mg_str mime = guess_content_type(mg_str(path), mime_types);
|
||||
bool gzip = false;
|
||||
|
||||
if (path != NULL) {
|
||||
@@ -2425,21 +2427,21 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
}
|
||||
|
||||
// Failed to open, and page404 is configured? Open it, then
|
||||
if (fd == NULL && opts->page404 != NULL) {
|
||||
if (fd == NULL && opts && opts->page404) {
|
||||
fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
|
||||
path = opts->page404;
|
||||
mime = guess_content_type(mg_str(path), opts->mime_types);
|
||||
mime = guess_content_type(mg_str(path), mime_types);
|
||||
}
|
||||
|
||||
if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
|
||||
mg_http_reply(c, 404, opts->extra_headers, "Not found\n");
|
||||
mg_http_reply(c, 404, hdrs, "Not found\n");
|
||||
mg_fs_close(fd);
|
||||
// NOTE: mg_http_etag() call should go first!
|
||||
} else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
|
||||
(inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
|
||||
mg_strcasecmp(*inm, mg_str(etag)) == 0) {
|
||||
mg_fs_close(fd);
|
||||
mg_http_reply(c, 304, opts->extra_headers, "");
|
||||
mg_http_reply(c, 304, hdrs, "");
|
||||
} else {
|
||||
int n, status = 200;
|
||||
char range[100];
|
||||
@@ -2473,11 +2475,11 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
"%s%s%s\r\n",
|
||||
status, mg_http_status_code_str(status), (int) mime.len, mime.buf,
|
||||
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
|
||||
range, opts->extra_headers ? opts->extra_headers : "");
|
||||
range, hdrs);
|
||||
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
|
||||
c->is_resp = 0;
|
||||
mg_fs_close(fd);
|
||||
} else { // start serving static content only if not closing, see #3354
|
||||
} else { // start serving static content only if not closing, see #3354
|
||||
// Track to-be-sent content length at the end of c->data, aligned
|
||||
size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
|
||||
sizeof(size_t) * sizeof(size_t)];
|
||||
|
||||
+26
-1
@@ -3177,10 +3177,26 @@ struct mg_field_set {
|
||||
int write_level;
|
||||
};
|
||||
|
||||
struct mg_dash_custom_handler {
|
||||
struct mg_dash_custom_handler *next;
|
||||
struct mg_str uri_pattern;
|
||||
mg_event_handler_t handler;
|
||||
};
|
||||
|
||||
struct mg_dash {
|
||||
struct mg_field_set *sets;
|
||||
struct mg_dash_custom_handler *custom_handlers;
|
||||
};
|
||||
|
||||
#define MG_DASH_REGISTER_CUSTOM_HANDLER(dash_, uri_, fn_) \
|
||||
do { \
|
||||
static struct mg_dash_custom_handler ch_; \
|
||||
ch_.next = (dash_)->custom_handlers; \
|
||||
ch_.uri_pattern = mg_str(uri_); \
|
||||
ch_.handler = (fn_); \
|
||||
(dash_)->custom_handlers = &ch_; \
|
||||
} while (0)
|
||||
|
||||
static inline struct mg_str trimq(struct mg_str s) { // Trim double quotes
|
||||
if (s.len > 1 && s.buf[0] == '"') s.len -= 2, s.buf++;
|
||||
return s;
|
||||
@@ -3394,12 +3410,21 @@ static inline void mg_dash_ev_handler(struct mg_connection *c, int ev,
|
||||
} else if (mg_match(hm->uri, mg_str("/api/set"), NULL)) {
|
||||
int count = mg_dash_apply(c, dash, hm->body);
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
|
||||
} else {
|
||||
} else if (mg_match(hm->uri, mg_str("/"), NULL)) {
|
||||
struct mg_http_serve_opts opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.fs = &mg_fs_packed;
|
||||
mg_mem_files = mg_packed_files;
|
||||
mg_http_serve_file(c, hm, "/dashboard.html", &opts);
|
||||
} else {
|
||||
struct mg_dash_custom_handler *ch = dash->custom_handlers;
|
||||
for (ch = dash->custom_handlers; ch != NULL; ch = ch->next) {
|
||||
if (mg_match(hm->uri, ch->uri_pattern, NULL)) {
|
||||
ch->handler(c, ev, ev_data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ch == NULL) mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
|
||||
}
|
||||
} else if (ev == MG_EV_WS_MSG) {
|
||||
// Add this to automatically handle "get" and "set" JSON-RPC calls
|
||||
|
||||
+26
-1
@@ -32,10 +32,26 @@ struct mg_field_set {
|
||||
int write_level;
|
||||
};
|
||||
|
||||
struct mg_dash_custom_handler {
|
||||
struct mg_dash_custom_handler *next;
|
||||
struct mg_str uri_pattern;
|
||||
mg_event_handler_t handler;
|
||||
};
|
||||
|
||||
struct mg_dash {
|
||||
struct mg_field_set *sets;
|
||||
struct mg_dash_custom_handler *custom_handlers;
|
||||
};
|
||||
|
||||
#define MG_DASH_REGISTER_CUSTOM_HANDLER(dash_, uri_, fn_) \
|
||||
do { \
|
||||
static struct mg_dash_custom_handler ch_; \
|
||||
ch_.next = (dash_)->custom_handlers; \
|
||||
ch_.uri_pattern = mg_str(uri_); \
|
||||
ch_.handler = (fn_); \
|
||||
(dash_)->custom_handlers = &ch_; \
|
||||
} while (0)
|
||||
|
||||
static inline struct mg_str trimq(struct mg_str s) { // Trim double quotes
|
||||
if (s.len > 1 && s.buf[0] == '"') s.len -= 2, s.buf++;
|
||||
return s;
|
||||
@@ -249,12 +265,21 @@ static inline void mg_dash_ev_handler(struct mg_connection *c, int ev,
|
||||
} else if (mg_match(hm->uri, mg_str("/api/set"), NULL)) {
|
||||
int count = mg_dash_apply(c, dash, hm->body);
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
|
||||
} else {
|
||||
} else if (mg_match(hm->uri, mg_str("/"), NULL)) {
|
||||
struct mg_http_serve_opts opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.fs = &mg_fs_packed;
|
||||
mg_mem_files = mg_packed_files;
|
||||
mg_http_serve_file(c, hm, "/dashboard.html", &opts);
|
||||
} else {
|
||||
struct mg_dash_custom_handler *ch = dash->custom_handlers;
|
||||
for (ch = dash->custom_handlers; ch != NULL; ch = ch->next) {
|
||||
if (mg_match(hm->uri, ch->uri_pattern, NULL)) {
|
||||
ch->handler(c, ev, ev_data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ch == NULL) mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
|
||||
}
|
||||
} else if (ev == MG_EV_WS_MSG) {
|
||||
// Add this to automatically handle "get" and "set" JSON-RPC calls
|
||||
|
||||
+19
-17
@@ -1,15 +1,12 @@
|
||||
#include "http.h"
|
||||
#include "arch.h"
|
||||
#include "base64.h"
|
||||
#include "fmt.h"
|
||||
#include "json.h"
|
||||
#include "log.h"
|
||||
#include "net.h"
|
||||
#include "printf.h"
|
||||
#include "ssi.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "ws.h"
|
||||
|
||||
static int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
|
||||
int diff = 0;
|
||||
@@ -255,11 +252,14 @@ static bool mg_http_parse_headers(const char *s, const char *end,
|
||||
v.len--; // Trim spaces
|
||||
}
|
||||
// detect duplicated headers -> discard
|
||||
if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) && (++cl_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) && (++te_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Authorization")) == 0) && (++auth_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
|
||||
if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) &&
|
||||
(++cl_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) &&
|
||||
(++te_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Authorization")) == 0) &&
|
||||
(++auth_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
|
||||
((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
|
||||
return false;
|
||||
// MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
|
||||
h[i].name = k, h[i].value = v; // Success. Assign values
|
||||
@@ -296,7 +296,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
|
||||
version_prefix_valid =
|
||||
hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0);
|
||||
if (!is_response && !version_prefix_valid)
|
||||
return -1; // no version detected in request
|
||||
return -1; // no version detected in request
|
||||
if (!is_response && hm->proto.len > 0 &&
|
||||
(!version_prefix_valid || hm->proto.len != 8 ||
|
||||
(hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') ||
|
||||
@@ -579,12 +579,14 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
const char *path,
|
||||
const struct mg_http_serve_opts *opts) {
|
||||
char etag[64], tmp[MG_PATH_MAX];
|
||||
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
||||
struct mg_fs *fs = opts && opts->fs ? opts->fs : &mg_fs_posix;
|
||||
struct mg_fd *fd = NULL;
|
||||
size_t size = 0;
|
||||
time_t mtime = 0;
|
||||
const char *mime_types = opts && opts->mime_types ? opts->mime_types : NULL;
|
||||
const char *hdrs = opts && opts->extra_headers ? opts->extra_headers : "";
|
||||
struct mg_str *inm = NULL;
|
||||
struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
|
||||
struct mg_str mime = guess_content_type(mg_str(path), mime_types);
|
||||
bool gzip = false;
|
||||
|
||||
if (path != NULL) {
|
||||
@@ -602,21 +604,21 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
}
|
||||
|
||||
// Failed to open, and page404 is configured? Open it, then
|
||||
if (fd == NULL && opts->page404 != NULL) {
|
||||
if (fd == NULL && opts && opts->page404) {
|
||||
fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
|
||||
path = opts->page404;
|
||||
mime = guess_content_type(mg_str(path), opts->mime_types);
|
||||
mime = guess_content_type(mg_str(path), mime_types);
|
||||
}
|
||||
|
||||
if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
|
||||
mg_http_reply(c, 404, opts->extra_headers, "Not found\n");
|
||||
mg_http_reply(c, 404, hdrs, "Not found\n");
|
||||
mg_fs_close(fd);
|
||||
// NOTE: mg_http_etag() call should go first!
|
||||
} else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
|
||||
(inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
|
||||
mg_strcasecmp(*inm, mg_str(etag)) == 0) {
|
||||
mg_fs_close(fd);
|
||||
mg_http_reply(c, 304, opts->extra_headers, "");
|
||||
mg_http_reply(c, 304, hdrs, "");
|
||||
} else {
|
||||
int n, status = 200;
|
||||
char range[100];
|
||||
@@ -650,11 +652,11 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
"%s%s%s\r\n",
|
||||
status, mg_http_status_code_str(status), (int) mime.len, mime.buf,
|
||||
etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
|
||||
range, opts->extra_headers ? opts->extra_headers : "");
|
||||
range, hdrs);
|
||||
if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
|
||||
c->is_resp = 0;
|
||||
mg_fs_close(fd);
|
||||
} else { // start serving static content only if not closing, see #3354
|
||||
} else { // start serving static content only if not closing, see #3354
|
||||
// Track to-be-sent content length at the end of c->data, aligned
|
||||
size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
|
||||
sizeof(size_t) * sizeof(size_t)];
|
||||
|
||||
@@ -4,7 +4,7 @@ PROG ?= example
|
||||
MONGOOSE_DIR ?= ../..
|
||||
SOURCES = main.c mongoose_dashboard.c dashboard.html.c $(MONGOOSE_DIR)/mongoose.c
|
||||
CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -g3 -O0
|
||||
PFLAGS = -I$(MONGOOSE_DIR) -DMG_DATA_SIZE=64 -DMG_TLS=MG_TLS_BUILTIN
|
||||
PFLAGS = -I$(MONGOOSE_DIR) -DMG_DATA_SIZE=64 -DMG_TLS=MG_TLS_BUILTIN -DMG_ENABLE_LINES=1
|
||||
AFLAGS ?= -fsanitize=signed-integer-overflow,address,undefined,alignment
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
@@ -10,24 +10,24 @@
|
||||
#endif
|
||||
|
||||
// Control panel
|
||||
static bool s_fan, s_relay1, s_relay2;
|
||||
static bool s_led1, s_led2, s_led3;
|
||||
|
||||
static struct mg_field fields_control[] = {
|
||||
{"fan", MG_VAL_BOOL, &s_fan, sizeof(s_fan)},
|
||||
{"relay1", MG_VAL_BOOL, &s_relay1, sizeof(s_relay1)},
|
||||
{"relay2", MG_VAL_BOOL, &s_relay2, sizeof(s_relay2)},
|
||||
static struct mg_field fields_leds[] = {
|
||||
{"led1", MG_VAL_BOOL, &s_led1, sizeof(s_led1)},
|
||||
{"led2", MG_VAL_BOOL, &s_led2, sizeof(s_led2)},
|
||||
{"led3", MG_VAL_BOOL, &s_led3, sizeof(s_led3)},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
|
||||
static void sync_control(bool is_write) {
|
||||
static void sync_leds(bool is_write) {
|
||||
if (is_write) {
|
||||
// gpio_write(FAN_PIN, s_fan);
|
||||
// gpio_write(RELAY_1_PIN, s_relay1);
|
||||
// gpio_write(RELAY_2_PIN, s_relay2);
|
||||
// gpio_write(LED1_PIN, s_led1);
|
||||
// gpio_write(LED2_PIN, s_led2);
|
||||
// gpio_write(LED3_PIN, s_led3);
|
||||
} else {
|
||||
// s_fan = gpio_read(FAN_PIN);
|
||||
// s_relay1 = gpio_read(RELAY_1_PIN);
|
||||
// s_relay2 = gpio_read(RELAY_2_PIN);
|
||||
// s_led1 = gpio_read(LED1_PIN);
|
||||
// s_led2 = gpio_read(LED2_PIN);
|
||||
// s_led3 = gpio_read(LED3_PIN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,16 +107,39 @@ void sync_chart1(bool is_write) {
|
||||
len += mg_snprintf(s_chart1_data + len, sizeof(s_chart1_data) - len, "]");
|
||||
}
|
||||
|
||||
static int s_num_log_files = 0;
|
||||
static int s_log_file_index = 0;
|
||||
static char s_log_file_name[100];
|
||||
static struct mg_field fields_log_files[] = {
|
||||
{"size", MG_VAL_INT, &s_num_log_files, sizeof(s_num_log_files)},
|
||||
{"index", MG_VAL_INT, &s_log_file_index, sizeof(s_log_file_index)},
|
||||
{"name", MG_VAL_STR, &s_log_file_name, sizeof(s_log_file_name)},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
static void sync_log_files(bool is_write) {
|
||||
if (is_write) return;
|
||||
mg_log_level = s_log_level;
|
||||
s_log_level = mg_log_level;
|
||||
}
|
||||
|
||||
static struct mg_field_set field_sets[] = {
|
||||
{"controls", fields_control, sync_control, 0, 0},
|
||||
{"leds", fields_leds, sync_leds, 0, 0},
|
||||
{"metrics", fields_metrics, sync_metrics, 0, 0},
|
||||
{"settings", fields_settings, sync_settings, 0, 0},
|
||||
{"chart1", fields_chart1, sync_chart1, 0, 0},
|
||||
{"log_files", fields_log_files, sync_log_files, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
};
|
||||
static struct mg_dash s_dashboard = {field_sets};
|
||||
static struct mg_dash s_dashboard = {field_sets, NULL};
|
||||
|
||||
static void log_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
mg_http_serve_file(c, ev_data, "Makefile", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void mg_dash_init(struct mg_mgr *mgr) {
|
||||
MG_DASH_REGISTER_CUSTOM_HANDLER(&s_dashboard, "/logs/#", log_ev_handler);
|
||||
mg_http_listen(mgr, HTTP_ADDR, mg_dash_ev_handler, &s_dashboard);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,22 +10,9 @@
|
||||
debug: true,
|
||||
data: {
|
||||
chart1: {data: ''},
|
||||
controls: {
|
||||
fan: true,
|
||||
relay1: false,
|
||||
relay2: false,
|
||||
},
|
||||
settings: {
|
||||
volume: 12,
|
||||
log_level: 3,
|
||||
name: 'Galway',
|
||||
},
|
||||
metrics: {
|
||||
temperature: 1.2,
|
||||
humidity: 3,
|
||||
cpu: 4,
|
||||
ram: 5,
|
||||
},
|
||||
leds: { led1: true, led2: false, led3: false },
|
||||
settings: { volume: 12, log_level: 3, name: 'Galway' },
|
||||
metrics: { temperature: 1.2, humidity: 3, cpu: 4, ram: 5 },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -75,7 +62,7 @@
|
||||
<path d="M8 17h5" />
|
||||
<rect x="4" y="4" width="16" height="16" rx="3" />
|
||||
</svg>
|
||||
Events
|
||||
Event Log
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -175,30 +162,19 @@
|
||||
|
||||
<!-- Controls Panel -->
|
||||
<div class="panel controls-panel">
|
||||
<div class="panel-title">Controls</div>
|
||||
<div class="panel-title">LED Control</div>
|
||||
<div class="panel-body">
|
||||
<div class="controls-list">
|
||||
<div class="control-row">
|
||||
<div class="control-info">
|
||||
<div class="control-name">Cooling Fan</div>
|
||||
<div class="control-desc">PWM controlled output</div>
|
||||
</div>
|
||||
<input type="checkbox" data-bind="controls.fan" data-autosave="1" class="toggle" />
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<div class="control-info">
|
||||
<div class="control-name">Relay Channel 1</div>
|
||||
<div class="control-desc">Auxiliary load output</div>
|
||||
</div>
|
||||
<input type="checkbox" data-bind="controls.relay1" data-autosave="1" class="toggle" />
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<div class="control-info">
|
||||
<div class="control-name">Relay Channel 2</div>
|
||||
<div class="control-desc">Safety-controlled line</div>
|
||||
</div>
|
||||
<input type="checkbox" data-bind="controls.relay2" data-autosave="1" class="toggle" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="field-label">LED1</div>
|
||||
<input type="checkbox" data-bind="leds.led1" data-autosave="1" class="toggle" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="field-label">LED2</div>
|
||||
<input type="checkbox" data-bind="leds.led2" data-autosave="1" class="toggle" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="field-label">LED3</div>
|
||||
<input type="checkbox" data-bind="leds.led3" data-autosave="1" class="toggle" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -236,6 +212,7 @@
|
||||
</section>
|
||||
<section id="firmware" class="tab-panel" role="tabpanel" aria-labelledby="tab-firmware" hidden></section>
|
||||
<section id="events" class="tab-panel" role="tabpanel" aria-labelledby="tab-events" hidden>
|
||||
<pre id="logs" class="file-content"></pre>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@@ -86,6 +86,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
Dashboard.on('change', args => updateGraphPoints(args?.chart1?.data));
|
||||
Dashboard.call('get', 'chart1').then(resp => updateGraphPoints(resp?.data));
|
||||
|
||||
// Set iframe source in JS, to prevent Monolith killing it
|
||||
document.getElementById("logs").src = "/logs/";
|
||||
// Show logs
|
||||
fetch('logs/')
|
||||
.then(resp => resp.text())
|
||||
.then(text => document.getElementById("logs").textContent = text);
|
||||
});
|
||||
@@ -265,18 +265,19 @@ body {
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
font-size: 0.72rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: #94a3b8;
|
||||
width: 4.5rem;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
min-width: 6rem;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
@@ -419,6 +420,35 @@ body {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.file-content {
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 7rem);
|
||||
margin-top: 0.5rem;
|
||||
padding: 1rem 1.15rem;
|
||||
overflow: auto;
|
||||
color: #dbeafe;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(15, 23, 42, 0.96), rgba(15, 23, 42, 0.98)),
|
||||
#0f172a;
|
||||
border: 1px solid rgba(51, 65, 85, 0.9);
|
||||
border-radius: 1rem;
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08),
|
||||
0 2px 6px rgba(15, 23, 42, 0.1),
|
||||
0 12px 32px rgba(15, 23, 42, 0.14);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.55;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
tab-size: 2;
|
||||
}
|
||||
|
||||
.file-content:empty::before {
|
||||
content: "Waiting for events...";
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* Chart */
|
||||
|
||||
.chart-panel {
|
||||
|
||||
Reference in new issue
Block a user