diff --git a/examples/webui-plain/Makefile b/examples/webui-plain/Makefile new file mode 100644 index 00000000..9addcb99 --- /dev/null +++ b/examples/webui-plain/Makefile @@ -0,0 +1,20 @@ +PROG ?= example +SSL = ? + +ifeq "$(SSL)" "MBEDTLS" +CFLAGS += -DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509 +endif + +ifeq "$(SSL)" "OPENSSL" +CFLAGS += -DMG_ENABLE_OPENSSL=1 -lssl -lcrypto +endif + +all: $(PROG) + $(DEBUGGER) ./$(PROG) $(ARGS) + + +$(PROG): main.c + $(CC) ../../mongoose.c -I../.. -W -Wall $(CFLAGS) $(EXTRA_CFLAGS) -o $(PROG) main.c + +clean: + rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb diff --git a/examples/webui-plain/ca.pem b/examples/webui-plain/ca.pem new file mode 120000 index 00000000..8addd9e2 --- /dev/null +++ b/examples/webui-plain/ca.pem @@ -0,0 +1 @@ +../../test/data/ca.pem \ No newline at end of file diff --git a/examples/webui-plain/main.c b/examples/webui-plain/main.c new file mode 100644 index 00000000..62bec56d --- /dev/null +++ b/examples/webui-plain/main.c @@ -0,0 +1,62 @@ +// Copyright (c) 2022 Cesanta Software Limited +// All rights reserved +// +// UI example +// It implements the following endpoints: +// /api/config/get - respond with current config +// /api/config/set - POST a config change +// any other URI serves static files from s_root_dir +// Data and results are JSON strings + +#include "mongoose.h" + +static const char *s_http_addr = "http://localhost:8000"; // HTTP port +static const char *s_root_dir = "web_root"; +#define MQTT_SERVER "mqtt://broker.hivemq.com:1883" +#define MQTT_PUBLISH_TOPIC "mg/my_device" +#define MQTT_SUBSCRIBE_TOPIC "mg/#" + +static struct config { char *url, *pub, *sub; } s_config; + +// Try to update a single configuration value +static void update_config(struct mg_str json, const char *path, char **value) { + char *jval; + if ((jval = mg_json_get_str(json, path)) != NULL) { + *value = strdup(jval); + } +} + +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_OPEN && c->is_listening) { + s_config.url = strdup(MQTT_SERVER); + s_config.pub = strdup(MQTT_PUBLISH_TOPIC); + s_config.sub = strdup(MQTT_SUBSCRIBE_TOPIC); + } else if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + if (mg_http_match_uri(hm, "/api/config/get")) { + mg_http_reply(c, 200, "Content-Type: application/json\r\n", + "{%Q:%Q,%Q:%Q,%Q:%Q}\n", "url", s_config.url, "pub", + s_config.pub, "sub", s_config.sub); + } else if (mg_http_match_uri(hm, "/api/config/set")) { + struct mg_str json = hm->body; + update_config(json, "$.url", &s_config.url); + update_config(json, "$.pub", &s_config.pub); + update_config(json, "$.sub", &s_config.sub); + mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + } else { + struct mg_http_serve_opts opts = {.root_dir = s_root_dir}; + mg_http_serve_dir(c, ev_data, &opts); + } + } + (void) fn_data; +} + +int main(void) { + struct mg_mgr mgr; // Event manager + mg_log_set("2"); // Set to 3 to enable debug + mg_mgr_init(&mgr); // Initialise event manager + mg_http_listen(&mgr, s_http_addr, fn, NULL); // Create HTTP listener + for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop + mg_mgr_free(&mgr); + return 0; +} diff --git a/examples/webui-plain/web_root/index.html b/examples/webui-plain/web_root/index.html new file mode 100644 index 00000000..d6caa787 --- /dev/null +++ b/examples/webui-plain/web_root/index.html @@ -0,0 +1,86 @@ + + + + + + +

Basic JavaScript UI demo

+
 
+
+

+ Device Configuration

+
+ MQTT server: + + +
+
+ Subscribe topic: + + +
+
+ Publish topic: + + +
+ +
+
 
+
Action log:
+
+ + + diff --git a/examples/webui-plain/web_root/style.css b/examples/webui-plain/web_root/style.css new file mode 100644 index 00000000..b483f801 --- /dev/null +++ b/examples/webui-plain/web_root/style.css @@ -0,0 +1,43 @@ +* { box-sizing: border-box; } +html, body { margin: 0; padding: 0; height: 100%; font: 16px sans-serif; } +select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; } +code, pre { color: #373; font-family: monospace; font-weight: bolder; font-size: smaller; background: #ddd; padding: 0.1em 0.3em; border-radius: 0.2em; } +textarea, input, .addon { font-size: 15px; border: 1px solid #ccc; padding: 0.5em; } +a, a:visited, a:active { color: #55f; } +.addon { background: #eee; min-width: 9em;} +.btn { + background: #ccc; border-radius: 0.3em; border: 0; color: #fff; cursor: pointer; + display: inline-block; padding: 0.6em 2em; font-weight: bolder; +} +.btn[disabled] { opacity: 0.5; cursor: auto;} +.smooth { transition: all .2s; } +.container { margin: 0 20px; width: auto; } +.d-flex { display: flex; } +.d-none { display: none; } +.border { border: 1px solid #ddd; } +.rounded { border-radius: 0.5em; } +.nowrap { white-space: nowrap; } +.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; } +.section { margin: 0 1em; } +.topic, .data, .qos { padding: 0.2em 0.5em; border-radius: 0.4em; margin-right: 0.5em; } +.qos { background: #efa; } +.topic { background: #fea; } +.data { background: #aef; } + +/* Grid */ +.row { display: flex; flex-wrap: wrap; } +.col { margin: 0; padding: 0; overflow: auto; } +.col-12 { width: 100%; } +.col-11 { width: 91.66%; } +.col-10 { width: 83.33%; } +.col-9 { width: 75%; } +.col-8 { width: 66.66%; } +.col-7 { width: 58.33%; } +.col-6 { width: 50%; } +.col-5 { width: 41.66%; } +.col-4 { width: 33.33%; } +.col-3 { width: 25%; } +.col-2 { width: 16.66%; } +.col-1 { width: 8.33%; } +@media (min-width: 1310px) { .container { margin: auto; width: 1270px; } } +@media (max-width: 920px) { .row .col { width: 100%; } }