Merge pull request #1630 from cesanta/webui-plain

added webUI plain JS example
This commit is contained in:
Sergey Lyubka 2022-07-17 18:08:45 +01:00 committed by GitHub
commit e5b40ee29e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 212 additions and 0 deletions

View File

@ -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

1
examples/webui-plain/ca.pem Symbolic link
View File

@ -0,0 +1 @@
../../test/data/ca.pem

View File

@ -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;
}

View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Basic JavaScript UI demo</h1>
<div style="height: 0.3em;">&nbsp;</div>
<div class="col col-6">
<h3 style="background: #c03434; color: #fff; padding: 0.4em;">
Device Configuration</h3>
<div style="margin: 0.5em 0; display: flex;">
<span class="addon nowrap">MQTT server:</span>
<input id="url" type="text" style="flex: 1 100%;" value="" onchange="updateurl()" />
<button class="btn" id="btn_url" onclick="updateurl()"
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<div style="margin: 0.5em 0; display: flex; ">
<span class="addon nowrap">Subscribe topic:</span>
<input id="sub" type="text" style="flex: 1 100%;" value="" onchange="updatesub()" />
<button class="btn" id="btn_sub" onclick="updatesub()"
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<div style="margin: 0.5em 0; display: flex;">
<span class="addon nowrap">Publish topic:</span>
<input id="pub" type="text" style="flex: 1 100%;" value="" onchange="updatepub()" />
<button class="btn" id="btn_pub" onclick="updatepub()"
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<button id="btn_get" onclick="getconfig()">Get configuration</button>
</div>
<div style="height: 0.3em;">&nbsp;</div>
<div style="margin-top: 1em;">Action log:</div>
<div id="log" style="background: #eee; height: 10em; padding: 0.5em; overflow:auto;"><div>
</body>
<script>
var E = function(id) { return document.getElementById(id); };
var inp_url = E('url'), inp_sub = E('sub'), inp_pub = E('pub'), msglog = E('log');;
var enable = function(en) { btn_url.disabled = btn_sub.disabled = btn_pub.disabled = !en; };
var log = text => msglog.innerHTML += text + '<br/>\n';
var showurl = function(val) { inp_url.value = val; };
var showsub = function(val) { inp_sub.value = val; };
var showpub = function(val) { inp_pub.value = val; };
const showconfig = function(r) {
showurl(r.url);
showsub(r.sub);
showpub(r.pub);
};
const getconfig = () =>
fetch('/api/config/get')
.then(r => r.json())
.then(r => {
showconfig(r);
log('GET /api/config/get: ' + JSON.stringify(r));
enable(true);
})
.catch(err => {
console.log(err);
enable(false);
});
const update = (name, val) => {
const myObj = { [name]: val };
log('POST ' + JSON.stringify(myObj) + ' to /api/config/set');
fetch('/api/config/set', {method: 'POST',
headers: {
'Content-Type': 'application/json'},
body:JSON.stringify(myObj)})
.catch(err => {
console.log(err);
enable(false);
});
};
const updateurl = ev => update('url', inp_url.value);
const updatesub = ev => update('sub', inp_sub.value);
const updatepub = ev => update('pub', inp_pub.value);
enable(false);
getconfig();
</script>
</html>

View File

@ -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%; } }