Rename complete -> device-dashboard, remove multiple-logins and dashboard

This commit is contained in:
Sergey Lyubka 2022-05-14 09:08:23 +01:00
parent c20e48179f
commit 443d0f0c6c
39 changed files with 440 additions and 2169 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

View File

@ -1,10 +0,0 @@
PROG ?= example
all: $(PROG)
$(DEBUGGER) ./$(PROG)
$(PROG):
$(CC) ../../mongoose.c -I../.. $(CFLAGS) $(EXTRA) -o $(PROG) main.c mjson.c
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb log.txt

View File

@ -1,82 +0,0 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include "mjson.h" // JSON parsing and printing
#include "mongoose.h"
// This is a configuration structure we're going to show on a dashboard
static struct config {
int value1;
char *value2;
} s_config = {123, NULL};
// Stringifies the config. A caller must free() it.
static char *stringify_config(struct config *cfg) {
char *s = NULL;
mjson_printf(mjson_print_dynamic_buf, &s, "{%Q:%d,%Q:%Q}", "value1",
cfg->value1, "value2", cfg->value2);
return s;
}
// Update config structure. Return true if changed, false otherwise
static bool update_config(struct mg_http_message *hm, struct config *cfg) {
bool changed = false;
char buf[256];
double dv;
if (mjson_get_number(hm->body.ptr, hm->body.len, "$.value1", &dv)) {
s_config.value1 = dv;
changed = true;
}
if (mjson_get_string(hm->body.ptr, hm->body.len, "$.value2", buf,
sizeof(buf)) > 0) {
free(s_config.value2);
s_config.value2 = strdup(buf);
changed = true;
}
return changed;
}
// Notify all config watchers about the config change
static void notify_config_change(struct mg_mgr *mgr) {
struct mg_connection *c;
char *s = stringify_config(&s_config);
for (c = mgr->conns; c != NULL; c = c->next) {
if (c->label[0] == 'W') mg_http_printf_chunk(c, "%s\n", s);
}
free(s);
}
// HTTP request handler function. It implements the following endpoints:
// /api/config/get - returns current config
// /api/config/set - updates current config
// /api/config/watch - does not return. Sends config as it changes in
// chunks all other URI - serves web_root/ directory
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
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")) {
char *s = stringify_config(&s_config);
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s\n",
(int) strlen(s) + 1, s);
free(s);
} else if (mg_http_match_uri(hm, "/api/config/set")) {
if (update_config(hm, &s_config)) notify_config_change(fn_data);
mg_printf(c, "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
} else if (mg_http_match_uri(hm, "/api/config/watch")) {
c->label[0] = 'W'; // Mark ourselves as a config watcher
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
} else {
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
}
}
}
int main(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://localhost:8000", cb, &mgr);
for (;;) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
return 0;
}

View File

@ -1,981 +0,0 @@
// Copyright (c) 2018-2020 Cesanta Software Limited
// All rights reserved
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "mjson.h"
#if !defined(_MSC_VER) || _MSC_VER >= 1700
#else
#define va_copy(x, y) (x) = (y)
#define snprintf _snprintf
#define alloca _alloca
#endif
static int mjson_esc(int c, int esc) {
const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
}
return 0;
}
static int mjson_pass_string(const char *s, int len) {
int i;
for (i = 0; i < len; i++) {
if (s[i] == '\\' && i + 1 < len && mjson_esc(s[i + 1], 1)) {
i++;
} else if (s[i] == '\0') {
return MJSON_ERROR_INVALID_INPUT;
} else if (s[i] == '"') {
return i;
}
}
return MJSON_ERROR_INVALID_INPUT;
}
int mjson(const char *s, int len, mjson_cb_t cb, void *ud) {
enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
unsigned char nesting[MJSON_MAX_DEPTH];
int i, depth = 0;
#define MJSONCALL(ev) \
if (cb != NULL && cb(ev, s, start, i - start + 1, ud)) return i + 1;
// In the ascii table, the distance between `[` and `]` is 2.
// Ditto for `{` and `}`. Hence +2 in the code below.
#define MJSONEOO() \
do { \
if (c != nesting[depth - 1] + 2) return MJSON_ERROR_INVALID_INPUT; \
depth--; \
if (depth == 0) { \
MJSONCALL(tok); \
return i + 1; \
} \
} while (0)
for (i = 0; i < len; i++) {
int start = i;
unsigned char c = ((unsigned char *) s)[i];
int tok = c;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
// printf("- %c [%.*s] %d %d\n", c, i, s, depth, expecting);
switch (expecting) {
case S_VALUE:
if (c == '{') {
if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
nesting[depth++] = c;
expecting = S_KEY;
break;
} else if (c == '[') {
if (depth >= (int) sizeof(nesting)) return MJSON_ERROR_TOO_DEEP;
nesting[depth++] = c;
break;
} else if (c == ']' && depth > 0) { // Empty array
MJSONEOO();
} else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
i += 3;
tok = MJSON_TOK_TRUE;
} else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
i += 3;
tok = MJSON_TOK_NULL;
} else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
i += 4;
tok = MJSON_TOK_FALSE;
} else if (c == '-' || ((c >= '0' && c <= '9'))) {
char *end = NULL;
strtod(&s[i], &end);
if (end != NULL) i += end - &s[i] - 1;
tok = MJSON_TOK_NUMBER;
} else if (c == '"') {
int n = mjson_pass_string(&s[i + 1], len - i - 1);
if (n < 0) return n;
i += n + 1;
tok = MJSON_TOK_STRING;
} else {
return MJSON_ERROR_INVALID_INPUT;
}
if (depth == 0) {
MJSONCALL(tok);
return i + 1;
}
expecting = S_COMMA_OR_EOO;
break;
case S_KEY:
if (c == '"') {
int n = mjson_pass_string(&s[i + 1], len - i - 1);
if (n < 0) return n;
i += n + 1;
tok = MJSON_TOK_KEY;
expecting = S_COLON;
} else if (c == '}') { // Empty object
MJSONEOO();
expecting = S_COMMA_OR_EOO;
} else {
return MJSON_ERROR_INVALID_INPUT;
}
break;
case S_COLON:
if (c == ':') {
expecting = S_VALUE;
} else {
return MJSON_ERROR_INVALID_INPUT;
}
break;
case S_COMMA_OR_EOO:
if (depth <= 0) return MJSON_ERROR_INVALID_INPUT;
if (c == ',') {
expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
} else if (c == ']' || c == '}') {
MJSONEOO();
} else {
return MJSON_ERROR_INVALID_INPUT;
}
break;
}
MJSONCALL(tok);
}
return MJSON_ERROR_INVALID_INPUT;
}
struct msjon_get_data {
const char *path; // Lookup json path
int pos; // Current path index
int d1; // Current depth of traversal
int d2; // Expected depth of traversal
int i1; // Index in an array
int i2; // Expected index in an array
int obj; // If the value is array/object, offset where it starts
const char **tokptr; // Destination
int *toklen; // Destination length
int tok; // Returned token
};
static int mjson_plen(const char *s) {
int i = 0;
while (s[i] != '\0' && s[i] != '.' && s[i] != '[') i++;
return i;
}
static int mjson_get_cb(int tok, const char *s, int off, int len, void *ud) {
struct msjon_get_data *data = (struct msjon_get_data *) ud;
// printf("--> %2x %2d %2d %2d %2d\t'%s'\t'%.*s'\t\t'%.*s'\n", tok, data->d1,
// data->d2, data->i1, data->i2, data->path + data->pos, off, s, len,
// s + off);
if (data->tok != MJSON_TOK_INVALID) return 1; // Found
if (tok == '{') {
if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
data->d1++;
} else if (tok == '[') {
if (data->d1 == data->d2 && data->path[data->pos] == '[') {
data->i1 = 0;
data->i2 = (int) strtod(&data->path[data->pos + 1], NULL);
if (data->i1 == data->i2) {
data->d2++;
data->pos += 3;
}
}
if (!data->path[data->pos] && data->d1 == data->d2) data->obj = off;
data->d1++;
} else if (tok == ',') {
if (data->d1 == data->d2 + 1) {
data->i1++;
if (data->i1 == data->i2) {
while (data->path[data->pos] != ']') data->pos++;
data->pos++;
data->d2++;
}
}
} else if (tok == MJSON_TOK_KEY && data->d1 == data->d2 + 1 &&
data->path[data->pos] == '.' && s[off] == '"' &&
s[off + len - 1] == '"' &&
mjson_plen(&data->path[data->pos + 1]) == len - 2 &&
!memcmp(s + off + 1, &data->path[data->pos + 1], len - 2)) {
data->d2++;
data->pos += len - 1;
} else if (tok == MJSON_TOK_KEY && data->d1 == data->d2) {
return 1; // Exhausted path, not found
} else if (tok == '}' || tok == ']') {
data->d1--;
// data->d2--;
if (!data->path[data->pos] && data->d1 == data->d2 && data->obj != -1) {
data->tok = tok - 2;
if (data->tokptr) *data->tokptr = s + data->obj;
if (data->toklen) *data->toklen = off - data->obj + 1;
return 1;
}
} else if (MJSON_TOK_IS_VALUE(tok)) {
// printf("TOK --> %d\n", tok);
if (data->d1 == data->d2 && !data->path[data->pos]) {
data->tok = tok;
if (data->tokptr) *data->tokptr = s + off;
if (data->toklen) *data->toklen = len;
return 1;
}
}
return 0;
}
enum mjson_tok mjson_find(const char *s, int len, const char *jp,
const char **tokptr, int *toklen) {
struct msjon_get_data data = {jp, 1, 0, 0, 0,
0, -1, tokptr, toklen, MJSON_TOK_INVALID};
if (jp[0] != '$') return MJSON_TOK_INVALID;
if (mjson(s, len, mjson_get_cb, &data) < 0) return MJSON_TOK_INVALID;
return (enum mjson_tok) data.tok;
}
int mjson_get_number(const char *s, int len, const char *path, double *v) {
const char *p;
int tok, n;
if ((tok = mjson_find(s, len, path, &p, &n)) == MJSON_TOK_NUMBER) {
if (v != NULL) *v = strtod(p, NULL);
}
return tok == MJSON_TOK_NUMBER ? 1 : 0;
}
int mjson_get_bool(const char *s, int len, const char *path, int *v) {
int tok = mjson_find(s, len, path, NULL, NULL);
if (tok == MJSON_TOK_TRUE && v != NULL) *v = 1;
if (tok == MJSON_TOK_FALSE && v != NULL) *v = 0;
return tok == MJSON_TOK_TRUE || tok == MJSON_TOK_FALSE ? 1 : 0;
}
static int mjson_unescape(const char *s, int len, char *to, int n) {
int i, j;
for (i = 0, j = 0; i < len && j < n; i++, j++) {
if (s[i] == '\\' && i + 1 < len) {
int c = mjson_esc(s[i + 1], 0);
if (c == 0) return -1;
to[j] = c;
i++;
} else {
to[j] = s[i];
}
}
if (j >= n) return -1;
if (n > 0) to[j] = '\0';
return j;
}
int mjson_get_string(const char *s, int len, const char *path, char *to,
int n) {
const char *p;
int sz;
if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return -1;
return mjson_unescape(p + 1, sz - 2, to, n);
}
int mjson_get_hex(const char *s, int len, const char *x, char *to, int n) {
const char *p;
int i, j, sz;
if (mjson_find(s, len, x, &p, &sz) != MJSON_TOK_STRING) return -1;
for (i = j = 0; i < sz - 3 && j < n; i += 2, j++) {
#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W')
unsigned char a = *(const unsigned char *) (p + i + 1);
unsigned char b = *(const unsigned char *) (p + i + 2);
((unsigned char *) to)[j] = (HEXTOI(a) << 4) | HEXTOI(b);
}
if (j < n) to[j] = '\0';
return j;
}
#if MJSON_ENABLE_BASE64
static int mjson_base64rev(int c) {
if (c >= 'A' && c <= 'Z') {
return c - 'A';
} else if (c >= 'a' && c <= 'z') {
return c + 26 - 'a';
} else if (c >= '0' && c <= '9') {
return c + 52 - '0';
} else if (c == '+') {
return 62;
} else if (c == '/') {
return 63;
} else {
return 64;
}
}
int mjson_base64_dec(const char *src, int n, char *dst, int dlen) {
const char *end = src + n;
int len = 0;
while (src + 3 < end && len < dlen) {
int a = mjson_base64rev(src[0]), b = mjson_base64rev(src[1]),
c = mjson_base64rev(src[2]), d = mjson_base64rev(src[3]);
dst[len++] = (a << 2) | (b >> 4);
if (src[2] != '=' && len < dlen) {
dst[len++] = (b << 4) | (c >> 2);
if (src[3] != '=' && len < dlen) {
dst[len++] = (c << 6) | d;
}
}
src += 4;
}
if (len < dlen) dst[len] = '\0';
return len;
}
int mjson_get_base64(const char *s, int len, const char *path, char *to,
int n) {
const char *p;
int sz;
if (mjson_find(s, len, path, &p, &sz) != MJSON_TOK_STRING) return 0;
return mjson_base64_dec(p + 1, sz - 2, to, n);
}
#endif // MJSON_ENABLE_BASE64
#if MJSON_ENABLE_NEXT
struct nextdata {
int off, len, depth, t, vo, arrayindex;
int *koff, *klen, *voff, *vlen, *vtype;
};
static int next_cb(int tok, const char *s, int off, int len, void *ud) {
struct nextdata *d = (struct nextdata *) ud;
// int i;
switch (tok) {
case '{':
case '[':
if (d->depth == 0 && tok == '[') d->arrayindex = 0;
if (d->depth == 1 && off > d->off) {
d->vo = off;
d->t = tok == '{' ? MJSON_TOK_OBJECT : MJSON_TOK_ARRAY;
if (d->voff) *d->voff = off;
if (d->vtype) *d->vtype = d->t;
}
d->depth++;
break;
case '}':
case ']':
d->depth--;
if (d->depth == 1 && d->vo) {
d->len = off + len;
if (d->vlen) *d->vlen = d->len - d->vo;
if (d->arrayindex >= 0) {
if (d->koff) *d->koff = d->arrayindex; // koff holds array index
if (d->klen) *d->klen = 0; // klen holds 0
}
return 1;
}
if (d->depth == 1 && d->arrayindex >= 0) d->arrayindex++;
break;
case ',':
case ':':
break;
case MJSON_TOK_KEY:
if (d->depth == 1 && d->off < off) {
if (d->koff) *d->koff = off; // And report back to the user
if (d->klen) *d->klen = len; // If we have to
}
break;
default:
if (d->depth != 1) break;
// If we're iterating over the array
if (off > d->off) {
d->len = off + len;
if (d->vlen) *d->vlen = len; // value length
if (d->voff) *d->voff = off; // value offset
if (d->vtype) *d->vtype = tok; // value type
if (d->arrayindex >= 0) {
if (d->koff) *d->koff = d->arrayindex; // koff holds array index
if (d->klen) *d->klen = 0; // klen holds 0
}
return 1;
}
if (d->arrayindex >= 0) d->arrayindex++;
break;
}
(void) s;
return 0;
}
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
int *vlen, int *vtype) {
struct nextdata d = {off, 0, 0, 0, 0, -1, koff, klen, voff, vlen, vtype};
mjson(s, n, next_cb, &d);
return d.len;
}
#endif
#if MJSON_ENABLE_PRINT
int mjson_print_fixed_buf(const char *ptr, int len, void *fndata) {
struct mjson_fixedbuf *fb = (struct mjson_fixedbuf *) fndata;
int i, left = fb->size - 1 - fb->len;
if (left < len) len = left;
for (i = 0; i < len; i++) fb->ptr[fb->len + i] = ptr[i];
fb->len += len;
fb->ptr[fb->len] = '\0';
return len;
}
int mjson_print_dynamic_buf(const char *ptr, int len, void *fndata) {
char *s, *buf = *(char **) fndata;
int curlen = buf == NULL ? 0 : strlen(buf);
if ((s = (char *) realloc(buf, curlen + len + 1)) == NULL) {
return 0;
} else {
memcpy(s + curlen, ptr, len);
s[curlen + len] = '\0';
*(char **) fndata = s;
return len;
}
}
int mjson_print_null(const char *ptr, int len, void *userdata) {
(void) ptr;
(void) userdata;
return len;
}
int mjson_print_file(const char *ptr, int len, void *userdata) {
return fwrite(ptr, 1, len, (FILE *) userdata);
}
int mjson_print_buf(mjson_print_fn_t fn, void *fndata, const char *buf,
int len) {
return fn(buf, len, fndata);
}
int mjson_print_int(mjson_print_fn_t fn, void *fndata, int value,
int is_signed) {
char buf[20];
int len = snprintf(buf, sizeof(buf), is_signed ? "%d" : "%u", value);
return fn(buf, len, fndata);
}
int mjson_print_long(mjson_print_fn_t fn, void *fndata, long value,
int is_signed) {
char buf[20];
const char *fmt = (is_signed ? "%ld" : "%lu");
int len = snprintf(buf, sizeof(buf), fmt, value);
return fn(buf, len, fndata);
}
int mjson_print_dbl(mjson_print_fn_t fn, void *fndata, double d,
const char *fmt) {
char buf[40];
int n = snprintf(buf, sizeof(buf), fmt, d);
return fn(buf, n, fndata);
}
int mjson_print_str(mjson_print_fn_t fn, void *fndata, const char *s, int len) {
int i, n = fn("\"", 1, fndata);
for (i = 0; i < len; i++) {
char c = mjson_esc(s[i], 1);
if (c) {
n += fn("\\", 1, fndata);
n += fn(&c, 1, fndata);
} else {
n += fn(&s[i], 1, fndata);
}
}
return n + fn("\"", 1, fndata);
}
#if MJSON_ENABLE_BASE64
int mjson_print_b64(mjson_print_fn_t fn, void *fndata, const unsigned char *s,
int n) {
const char *t =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int i, len = fn("\"", 1, fndata);
for (i = 0; i < n; i += 3) {
int a = s[i], b = i + 1 < n ? s[i + 1] : 0, c = i + 2 < n ? s[i + 2] : 0;
char buf[4] = {t[a >> 2], t[(a & 3) << 4 | (b >> 4)], '=', '='};
if (i + 1 < n) buf[2] = t[(b & 15) << 2 | (c >> 6)];
if (i + 2 < n) buf[3] = t[c & 63];
len += fn(buf, sizeof(buf), fndata);
}
return len + fn("\"", 1, fndata);
}
#endif /* MJSON_ENABLE_BASE64 */
int mjson_vprintf(mjson_print_fn_t fn, void *fndata, const char *fmt,
va_list xap) {
int i = 0, n = 0;
va_list ap;
va_copy(ap, xap);
while (fmt[i] != '\0') {
if (fmt[i] == '%') {
char fc = fmt[++i];
int is_long = 0;
if (fc == 'l') {
is_long = 1;
fc = fmt[i + 1];
}
if (fc == 'Q') {
char *buf = va_arg(ap, char *);
n += mjson_print_str(fn, fndata, buf ? buf : "", buf ? strlen(buf) : 0);
} else if (strncmp(&fmt[i], ".*Q", 3) == 0) {
int len = va_arg(ap, int);
char *buf = va_arg(ap, char *);
n += mjson_print_str(fn, fndata, buf, len);
i += 2;
} else if (fc == 'd' || fc == 'u') {
int is_signed = (fc == 'd');
if (is_long) {
long val = va_arg(ap, long);
n += mjson_print_long(fn, fndata, val, is_signed);
i++;
} else {
int val = va_arg(ap, int);
n += mjson_print_int(fn, fndata, val, is_signed);
}
} else if (fc == 'B') {
const char *s = va_arg(ap, int) ? "true" : "false";
n += mjson_print_buf(fn, fndata, s, strlen(s));
} else if (fc == 's') {
char *buf = va_arg(ap, char *);
n += mjson_print_buf(fn, fndata, buf, strlen(buf));
} else if (strncmp(&fmt[i], ".*s", 3) == 0) {
int len = va_arg(ap, int);
char *buf = va_arg(ap, char *);
n += mjson_print_buf(fn, fndata, buf, len);
i += 2;
} else if (fc == 'g') {
n += mjson_print_dbl(fn, fndata, va_arg(ap, double), "%g");
} else if (fc == 'f') {
n += mjson_print_dbl(fn, fndata, va_arg(ap, double), "%f");
#if MJSON_ENABLE_BASE64
} else if (fc == 'V') {
int len = va_arg(ap, int);
const char *buf = va_arg(ap, const char *);
n += mjson_print_b64(fn, fndata, (unsigned char *) buf, len);
#endif
} else if (fc == 'H') {
const char *hex = "0123456789abcdef";
int i, len = va_arg(ap, int);
const unsigned char *p = va_arg(ap, const unsigned char *);
n += fn("\"", 1, fndata);
for (i = 0; i < len; i++) {
n += fn(&hex[(p[i] >> 4) & 15], 1, fndata);
n += fn(&hex[p[i] & 15], 1, fndata);
}
n += fn("\"", 1, fndata);
} else if (fc == 'M') {
mjson_vprint_fn_t vfn = va_arg(ap, mjson_vprint_fn_t);
n += vfn(fn, fndata, &ap);
}
i++;
} else {
n += mjson_print_buf(fn, fndata, &fmt[i++], 1);
}
}
va_end(xap);
va_end(ap);
return n;
}
int mjson_printf(mjson_print_fn_t fn, void *fndata, const char *fmt, ...) {
va_list ap;
int len;
va_start(ap, fmt);
len = mjson_vprintf(fn, fndata, fmt, ap);
va_end(ap);
return len;
}
#endif /* MJSON_ENABLE_PRINT */
#if MJSON_IMPLEMENT_STRTOD
static inline int is_digit(int c) {
return c >= '0' && c <= '9';
}
/* NOTE: strtod() implementation by Yasuhiro Matsumoto. */
double strtod(const char *str, char **end) {
double d = 0.0;
int sign = 1, n = 0;
const char *p = str, *a = str;
/* decimal part */
if (*p == '-') {
sign = -1;
++p;
} else if (*p == '+')
++p;
if (is_digit(*p)) {
d = (double) (*p++ - '0');
while (*p && is_digit(*p)) {
d = d * 10.0 + (double) (*p - '0');
++p;
++n;
}
a = p;
} else if (*p != '.')
goto done;
d *= sign;
/* fraction part */
if (*p == '.') {
double f = 0.0;
double base = 0.1;
++p;
if (is_digit(*p)) {
while (*p && is_digit(*p)) {
f += base * (*p - '0');
base /= 10.0;
++p;
++n;
}
}
d += f * sign;
a = p;
}
/* exponential part */
if ((*p == 'E') || (*p == 'e')) {
int e = 0;
++p;
sign = 1;
if (*p == '-') {
sign = -1;
++p;
} else if (*p == '+')
++p;
if (is_digit(*p)) {
while (*p == '0') ++p;
e = (int) (*p++ - '0');
while (*p && is_digit(*p)) {
e = e * 10 + (int) (*p - '0');
++p;
}
e *= sign;
} else if (!is_digit(*(a - 1))) {
a = str;
goto done;
} else if (*p == 0)
goto done;
if (d == 2.2250738585072011 && e == -308) {
d = 0.0;
a = p;
goto done;
}
if (d == 2.2250738585072012 && e <= -308) {
d *= 1.0e-308;
a = p;
goto done;
}
{
int i;
for (i = 0; i < 10; i++) d *= 10;
}
a = p;
} else if (p > str && !is_digit(*(p - 1))) {
a = str;
goto done;
}
done:
if (end) *end = (char *) a;
return d;
}
#endif
#if MJSON_ENABLE_MERGE
int mjson_merge(const char *s, int n, const char *s2, int n2,
mjson_print_fn_t fn, void *userdata) {
int koff, klen, voff, vlen, t, t2, k, off = 0, len = 0, comma = 0;
if (n < 2) return len;
len += fn("{", 1, userdata);
while ((off = mjson_next(s, n, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
#if !defined(_MSC_VER) || _MSC_VER >= 1700
char path[klen + 1];
#else
char *path = (char *) alloca(klen + 1);
#endif
const char *val;
memcpy(path, "$.", 2);
memcpy(path + 2, s + koff + 1, klen - 2);
path[klen] = '\0';
if ((t2 = mjson_find(s2, n2, path, &val, &k)) != MJSON_TOK_INVALID) {
if (t2 == MJSON_TOK_NULL) continue; // null deletes the key
} else {
val = s + voff; // Key is not found in the update. Copy the old value.
}
if (comma) len += fn(",", 1, userdata);
len += fn(s + koff, klen, userdata);
len += fn(":", 1, userdata);
if (t == MJSON_TOK_OBJECT && t2 == MJSON_TOK_OBJECT) {
len += mjson_merge(s + voff, vlen, val, k, fn, userdata);
} else {
if (t2 != MJSON_TOK_INVALID) vlen = k;
len += fn(val, vlen, userdata);
}
comma = 1;
}
// Add missing keys
off = 0;
while ((off = mjson_next(s2, n2, off, &koff, &klen, &voff, &vlen, &t)) != 0) {
#if !defined(_MSC_VER) || _MSC_VER >= 1700
char path[klen + 1];
#else
char *path = (char *) alloca(klen + 1);
#endif
const char *val;
if (t == MJSON_TOK_NULL) continue;
memcpy(path, "$.", 2);
memcpy(path + 2, s2 + koff + 1, klen - 2);
path[klen] = '\0';
if (mjson_find(s, n, path, &val, &vlen) != MJSON_TOK_INVALID) continue;
if (comma) len += fn(",", 1, userdata);
len += fn(s2 + koff, klen, userdata);
len += fn(":", 1, userdata);
len += fn(s2 + voff, vlen, userdata);
comma = 1;
}
len += fn("}", 1, userdata);
return len;
}
#endif // MJSON_ENABLE_MERGE
#if MJSON_ENABLE_PRETTY
struct prettydata {
int level;
int len;
int prev;
const char *pad;
int padlen;
mjson_print_fn_t fn;
void *userdata;
};
static int pretty_cb(int ev, const char *s, int off, int len, void *ud) {
struct prettydata *d = (struct prettydata *) ud;
int i;
switch (ev) {
case '{':
case '[':
d->level++;
d->len += d->fn(s + off, len, d->userdata);
break;
case '}':
case ']':
d->level--;
if (d->prev != '[' && d->prev != '{' && d->padlen > 0) {
d->len += d->fn("\n", 1, d->userdata);
for (i = 0; i < d->level; i++)
d->len += d->fn(d->pad, d->padlen, d->userdata);
}
d->len += d->fn(s + off, len, d->userdata);
break;
case ',':
d->len += d->fn(s + off, len, d->userdata);
if (d->padlen > 0) {
d->len += d->fn("\n", 1, d->userdata);
for (i = 0; i < d->level; i++)
d->len += d->fn(d->pad, d->padlen, d->userdata);
}
break;
case ':':
d->len += d->fn(s + off, len, d->userdata);
if (d->padlen > 0) d->len += d->fn(" ", 1, d->userdata);
break;
case MJSON_TOK_KEY:
if (d->prev == '{' && d->padlen > 0) {
d->len += d->fn("\n", 1, d->userdata);
for (i = 0; i < d->level; i++)
d->len += d->fn(d->pad, d->padlen, d->userdata);
}
d->len += d->fn(s + off, len, d->userdata);
break;
default:
if (d->prev == '[' && d->padlen > 0) {
d->len += d->fn("\n", 1, d->userdata);
for (i = 0; i < d->level; i++)
d->len += d->fn(d->pad, d->padlen, d->userdata);
}
d->len += d->fn(s + off, len, d->userdata);
break;
}
d->prev = ev;
return 0;
}
int mjson_pretty(const char *s, int n, const char *pad, mjson_print_fn_t fn,
void *userdata) {
struct prettydata d = {0, 0, 0, pad, strlen(pad), fn, userdata};
if (mjson(s, n, pretty_cb, &d) < 0) return -1;
return d.len;
}
#endif // MJSON_ENABLE_PRETTY
#if MJSON_ENABLE_RPC
struct jsonrpc_ctx jsonrpc_default_context;
struct jsonrpc_userdata {
mjson_print_fn_t fn;
void *fndata;
};
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2) {
int i = 0, j = 0, ni = 0, nj = 0;
while (i < n1 || j < n2) {
if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
i++, j++;
} else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
ni = i, nj = j + 1, i++;
} else if (nj > 0 && nj <= n2 && (s1[i - 1] == '#' || s2[j] != '/')) {
i = ni, j = nj;
} else {
return 0;
}
}
return 1;
}
static int jsonrpc_printer(const char *buf, int len, void *userdata) {
struct jsonrpc_userdata *u = (struct jsonrpc_userdata *) userdata;
return u->fn(buf, len, u->fndata);
}
void jsonrpc_return_errorv(struct jsonrpc_request *r, int code,
const char *message, const char *data_fmt,
va_list ap) {
if (r->id_len == 0) return;
mjson_printf(r->fn, r->fndata,
"{\"id\":%.*s,\"error\":{\"code\":%d,\"message\":%Q", r->id_len,
r->id, code, message == NULL ? "" : message);
if (data_fmt != NULL) {
mjson_printf(r->fn, r->fndata, ",\"data\":");
mjson_vprintf(r->fn, r->fndata, data_fmt, ap);
}
mjson_printf(r->fn, r->fndata, "}}\n");
}
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
const char *message, const char *data_fmt, ...) {
va_list ap;
va_start(ap, data_fmt);
jsonrpc_return_errorv(r, code, message, data_fmt, ap);
va_end(ap);
}
void jsonrpc_return_successv(struct jsonrpc_request *r, const char *result_fmt,
va_list ap) {
if (r->id_len == 0) return;
mjson_printf(r->fn, r->fndata, "{\"id\":%.*s,\"result\":", r->id_len, r->id);
if (result_fmt != NULL) {
mjson_vprintf(r->fn, r->fndata, result_fmt, ap);
} else {
mjson_printf(r->fn, r->fndata, "%s", "null");
}
mjson_printf(r->fn, r->fndata, "}\n");
}
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
...) {
va_list ap;
va_start(ap, result_fmt);
jsonrpc_return_successv(r, result_fmt, ap);
va_end(ap);
}
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
mjson_print_fn_t fn, void *fndata) {
const char *result = NULL, *error = NULL;
int result_sz = 0, error_sz = 0;
struct jsonrpc_method *m = NULL;
struct jsonrpc_userdata d;
struct jsonrpc_request r = {req, req_sz, 0, 0, 0, 0, 0,
0, &jsonrpc_printer, NULL, NULL};
d.fn = fn;
d.fndata = fndata;
r.fndata = &d;
// Is is a response frame?
mjson_find(req, req_sz, "$.result", &result, &result_sz);
if (result == NULL) mjson_find(req, req_sz, "$.error", &error, &error_sz);
if (result_sz > 0 || error_sz > 0) {
if (ctx->response_cb != NULL) ctx->response_cb(req, req_sz, ctx->userdata);
return;
}
// Method must exist and must be a string
if (mjson_find(req, req_sz, "$.method", &r.method, &r.method_len) !=
MJSON_TOK_STRING) {
mjson_printf(fn, fndata, "{\"error\":{\"code\":-32700,\"message\":%.*Q}}\n",
req_sz, req);
ctx->in_len = 0;
return;
}
// id and params are optional
mjson_find(req, req_sz, "$.id", &r.id, &r.id_len);
mjson_find(req, req_sz, "$.params", &r.params, &r.params_len);
for (m = ctx->methods; m != NULL; m = m->next) {
if (mjson_globmatch(m->method, m->method_sz, r.method + 1,
r.method_len - 2) > 0) {
if (r.params == NULL) r.params = "";
r.userdata = m->cbdata;
m->cb(&r);
break;
}
}
if (m == NULL) {
jsonrpc_return_error(&r, JSONRPC_ERROR_NOT_FOUND, "method not found", NULL);
}
}
static int jsonrpc_print_methods(mjson_print_fn_t fn, void *fndata,
va_list *ap) {
struct jsonrpc_ctx *ctx = va_arg(*ap, struct jsonrpc_ctx *);
struct jsonrpc_method *m;
int len = 0;
for (m = ctx->methods; m != NULL; m = m->next) {
if (m != ctx->methods) len += mjson_print_buf(fn, fndata, ",", 1);
len += mjson_print_str(fn, fndata, m->method, strlen(m->method));
}
return len;
}
static void rpclist(struct jsonrpc_request *r) {
jsonrpc_return_success(r, "[%M]", jsonrpc_print_methods, r->userdata);
}
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t response_cb,
void *userdata) {
ctx->response_cb = response_cb;
ctx->userdata = userdata;
jsonrpc_ctx_export(ctx, MJSON_RPC_LIST_NAME, rpclist, ctx);
}
void jsonrpc_ctx_process_byte(struct jsonrpc_ctx *ctx, unsigned char ch,
mjson_print_fn_t fn, void *p) {
if (ctx->in_len >= (int) sizeof(ctx->in)) ctx->in_len = 0; // Overflow
if (ch == '\n') { // If new line, parse frame
if (ctx->in_len > 1) jsonrpc_ctx_process(ctx, ctx->in, ctx->in_len, fn, p);
ctx->in_len = 0;
} else {
ctx->in[ctx->in_len] = ch; // Append to the buffer
ctx->in_len++;
}
}
void jsonrpc_init(mjson_print_fn_t response_cb, void *userdata) {
jsonrpc_ctx_init(&jsonrpc_default_context, response_cb, userdata);
}
#endif // MJSON_ENABLE_RPC

View File

@ -1,220 +0,0 @@
// Copyright (c) 2018-2020 Cesanta Software Limited
// All rights reserved
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef MJSON_H
#define MJSON_H
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef MJSON_ENABLE_PRINT
#define MJSON_ENABLE_PRINT 1
#endif
#ifndef MJSON_ENABLE_RPC
#define MJSON_ENABLE_RPC 1
#endif
#ifndef MJSON_ENABLE_BASE64
#define MJSON_ENABLE_BASE64 1
#endif
#ifndef MJSON_ENABLE_MERGE
#define MJSON_ENABLE_MERGE 0
#elif MJSON_ENABLE_MERGE
#define MJSON_ENABLE_NEXT 1
#endif
#ifndef MJSON_ENABLE_PRETTY
#define MJSON_ENABLE_PRETTY 0
#elif MJSON_ENABLE_PRETTY
#define MJSON_ENABLE_NEXT 1
#endif
#ifndef MJSON_ENABLE_NEXT
#define MJSON_ENABLE_NEXT 0
#endif
#ifndef MJSON_RPC_IN_BUF_SIZE
#define MJSON_RPC_IN_BUF_SIZE 256
#endif
#ifndef MJSON_RPC_LIST_NAME
#define MJSON_RPC_LIST_NAME "rpc.list"
#endif
#ifdef __cplusplus
extern "C" {
#endif
enum {
MJSON_ERROR_INVALID_INPUT = -1,
MJSON_ERROR_TOO_DEEP = -2,
};
enum mjson_tok {
MJSON_TOK_INVALID = 0,
MJSON_TOK_KEY = 1,
MJSON_TOK_STRING = 11,
MJSON_TOK_NUMBER = 12,
MJSON_TOK_TRUE = 13,
MJSON_TOK_FALSE = 14,
MJSON_TOK_NULL = 15,
MJSON_TOK_ARRAY = 91,
MJSON_TOK_OBJECT = 123,
};
#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
typedef int (*mjson_cb_t)(int ev, const char *s, int off, int len, void *ud);
#ifndef MJSON_MAX_DEPTH
#define MJSON_MAX_DEPTH 20
#endif
int mjson(const char *s, int len, mjson_cb_t cb, void *ud);
enum mjson_tok mjson_find(const char *s, int len, const char *jp,
const char **tokptr, int *toklen);
int mjson_get_number(const char *s, int len, const char *path, double *v);
int mjson_get_bool(const char *s, int len, const char *path, int *v);
int mjson_get_string(const char *s, int len, const char *path, char *to, int n);
int mjson_get_hex(const char *s, int len, const char *path, char *to, int n);
#if MJSON_ENABLE_NEXT
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
int *vlen, int *vtype);
#endif
#if MJSON_ENABLE_BASE64
int mjson_get_base64(const char *s, int len, const char *path, char *to, int n);
int mjson_base64_dec(const char *src, int n, char *dst, int dlen);
#endif
#if MJSON_ENABLE_PRINT
typedef int (*mjson_print_fn_t)(const char *buf, int len, void *userdata);
typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t, void *, va_list *);
struct mjson_fixedbuf {
char *ptr;
int size, len;
};
int mjson_printf(mjson_print_fn_t, void *, const char *fmt, ...);
int mjson_vprintf(mjson_print_fn_t, void *, const char *fmt, va_list ap);
int mjson_print_str(mjson_print_fn_t, void *, const char *s, int len);
int mjson_print_int(mjson_print_fn_t, void *, int value, int is_signed);
int mjson_print_long(mjson_print_fn_t, void *, long value, int is_signed);
int mjson_print_buf(mjson_print_fn_t fn, void *, const char *buf, int len);
int mjson_print_null(const char *ptr, int len, void *userdata);
int mjson_print_file(const char *ptr, int len, void *userdata);
int mjson_print_fixed_buf(const char *ptr, int len, void *userdata);
int mjson_print_dynamic_buf(const char *ptr, int len, void *userdata);
#if MJSON_ENABLE_PRETTY
int mjson_pretty(const char *, int, const char *, mjson_print_fn_t, void *);
#endif
#if MJSON_ENABLE_MERGE
int mjson_merge(const char *, int, const char *, int, mjson_print_fn_t, void *);
#endif
#endif // MJSON_ENABLE_PRINT
#if MJSON_ENABLE_RPC
void jsonrpc_init(mjson_print_fn_t, void *userdata);
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
struct jsonrpc_request {
const char *frame; // Points to the whole frame
int frame_len; // Frame length
const char *params; // Points to the "params" in the request frame
int params_len; // Length of the "params"
const char *id; // Points to the "id" in the request frame
int id_len; // Length of the "id"
const char *method; // Points to the "method" in the request frame
int method_len; // Length of the "method"
mjson_print_fn_t fn; // Printer function
void *fndata; // Printer function data
void *userdata; // Callback's user data as specified at export time
};
struct jsonrpc_method {
const char *method;
int method_sz;
void (*cb)(struct jsonrpc_request *);
void *cbdata;
struct jsonrpc_method *next;
};
// Main RPC context, stores current request information and a list of
// exported RPC methods.
struct jsonrpc_ctx {
struct jsonrpc_method *methods;
void *userdata;
mjson_print_fn_t response_cb;
int in_len;
char in[MJSON_RPC_IN_BUF_SIZE];
};
// Registers function fn under the given name within the given RPC context
#define jsonrpc_ctx_export(ctx, name, fn, ud) \
do { \
static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0, 0}; \
m.cbdata = (ud); \
m.next = (ctx)->methods; \
(ctx)->methods = &m; \
} while (0)
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t, void *);
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
const char *message, const char *data_fmt, ...);
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
...);
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
mjson_print_fn_t fn, void *fndata);
void jsonrpc_ctx_process_byte(struct jsonrpc_ctx *ctx, unsigned char ch,
mjson_print_fn_t fn, void *fndata);
extern struct jsonrpc_ctx jsonrpc_default_context;
#define jsonrpc_export(name, fn, ud) \
jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn), (ud))
#define jsonrpc_process(buf, len, fn, data) \
jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (data))
#define jsonrpc_process_byte(x, fn, data) \
jsonrpc_ctx_process_byte(&jsonrpc_default_context, (x), (fn), (data))
#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
#endif // MJSON_ENABLE_RPC
#ifdef __cplusplus
}
#endif
#endif // MJSON_H

View File

@ -1,76 +0,0 @@
const {h, render, Component} = preact;
const html = htm.bind(h);
const Info = () => html`<div id="info">
This dashboard shows values kept in server memory. Many clients
can open this page. As soon as any client changes any value,
all clients update automatically.
<br/><br/>
The JS code that watches state changes, reconnects on network failures.
That means if server restarts, dashboard on all connected clients
refresh automatically.
<br/><br/>
You can use <code>curl</code> command-line utility:
<br/><code>curl localhost:8000/api/config/get</code>
<br/><code>curl localhost:8000/api/config/watch</code>
<br/><code>curl localhost:8000/api/config/set -d '{"a":123}'</code>
</div>`;
class Dashboard extends Component {
state = {value1: null, value2: null};
componentDidMount() {
axios.get('/api/config/get')
.then(r => this.setState(r.data))
.catch(e => console.log(e));
var self = this;
var f = function(reader) {
return reader.read().then(function(result) {
var data = String.fromCharCode.apply(null, result.value);
self.setState(JSON.parse(data));
// console.log(JSON.parse(data));
if (!result.done) return f(reader);
});
};
fetch('/api/config/watch')
.then(r => r.body.getReader())
.then(f)
.catch(e => setTimeout(x => self.componentDidMount(), 1000));
}
render(props, state) {
return html
`<div id="dashboard" style="display: flex; justify-content: flex-evenly;">
<div style="width:50%;text-align:center;">Value 1 (number):
<div style="font-size:140%;"><b>${state.value1}</b></div></div>
<div style="width:50%;text-align:center;">Value 2 (string):
<div style="font-size:140%;"><b>${state.value2}</b></div></div>
</div>`
}
};
class Form extends Component {
render(props, state) {
const onclick = ev => axios.post(
'/api/config/set', {value1: +state.value1, value2: state.value2});
// alert(JSON.stringify(state));
return html`<div id="form">
<div><b>Change values</b></div>
<table>
<tr><td>Value 1 (number):</td><td><input value=${state.value1}
onInput=${linkState(this, 'value1')} /></td></tr>
<tr><td>Value 2 (string):</td><td><input value=${state.value2}
onInput=${linkState(this, 'value2')}/></td></tr>
<tr><td></td>
<td><button onClick=${onclick}>Save values</button></td></tr>
</table>
</div>`
}
};
const App = () => html`<div id="container">
<${Info} />
<${Dashboard} />
<${Form} />
</div>`;
window.onload = () => render(h(App), document.body);

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
!function(){function n(n){var t=e.get(this);return t||(t=new Map,e.set(this,t)),1<(t=a(this,t.get(n)||(t.set(n,t=function(n){function t(n){1===u&&(n||(r=r.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?p.push(0,n,r):3===u&&(n||r)?(p.push(3,n,r),u=2):2===u&&"..."===r&&n?p.push(4,n,0):2===u&&r&&!n?p.push(5,0,!0,r):5<=u&&((r||!n&&5===u)&&(p.push(u,0,r,s),u=6),n&&(p.push(u,n,0,s),u=6)),r=""}for(var e,s,u=1,r="",h="",p=[0],a=0;a<n.length;a++){a&&(1===u&&t(),t(a));for(var o=0;o<n[a].length;o++)e=n[a][o],1===u?"<"===e?(t(),p=[p],u=3):r+=e:4===u?r="--"===r&&">"===e?(u=1,""):e+r[0]:h?e===h?h="":r+=e:'"'===e||"'"===e?h=e:">"===e?(t(),u=1):u&&("="===e?(u=5,s=r,r=""):"/"===e&&(u<5||">"===n[a][o+1])?(t(),3===u&&(p=p[0]),(p=(u=p)[0]).push(2,0,u),u=0):" "===e||"\t"===e||"\n"===e||"\r"===e?(t(),u=2):r+=e),3===u&&"!--"===r&&(u=4,p=p[0])}return t(),p}(n)),t),arguments,[])).length?t:t[0]}var a=function(n,t,e,s){var u;t[0]=0;for(var r=1;r<t.length;r++){var h=t[r++],p=t[r]?(t[0]|=h?1:2,e[t[r++]]):t[++r];3===h?s[0]=p:4===h?s[1]=Object.assign(s[1]||{},p):5===h?(s[1]=s[1]||{})[t[++r]]=p:6===h?s[1][t[++r]]+=p+"":h?(u=n.apply(p,a(n,p,e,["",null])),s.push(u),p[0]?t[0]|=2:(t[r-2]=0,t[r]=u)):s.push(p)}return s},e=new Map;"undefined"!=typeof module?module.exports=n:self.htm=n}();

View File

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>example</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<script src="preact.min.js"></script>
<script src="htm.min.js"></script>
<script src="linkstate.min.js"></script>
<script src="axios.min.js"></script>
<script src="app.js"></script>
</head>
<body></body>
</html>

View File

@ -1,18 +0,0 @@
function linkState(component, key, eventPath) {
let path = key.split('.'), cache = component.__lsc || (component.__lsc = {});
return cache[key + eventPath] || (cache[key + eventPath] = function(e) {
let t = e && e.target || this, state = {}, obj = state,
v = typeof eventPath === 'string' ?
delve(e, eventPath) :
t.nodeName ? (t.type.match(/^che|rad/) ? t.checked : t.value) :
e,
i = 0;
for (; i < path.length - 1; i++) {
obj = obj[path[i]] ||
(obj[path[i]] = !i && component.state[path[i]] || {});
}
obj[path[i]] = v;
component.setState(state);
});
}

File diff suppressed because one or more lines are too long

View File

@ -1,6 +0,0 @@
#container { margin-right: auto; margin-left: auto; max-width: 480px; }
#info { background: #d0f0f0; border-radius: .5em; padding: 2em; }
#dashboard { background: #f0f0f0; border-radius: .5em; padding: 0.5em; max-height: 10em; overflow: auto; height: 100%; margin-top: 1em; }
#form { background: #d0c0f0; border-radius: .5em; padding: 0.5em; max-height: 10em; overflow: auto; height: 100%; margin-top: 1em; }
button { width: 100%; }
button, select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }

View File

@ -4,16 +4,14 @@ This example is a demonstration of how Mongoose Library could be integrated
into an embedded device and provide a complete device dashboard with the
following features:
- Authentication: login-protected dashboard
- Multiple logins with different permissions (admin and user)
- Login screen for non-authenticated connections
- A [preact](https://preactjs.com/)-based dashboard with multiple pages
- Web UI is fully embedded into the server/firmware binary, and does not
need a filesystem to serve it
need a filesystem to serve it. UI is resilient to FS problems
- View and change server settings
- All changes are propagates to all connected clients
- Live interactive chat that demonstrates bi-directional data exchange
# Screenshots
This is a login screen that prompts for user/password
@ -22,10 +20,6 @@ This is a login screen that prompts for user/password
# Main dashboard
A main dashboard page shows internal data, live video stream, and a form
A main dashboard page shows device settings form, and live chat
![](screenshots/dashboard.png)
A live log page shows live log stream coming from device
![](screenshots/logs.png)

View File

@ -334,446 +334,455 @@ static const unsigned char v2[] = {
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 125, 41, 46, 99, 97, 116, 99, 104, 40, 101, // }).catch(e
114, 114, 32, 61, 62, 32, 101, 114, 114, 41, 59, 10, // rr => err);.
32, 32, 114, 101, 116, 117, 114, 110, 32, 104, 116, 109, // return htm
108, 96, 10, 60, 100, 105, 118, 32, 115, 116, 121, 108, // l`.<div styl
101, 61, 34, 109, 97, 114, 103, 105, 110, 58, 32, 48, // e="margin: 0
32, 48, 46, 51, 101, 109, 59, 34, 62, 10, 32, 32, // 0.3em;">.
60, 104, 51, 32, 115, 116, 121, 108, 101, 61, 34, 98, // <h3 style="b
97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, 35, // ackground: #
99, 48, 51, 52, 51, 52, 59, 32, 99, 111, 108, 111, // c03434; colo
114, 58, 32, 35, 102, 102, 102, 59, 32, 112, 97, 100, // r: #fff; pad
100, 105, 110, 103, 58, 32, 48, 46, 52, 101, 109, 59, // ding: 0.4em;
34, 62, 10, 32, 32, 32, 32, 67, 104, 97, 110, 103, // ">. Chang
101, 32, 99, 111, 110, 102, 105, 103, 117, 114, 97, 116, // e configurat
105, 111, 110, 60, 47, 104, 51, 62, 10, 32, 32, 60, // ion</h3>. <
100, 105, 118, 32, 115, 116, 121, 108, 101, 61, 34, 109, // div style="m
97, 114, 103, 105, 110, 58, 32, 48, 46, 53, 101, 109, // argin: 0.5em
32, 48, 59, 34, 62, 10, 32, 32, 32, 32, 60, 115, // 0;">. <s
112, 97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 97, // pan class="a
100, 100, 111, 110, 34, 62, 118, 97, 108, 117, 101, 49, // ddon">value1
58, 60, 47, 115, 112, 97, 110, 62, 10, 32, 32, 32, // :</span>.
32, 60, 105, 110, 112, 117, 116, 32, 116, 121, 112, 101, // <input type
61, 34, 116, 101, 120, 116, 34, 32, 118, 97, 108, 117, // ="text" valu
101, 61, 36, 123, 118, 97, 108, 117, 101, 49, 125, 10, // e=${value1}.
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 111, 110, // on
105, 110, 112, 117, 116, 61, 36, 123, 101, 118, 32, 61, // input=${ev =
62, 32, 115, 101, 116, 86, 97, 108, 117, 101, 49, 40, // > setValue1(
101, 118, 46, 116, 97, 114, 103, 101, 116, 46, 118, 97, // ev.target.va
108, 117, 101, 41, 125, 32, 47, 62, 10, 32, 32, 32, // lue)} />.
32, 60, 98, 117, 116, 116, 111, 110, 32, 99, 108, 97, // <button cla
115, 115, 61, 34, 98, 116, 110, 34, 32, 100, 105, 115, // ss="btn" dis
97, 98, 108, 101, 100, 61, 36, 123, 33, 118, 97, 108, // abled=${!val
117, 101, 49, 125, 10, 32, 32, 32, 32, 32, 32, 111, // ue1}. o
110, 99, 108, 105, 99, 107, 61, 36, 123, 101, 118, 32, // nclick=${ev
61, 62, 32, 117, 112, 100, 97, 116, 101, 40, 39, 118, // => update('v
97, 108, 117, 101, 49, 39, 44, 32, 118, 97, 108, 117, // alue1', valu
101, 49, 41, 125, 10, 32, 32, 32, 32, 32, 32, 115, // e1)}. s
116, 121, 108, 101, 61, 34, 109, 97, 114, 103, 105, 110, // tyle="margin
45, 108, 101, 102, 116, 58, 32, 49, 101, 109, 59, 32, // -left: 1em;
98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, // background:
35, 56, 97, 97, 59, 34, 62, 85, 112, 100, 97, 116, // #8aa;">Updat
101, 60, 47, 98, 117, 116, 116, 111, 110, 62, 10, 32, // e</button>.
32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 60, 100, // </div>. <d
105, 118, 32, 115, 116, 121, 108, 101, 61, 34, 109, 97, // iv style="ma
114, 103, 105, 110, 58, 32, 48, 46, 53, 101, 109, 32, // rgin: 0.5em
48, 59, 34, 62, 10, 32, 32, 32, 32, 60, 115, 112, // 0;">. <sp
97, 110, 32, 99, 108, 97, 115, 115, 61, 34, 97, 100, // an class="ad
100, 111, 110, 34, 62, 118, 97, 108, 117, 101, 50, 58, // don">value2:
60, 47, 115, 112, 97, 110, 62, 10, 32, 32, 32, 32, // </span>.
60, 105, 110, 112, 117, 116, 32, 116, 121, 112, 101, 61, // <input type=
34, 116, 101, 120, 116, 34, 32, 118, 97, 108, 117, 101, // "text" value
61, 36, 123, 118, 97, 108, 117, 101, 50, 125, 10, 32, // =${value2}.
32, 32, 32, 32, 32, 32, 32, 32, 32, 111, 110, 105, // oni
110, 112, 117, 116, 61, 36, 123, 101, 118, 32, 61, 62, // nput=${ev =>
32, 115, 101, 116, 86, 97, 108, 117, 101, 50, 40, 101, // setValue2(e
118, 46, 116, 97, 114, 103, 101, 116, 46, 118, 97, 108, // v.target.val
117, 101, 41, 125, 32, 47, 62, 10, 32, 32, 32, 32, // ue)} />.
60, 98, 117, 116, 116, 111, 110, 32, 99, 108, 97, 115, // <button clas
115, 61, 34, 98, 116, 110, 34, 32, 100, 105, 115, 97, // s="btn" disa
98, 108, 101, 100, 61, 36, 123, 33, 118, 97, 108, 117, // bled=${!valu
101, 50, 125, 10, 32, 32, 32, 32, 32, 32, 111, 110, // e2}. on
99, 108, 105, 99, 107, 61, 36, 123, 101, 118, 32, 61, // click=${ev =
62, 32, 117, 112, 100, 97, 116, 101, 40, 39, 118, 97, // > update('va
108, 117, 101, 50, 39, 44, 32, 118, 97, 108, 117, 101, // lue2', value
50, 41, 125, 10, 32, 32, 32, 32, 32, 32, 115, 116, // 2)}. st
121, 108, 101, 61, 34, 109, 97, 114, 103, 105, 110, 45, // yle="margin-
108, 101, 102, 116, 58, 32, 49, 101, 109, 59, 32, 98, // left: 1em; b
97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, 35, // ackground: #
56, 97, 97, 59, 34, 62, 85, 112, 100, 97, 116, 101, // 8aa;">Update
60, 47, 98, 117, 116, 116, 111, 110, 62, 10, 32, 32, // </button>.
60, 47, 100, 105, 118, 62, 10, 32, 32, 60, 100, 105, // </div>. <di
118, 32, 99, 108, 97, 115, 115, 61, 34, 109, 115, 103, // v class="msg
34, 62, 10, 32, 32, 32, 32, 65, 115, 32, 115, 111, // ">. As so
111, 110, 32, 97, 115, 32, 97, 100, 109, 105, 110, 105, // on as admini
115, 116, 114, 97, 116, 111, 114, 32, 117, 112, 100, 97, // strator upda
116, 101, 115, 32, 99, 111, 110, 102, 105, 103, 117, 114, // tes configur
97, 116, 105, 111, 110, 44, 32, 10, 32, 32, 32, 32, // ation, .
115, 101, 114, 118, 101, 114, 32, 105, 116, 101, 114, 97, // server itera
116, 101, 115, 32, 111, 118, 101, 114, 32, 97, 108, 108, // tes over all
32, 99, 111, 110, 110, 101, 99, 116, 101, 100, 32, 99, // connected c
108, 105, 101, 110, 116, 115, 32, 97, 110, 100, 32, 115, // lients and s
101, 110, 100, 115, 32, 117, 112, 100, 97, 116, 101, 10, // ends update.
32, 32, 32, 32, 110, 111, 116, 105, 102, 105, 99, 97, // notifica
116, 105, 111, 110, 115, 32, 116, 111, 32, 97, 108, 108, // tions to all
32, 111, 102, 32, 116, 104, 101, 109, 32, 45, 32, 115, // of them - s
111, 32, 116, 104, 101, 121, 32, 117, 112, 100, 97, 116, // o they updat
101, 32, 97, 117, 116, 111, 109, 97, 116, 105, 99, 97, // e automatica
108, 108, 121, 46, 10, 32, 32, 60, 47, 100, 105, 118, // lly.. </div
62, 10, 60, 47, 100, 105, 118, 62, 96, 59, 10, 125, // >.</div>`;.}
59, 10, 10, 99, 111, 110, 115, 116, 32, 76, 111, 103, // ;..const Log
105, 110, 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, // in = functio
110, 40, 112, 114, 111, 112, 115, 41, 32, 123, 10, 32, // n(props) {.
32, 99, 111, 110, 115, 116, 32, 91, 117, 115, 101, 114, // const [user
44, 32, 115, 101, 116, 85, 115, 101, 114, 93, 32, 61, // , setUser] =
32, 117, 115, 101, 83, 116, 97, 116, 101, 40, 39, 39, // useState(''
41, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, 91, // );. const [
112, 97, 115, 115, 44, 32, 115, 101, 116, 80, 97, 115, // pass, setPas
115, 93, 32, 61, 32, 117, 115, 101, 83, 116, 97, 116, // s] = useStat
101, 40, 39, 39, 41, 59, 10, 32, 32, 99, 111, 110, // e('');. con
115, 116, 32, 108, 111, 103, 105, 110, 32, 61, 32, 101, // st login = e
118, 32, 61, 62, 10, 32, 32, 32, 32, 32, 32, 102, // v =>. f
101, 116, 99, 104, 40, 10, 32, 32, 32, 32, 32, 32, // etch(.
32, 32, 32, 32, 39, 47, 97, 112, 105, 47, 108, 111, // '/api/lo
103, 105, 110, 39, 44, 10, 32, 32, 32, 32, 32, 32, // gin',.
32, 32, 32, 32, 123, 104, 101, 97, 100, 101, 114, 115, // {headers
58, 32, 123, 65, 117, 116, 104, 111, 114, 105, 122, 97, // : {Authoriza
116, 105, 111, 110, 58, 32, 39, 66, 97, 115, 105, 99, // tion: 'Basic
32, 39, 32, 43, 32, 98, 116, 111, 97, 40, 117, 115, // ' + btoa(us
101, 114, 32, 43, 32, 39, 58, 39, 32, 43, 32, 112, // er + ':' + p
97, 115, 115, 41, 125, 125, 41, 10, 32, 32, 32, 32, // ass)}}).
32, 32, 32, 32, 32, 32, 46, 116, 104, 101, 110, 40, // .then(
114, 32, 61, 62, 32, 114, 46, 106, 115, 111, 110, 40, // r => r.json(
41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, // )).
32, 46, 116, 104, 101, 110, 40, 114, 32, 61, 62, 32, // .then(r =>
114, 32, 38, 38, 32, 112, 114, 111, 112, 115, 46, 108, // r && props.l
111, 103, 105, 110, 40, 114, 41, 41, 10, 32, 32, 32, // ogin(r)).
32, 32, 32, 32, 32, 32, 32, 46, 99, 97, 116, 99, // .catc
104, 40, 101, 114, 114, 32, 61, 62, 32, 101, 114, 114, // h(err => err
41, 59, 10, 32, 32, 114, 101, 116, 117, 114, 110, 32, // );. return
104, 116, 109, 108, 96, 10, 60, 100, 105, 118, 32, 99, // html`.<div c
108, 97, 115, 115, 61, 34, 114, 111, 117, 110, 100, 101, // lass="rounde
100, 32, 98, 111, 114, 100, 101, 114, 34, 32, 115, 116, // d border" st
121, 108, 101, 61, 34, 109, 97, 120, 45, 119, 105, 100, // yle="max-wid
116, 104, 58, 32, 52, 56, 48, 112, 120, 59, 32, 109, // th: 480px; m
97, 114, 103, 105, 110, 58, 32, 48, 32, 97, 117, 116, // argin: 0 aut
111, 59, 32, 109, 97, 114, 103, 105, 110, 45, 116, 111, // o; margin-to
112, 58, 32, 53, 101, 109, 59, 32, 98, 97, 99, 107, // p: 5em; back
103, 114, 111, 117, 110, 100, 58, 32, 35, 101, 101, 101, // ground: #eee
59, 32, 34, 62, 10, 32, 32, 60, 100, 105, 118, 32, // ; ">. <div
115, 116, 121, 108, 101, 61, 34, 112, 97, 100, 100, 105, // style="paddi
110, 103, 58, 32, 50, 101, 109, 59, 32, 34, 62, 10, // ng: 2em; ">.
32, 32, 32, 32, 60, 104, 49, 32, 115, 116, 121, 108, // <h1 styl
101, 61, 34, 99, 111, 108, 111, 114, 58, 32, 35, 54, // e="color: #6
54, 54, 59, 34, 62, 68, 101, 118, 105, 99, 101, 32, // 66;">Device
68, 97, 115, 104, 98, 111, 97, 114, 100, 32, 76, 111, // Dashboard Lo
103, 105, 110, 32, 60, 47, 104, 49, 62, 10, 32, 32, // gin </h1>.
32, 32, 60, 100, 105, 118, 32, 115, 116, 121, 108, 101, // <div style
61, 34, 109, 97, 114, 103, 105, 110, 58, 32, 48, 46, // ="margin: 0.
53, 101, 109, 32, 48, 59, 34, 62, 10, 32, 32, 32, // 5em 0;">.
32, 32, 32, 60, 105, 110, 112, 117, 116, 32, 116, 121, // <input ty
112, 101, 61, 39, 116, 101, 120, 116, 39, 32, 112, 108, // pe='text' pl
97, 99, 101, 104, 111, 108, 100, 101, 114, 61, 39, 78, // aceholder='N
97, 109, 101, 39, 32, 115, 116, 121, 108, 101, 61, 34, // ame' style="
119, 105, 100, 116, 104, 58, 32, 49, 48, 48, 37, 59, // width: 100%;
34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 111, 110, // ". on
105, 110, 112, 117, 116, 61, 36, 123, 101, 118, 32, 61, // input=${ev =
62, 32, 115, 101, 116, 85, 115, 101, 114, 40, 101, 118, // > setUser(ev
46, 116, 97, 114, 103, 101, 116, 46, 118, 97, 108, 117, // .target.valu
101, 41, 125, 32, 118, 97, 108, 117, 101, 61, 36, 123, // e)} value=${
117, 115, 101, 114, 125, 32, 47, 62, 10, 32, 32, 32, // user} />.
32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, // </div>.
60, 100, 105, 118, 32, 115, 116, 121, 108, 101, 61, 34, // <div style="
109, 97, 114, 103, 105, 110, 58, 32, 48, 46, 53, 101, // margin: 0.5e
109, 32, 48, 59, 34, 62, 10, 32, 32, 32, 32, 32, // m 0;">.
32, 60, 105, 110, 112, 117, 116, 32, 116, 121, 112, 101, // <input type
61, 34, 112, 97, 115, 115, 119, 111, 114, 100, 34, 32, // ="password"
112, 108, 97, 99, 101, 104, 111, 108, 100, 101, 114, 61, // placeholder=
34, 80, 97, 115, 115, 119, 111, 114, 100, 34, 32, 115, // "Password" s
116, 121, 108, 101, 61, 34, 119, 105, 100, 116, 104, 58, // tyle="width:
32, 49, 48, 48, 37, 59, 34, 10, 32, 32, 32, 32, // 100%;".
32, 32, 32, 32, 111, 110, 105, 110, 112, 117, 116, 61, // oninput=
36, 123, 101, 118, 32, 61, 62, 32, 115, 101, 116, 80, // ${ev => setP
97, 115, 115, 40, 101, 118, 46, 116, 97, 114, 103, 101, // ass(ev.targe
116, 46, 118, 97, 108, 117, 101, 41, 125, 32, 118, 97, // t.value)} va
108, 117, 101, 61, 36, 123, 112, 97, 115, 115, 125, 10, // lue=${pass}.
32, 32, 32, 32, 32, 32, 32, 32, 111, 110, 99, 104, // onch
97, 110, 103, 101, 61, 36, 123, 108, 111, 103, 105, 110, // ange=${login
125, 32, 47, 62, 10, 32, 32, 32, 32, 60, 47, 100, // } />. </d
105, 118, 62, 10, 32, 32, 32, 32, 60, 100, 105, 118, // iv>. <div
32, 115, 116, 121, 108, 101, 61, 34, 109, 97, 114, 103, // style="marg
105, 110, 58, 32, 49, 101, 109, 32, 48, 59, 34, 62, // in: 1em 0;">
10, 32, 32, 32, 32, 32, 32, 60, 98, 117, 116, 116, // . <butt
111, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 116, // on class="bt
110, 34, 32, 115, 116, 121, 108, 101, 61, 34, 119, 105, // n" style="wi
100, 116, 104, 58, 32, 49, 48, 48, 37, 59, 32, 98, // dth: 100%; b
97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, 35, // ackground: #
56, 97, 97, 59, 34, 10, 32, 32, 32, 32, 32, 32, // 8aa;".
32, 32, 100, 105, 115, 97, 98, 108, 101, 100, 61, 36, // disabled=$
123, 33, 117, 115, 101, 114, 32, 124, 124, 32, 33, 112, // {!user || !p
97, 115, 115, 125, 32, 111, 110, 99, 108, 105, 99, 107, // ass} onclick
61, 36, 123, 108, 111, 103, 105, 110, 125, 62, 32, 76, // =${login}> L
111, 103, 105, 110, 32, 60, 47, 98, 117, 116, 116, 111, // ogin </butto
110, 62, 10, 32, 32, 32, 32, 60, 47, 100, 105, 118, // n>. </div
62, 10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 115, // >. <div s
116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, 58, // tyle="color:
32, 35, 55, 55, 55, 59, 32, 109, 97, 114, 103, 105, // #777; margi
110, 45, 116, 111, 112, 58, 32, 50, 101, 109, 59, 34, // n-top: 2em;"
62, 10, 32, 32, 32, 32, 32, 32, 86, 97, 108, 105, // >. Vali
100, 32, 108, 111, 103, 105, 110, 115, 58, 32, 97, 100, // d logins: ad
109, 105, 110, 58, 112, 97, 115, 115, 48, 44, 32, 117, // min:pass0, u
115, 101, 114, 49, 58, 112, 97, 115, 115, 49, 44, 32, // ser1:pass1,
117, 115, 101, 114, 50, 58, 112, 97, 115, 115, 50, 10, // user2:pass2.
32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, // </div>.
32, 60, 47, 100, 105, 118, 62, 10, 60, 47, 100, 105, // </div>.</di
118, 62, 96, 59, 10, 125, 59, 10, 10, 99, 111, 110, // v>`;.};..con
115, 116, 32, 77, 101, 115, 115, 97, 103, 101, 32, 61, // st Message =
32, 116, 101, 120, 116, 32, 61, 62, 32, 104, 116, 109, // text => htm
108, 96, 60, 100, 105, 118, 32, 115, 116, 121, 108, 101, // l`<div style
61, 34, 109, 97, 114, 103, 105, 110, 58, 32, 48, 46, // ="margin: 0.
53, 101, 109, 32, 48, 59, 34, 62, 36, 123, 116, 101, // 5em 0;">${te
120, 116, 125, 60, 47, 100, 105, 118, 62, 96, 59, 10, // xt}</div>`;.
10, 99, 111, 110, 115, 116, 32, 67, 104, 97, 116, 32, // .const Chat
61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 112, // = function(p
114, 111, 112, 115, 41, 32, 123, 10, 32, 32, 99, 111, // rops) {. co
110, 115, 116, 32, 91, 109, 101, 115, 115, 97, 103, 101, // nst [message
44, 32, 115, 101, 116, 77, 101, 115, 115, 97, 103, 101, // , setMessage
93, 32, 61, 32, 117, 115, 101, 83, 116, 97, 116, 101, // ] = useState
40, 39, 39, 41, 59, 10, 32, 32, 99, 111, 110, 115, // ('');. cons
116, 32, 115, 101, 110, 100, 109, 101, 115, 115, 97, 103, // t sendmessag
101, 32, 61, 32, 101, 118, 32, 61, 62, 32, 102, 101, // e = ev => fe
116, 99, 104, 40, 39, 47, 97, 112, 105, 47, 109, 101, // tch('/api/me
115, 115, 97, 103, 101, 47, 115, 101, 110, 100, 39, 44, // ssage/send',
32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, // {.
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 32, 32, 32, 32, 32, 32, 32, 109, 101, 116, // met
104, 111, 100, 58, 32, 39, 112, 111, 115, 116, 39, 44, // hod: 'post',
10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // .
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 32, 32, 32, 32, 32, 98, 111, 100, 121, 58, // body:
32, 96, 109, 101, 115, 115, 97, 103, 101, 61, 36, 123, // `message=${
101, 110, 99, 111, 100, 101, 85, 82, 73, 67, 111, 109, // encodeURICom
112, 111, 110, 101, 110, 116, 40, 109, 101, 115, 115, 97, // ponent(messa
103, 101, 41, 125, 96, 10, 32, 32, 32, 32, 32, 32, // ge)}`.
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 41, // })
46, 116, 104, 101, 110, 40, 114, 32, 61, 62, 32, 115, // .then(r => s
101, 116, 77, 101, 115, 115, 97, 103, 101, 40, 39, 39, // etMessage(''
41, 41, 59, 10, 10, 32, 32, 99, 111, 110, 115, 116, // ));.. const
32, 109, 101, 115, 115, 97, 103, 101, 115, 32, 61, 32, // messages =
112, 114, 111, 112, 115, 46, 109, 101, 115, 115, 97, 103, // props.messag
101, 115, 46, 109, 97, 112, 40, 10, 32, 32, 32, 32, // es.map(.
32, 32, 116, 101, 120, 116, 32, 61, 62, 32, 104, 116, // text => ht
109, 108, 96, 60, 100, 105, 118, 32, 115, 116, 121, 108, // ml`<div styl
101, 61, 34, 109, 97, 114, 103, 105, 110, 58, 32, 48, // e="margin: 0
46, 53, 101, 109, 32, 48, 59, 34, 62, 36, 123, 116, // .5em 0;">${t
101, 120, 116, 125, 60, 47, 100, 105, 118, 62, 96, 41, // ext}</div>`)
32, 32, 99, 111, 110, 115, 116, 32, 117, 112, 100, 97, // const upda
116, 101, 118, 97, 108, 117, 101, 49, 32, 61, 32, 101, // tevalue1 = e
118, 32, 61, 62, 32, 117, 112, 100, 97, 116, 101, 40, // v => update(
39, 118, 97, 108, 117, 101, 49, 39, 44, 32, 118, 97, // 'value1', va
108, 117, 101, 49, 41, 59, 10, 32, 32, 99, 111, 110, // lue1);. con
115, 116, 32, 117, 112, 100, 97, 116, 101, 118, 97, 108, // st updateval
117, 101, 50, 32, 61, 32, 101, 118, 32, 61, 62, 32, // ue2 = ev =>
117, 112, 100, 97, 116, 101, 40, 39, 118, 97, 108, 117, // update('valu
101, 50, 39, 44, 32, 118, 97, 108, 117, 101, 50, 41, // e2', value2)
59, 10, 32, 32, 114, 101, 116, 117, 114, 110, 32, 104, // ;. return h
116, 109, 108, 96, 10, 60, 100, 105, 118, 32, 115, 116, // tml`.<div st
121, 108, 101, 61, 34, 109, 97, 114, 103, 105, 110, 58, // yle="margin:
32, 48, 32, 48, 46, 51, 101, 109, 59, 34, 62, 10, // 0 0.3em;">.
32, 32, 60, 104, 51, 32, 115, 116, 121, 108, 101, 61, // <h3 style=
34, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 58, // "background:
32, 35, 51, 48, 99, 48, 52, 48, 59, 32, 99, 111, // #30c040; co
32, 35, 99, 48, 51, 52, 51, 52, 59, 32, 99, 111, // #c03434; co
108, 111, 114, 58, 32, 35, 102, 102, 102, 59, 32, 112, // lor: #fff; p
97, 100, 100, 105, 110, 103, 58, 32, 48, 46, 52, 101, // adding: 0.4e
109, 59, 34, 62, 85, 115, 101, 114, 32, 99, 104, 97, // m;">User cha
116, 60, 47, 104, 51, 62, 10, 32, 32, 60, 100, 105, // t</h3>. <di
118, 32, 115, 116, 121, 108, 101, 61, 34, 104, 101, 105, // v style="hei
103, 104, 116, 58, 32, 49, 48, 101, 109, 59, 32, 111, // ght: 10em; o
118, 101, 114, 102, 108, 111, 119, 58, 32, 97, 117, 116, // verflow: aut
111, 59, 32, 112, 97, 100, 100, 105, 110, 103, 58, 32, // o; padding:
48, 46, 53, 101, 109, 59, 32, 34, 32, 99, 108, 97, // 0.5em; " cla
115, 115, 61, 34, 98, 111, 114, 100, 101, 114, 34, 62, // ss="border">
10, 32, 32, 32, 32, 36, 123, 109, 101, 115, 115, 97, // . ${messa
103, 101, 115, 125, 10, 32, 32, 60, 47, 100, 105, 118, // ges}. </div
62, 10, 32, 32, 60, 100, 105, 118, 32, 115, 116, 121, // >. <div sty
109, 59, 34, 62, 10, 32, 32, 32, 32, 67, 104, 97, // m;">. Cha
110, 103, 101, 32, 99, 111, 110, 102, 105, 103, 117, 114, // nge configur
97, 116, 105, 111, 110, 60, 47, 104, 51, 62, 10, 32, // ation</h3>.
32, 60, 100, 105, 118, 32, 115, 116, 121, 108, 101, 61, // <div style=
34, 109, 97, 114, 103, 105, 110, 58, 32, 48, 46, 53, // "margin: 0.5
101, 109, 32, 48, 59, 34, 62, 10, 32, 32, 32, 32, // em 0;">.
60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, // <span class=
34, 97, 100, 100, 111, 110, 34, 62, 118, 97, 108, 117, // "addon">valu
101, 49, 58, 60, 47, 115, 112, 97, 110, 62, 10, 32, // e1:</span>.
32, 32, 32, 60, 105, 110, 112, 117, 116, 32, 116, 121, // <input ty
112, 101, 61, 34, 116, 101, 120, 116, 34, 32, 118, 97, // pe="text" va
108, 117, 101, 61, 36, 123, 118, 97, 108, 117, 101, 49, // lue=${value1
125, 32, 111, 110, 99, 104, 97, 110, 103, 101, 61, 36, // } onchange=$
123, 117, 112, 100, 97, 116, 101, 118, 97, 108, 117, 101, // {updatevalue
49, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 1}.
32, 111, 110, 105, 110, 112, 117, 116, 61, 36, 123, 101, // oninput=${e
118, 32, 61, 62, 32, 115, 101, 116, 86, 97, 108, 117, // v => setValu
101, 49, 40, 101, 118, 46, 116, 97, 114, 103, 101, 116, // e1(ev.target
46, 118, 97, 108, 117, 101, 41, 125, 32, 47, 62, 10, // .value)} />.
32, 32, 32, 32, 60, 98, 117, 116, 116, 111, 110, 32, // <button
99, 108, 97, 115, 115, 61, 34, 98, 116, 110, 34, 32, // class="btn"
100, 105, 115, 97, 98, 108, 101, 100, 61, 36, 123, 33, // disabled=${!
118, 97, 108, 117, 101, 49, 125, 32, 111, 110, 99, 108, // value1} oncl
105, 99, 107, 61, 36, 123, 117, 112, 100, 97, 116, 101, // ick=${update
118, 97, 108, 117, 101, 49, 125, 10, 32, 32, 32, 32, // value1}.
32, 32, 115, 116, 121, 108, 101, 61, 34, 109, 97, 114, // style="mar
103, 105, 110, 45, 108, 101, 102, 116, 58, 32, 49, 101, // gin-left: 1e
109, 59, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, // m; backgroun
100, 58, 32, 35, 56, 97, 97, 59, 34, 62, 85, 112, // d: #8aa;">Up
100, 97, 116, 101, 60, 47, 98, 117, 116, 116, 111, 110, // date</button
62, 10, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, // >. </div>.
32, 60, 100, 105, 118, 32, 115, 116, 121, 108, 101, 61, // <div style=
34, 109, 97, 114, 103, 105, 110, 58, 32, 48, 46, 53, // "margin: 0.5
101, 109, 32, 48, 59, 34, 62, 10, 32, 32, 32, 32, // em 0;">.
60, 115, 112, 97, 110, 32, 99, 108, 97, 115, 115, 61, // <span class=
34, 97, 100, 100, 111, 110, 34, 62, 118, 97, 108, 117, // "addon">valu
101, 50, 58, 60, 47, 115, 112, 97, 110, 62, 10, 32, // e2:</span>.
32, 32, 32, 60, 105, 110, 112, 117, 116, 32, 116, 121, // <input ty
112, 101, 61, 34, 116, 101, 120, 116, 34, 32, 118, 97, // pe="text" va
108, 117, 101, 61, 36, 123, 118, 97, 108, 117, 101, 50, // lue=${value2
125, 32, 111, 110, 99, 104, 97, 110, 103, 101, 61, 36, // } onchange=$
123, 117, 112, 100, 97, 116, 101, 118, 97, 108, 117, 101, // {updatevalue
50, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 2}.
32, 111, 110, 105, 110, 112, 117, 116, 61, 36, 123, 101, // oninput=${e
118, 32, 61, 62, 32, 115, 101, 116, 86, 97, 108, 117, // v => setValu
101, 50, 40, 101, 118, 46, 116, 97, 114, 103, 101, 116, // e2(ev.target
46, 118, 97, 108, 117, 101, 41, 125, 32, 47, 62, 10, // .value)} />.
32, 32, 32, 32, 60, 98, 117, 116, 116, 111, 110, 32, // <button
99, 108, 97, 115, 115, 61, 34, 98, 116, 110, 34, 32, // class="btn"
100, 105, 115, 97, 98, 108, 101, 100, 61, 36, 123, 33, // disabled=${!
118, 97, 108, 117, 101, 50, 125, 32, 111, 110, 99, 108, // value2} oncl
105, 99, 107, 61, 36, 123, 117, 112, 100, 97, 116, 101, // ick=${update
118, 97, 108, 117, 101, 50, 125, 10, 32, 32, 32, 32, // value2}.
32, 32, 115, 116, 121, 108, 101, 61, 34, 109, 97, 114, // style="mar
103, 105, 110, 45, 108, 101, 102, 116, 58, 32, 49, 101, // gin-left: 1e
109, 59, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, // m; backgroun
100, 58, 32, 35, 56, 97, 97, 59, 34, 62, 85, 112, // d: #8aa;">Up
100, 97, 116, 101, 60, 47, 98, 117, 116, 116, 111, 110, // date</button
62, 10, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, // >. </div>.
32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, // <div class=
34, 109, 115, 103, 34, 62, 10, 32, 32, 32, 32, 65, // "msg">. A
115, 32, 115, 111, 111, 110, 32, 97, 115, 32, 97, 100, // s soon as ad
109, 105, 110, 105, 115, 116, 114, 97, 116, 111, 114, 32, // ministrator
117, 112, 100, 97, 116, 101, 115, 32, 99, 111, 110, 102, // updates conf
105, 103, 117, 114, 97, 116, 105, 111, 110, 44, 32, 10, // iguration, .
32, 32, 32, 32, 115, 101, 114, 118, 101, 114, 32, 105, // server i
116, 101, 114, 97, 116, 101, 115, 32, 111, 118, 101, 114, // terates over
32, 97, 108, 108, 32, 99, 111, 110, 110, 101, 99, 116, // all connect
101, 100, 32, 99, 108, 105, 101, 110, 116, 115, 32, 97, // ed clients a
110, 100, 32, 115, 101, 110, 100, 115, 32, 117, 112, 100, // nd sends upd
97, 116, 101, 10, 32, 32, 32, 32, 110, 111, 116, 105, // ate. noti
102, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111, // fications to
32, 97, 108, 108, 32, 111, 102, 32, 116, 104, 101, 109, // all of them
32, 45, 32, 115, 111, 32, 116, 104, 101, 121, 32, 117, // - so they u
112, 100, 97, 116, 101, 32, 97, 117, 116, 111, 109, 97, // pdate automa
116, 105, 99, 97, 108, 108, 121, 46, 10, 32, 32, 60, // tically.. <
47, 100, 105, 118, 62, 10, 60, 47, 100, 105, 118, 62, // /div>.</div>
96, 59, 10, 125, 59, 10, 10, 99, 111, 110, 115, 116, // `;.};..const
32, 76, 111, 103, 105, 110, 32, 61, 32, 102, 117, 110, // Login = fun
99, 116, 105, 111, 110, 40, 112, 114, 111, 112, 115, 41, // ction(props)
32, 123, 10, 32, 32, 99, 111, 110, 115, 116, 32, 91, // {. const [
117, 115, 101, 114, 44, 32, 115, 101, 116, 85, 115, 101, // user, setUse
114, 93, 32, 61, 32, 117, 115, 101, 83, 116, 97, 116, // r] = useStat
101, 40, 39, 39, 41, 59, 10, 32, 32, 99, 111, 110, // e('');. con
115, 116, 32, 91, 112, 97, 115, 115, 44, 32, 115, 101, // st [pass, se
116, 80, 97, 115, 115, 93, 32, 61, 32, 117, 115, 101, // tPass] = use
83, 116, 97, 116, 101, 40, 39, 39, 41, 59, 10, 32, // State('');.
32, 99, 111, 110, 115, 116, 32, 108, 111, 103, 105, 110, // const login
32, 61, 32, 101, 118, 32, 61, 62, 10, 32, 32, 32, // = ev =>.
32, 32, 32, 102, 101, 116, 99, 104, 40, 10, 32, 32, // fetch(.
32, 32, 32, 32, 32, 32, 32, 32, 39, 47, 97, 112, // '/ap
105, 47, 108, 111, 103, 105, 110, 39, 44, 10, 32, 32, // i/login',.
32, 32, 32, 32, 32, 32, 32, 32, 123, 104, 101, 97, // {hea
100, 101, 114, 115, 58, 32, 123, 65, 117, 116, 104, 111, // ders: {Autho
114, 105, 122, 97, 116, 105, 111, 110, 58, 32, 39, 66, // rization: 'B
97, 115, 105, 99, 32, 39, 32, 43, 32, 98, 116, 111, // asic ' + bto
97, 40, 117, 115, 101, 114, 32, 43, 32, 39, 58, 39, // a(user + ':'
32, 43, 32, 112, 97, 115, 115, 41, 125, 125, 41, 10, // + pass)}}).
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 116, // .t
104, 101, 110, 40, 114, 32, 61, 62, 32, 114, 46, 106, // hen(r => r.j
115, 111, 110, 40, 41, 41, 10, 32, 32, 32, 32, 32, // son()).
32, 32, 32, 32, 32, 46, 116, 104, 101, 110, 40, 114, // .then(r
32, 61, 62, 32, 114, 32, 38, 38, 32, 112, 114, 111, // => r && pro
112, 115, 46, 108, 111, 103, 105, 110, 40, 114, 41, 41, // ps.login(r))
10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, // . .
99, 97, 116, 99, 104, 40, 101, 114, 114, 32, 61, 62, // catch(err =>
32, 101, 114, 114, 41, 59, 10, 32, 32, 114, 101, 116, // err);. ret
117, 114, 110, 32, 104, 116, 109, 108, 96, 10, 60, 100, // urn html`.<d
105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 114, 111, // iv class="ro
117, 110, 100, 101, 100, 32, 98, 111, 114, 100, 101, 114, // unded border
34, 32, 115, 116, 121, 108, 101, 61, 34, 109, 97, 120, // " style="max
45, 119, 105, 100, 116, 104, 58, 32, 52, 56, 48, 112, // -width: 480p
120, 59, 32, 109, 97, 114, 103, 105, 110, 58, 32, 48, // x; margin: 0
32, 97, 117, 116, 111, 59, 32, 109, 97, 114, 103, 105, // auto; margi
110, 45, 116, 111, 112, 58, 32, 53, 101, 109, 59, 32, // n-top: 5em;
98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, // background:
35, 101, 101, 101, 59, 32, 34, 62, 10, 32, 32, 60, // #eee; ">. <
100, 105, 118, 32, 115, 116, 121, 108, 101, 61, 34, 112, // div style="p
97, 100, 100, 105, 110, 103, 58, 32, 50, 101, 109, 59, // adding: 2em;
32, 34, 62, 10, 32, 32, 32, 32, 60, 104, 49, 32, // ">. <h1
115, 116, 121, 108, 101, 61, 34, 99, 111, 108, 111, 114, // style="color
58, 32, 35, 54, 54, 54, 59, 34, 62, 68, 101, 118, // : #666;">Dev
105, 99, 101, 32, 68, 97, 115, 104, 98, 111, 97, 114, // ice Dashboar
100, 32, 76, 111, 103, 105, 110, 32, 60, 47, 104, 49, // d Login </h1
62, 10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 115, // >. <div s
116, 121, 108, 101, 61, 34, 109, 97, 114, 103, 105, 110, // tyle="margin
58, 32, 48, 46, 53, 101, 109, 32, 48, 59, 34, 62, // : 0.5em 0;">
10, 32, 32, 32, 32, 32, 32, 60, 105, 110, 112, 117, // . <inpu
116, 32, 116, 121, 112, 101, 61, 39, 116, 101, 120, 116, // t type='text
39, 32, 112, 108, 97, 99, 101, 104, 111, 108, 100, 101, // ' placeholde
114, 61, 39, 78, 97, 109, 101, 39, 32, 115, 116, 121, // r='Name' sty
108, 101, 61, 34, 119, 105, 100, 116, 104, 58, 32, 49, // le="width: 1
48, 48, 37, 59, 34, 10, 32, 32, 32, 32, 32, 32, // 00%;".
32, 32, 111, 110, 105, 110, 112, 117, 116, 61, 36, 123, // oninput=${
101, 118, 32, 61, 62, 32, 115, 101, 116, 85, 115, 101, // ev => setUse
114, 40, 101, 118, 46, 116, 97, 114, 103, 101, 116, 46, // r(ev.target.
118, 97, 108, 117, 101, 41, 125, 32, 118, 97, 108, 117, // value)} valu
101, 61, 36, 123, 117, 115, 101, 114, 125, 32, 47, 62, // e=${user} />
10, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, // . </div>.
32, 32, 32, 32, 60, 100, 105, 118, 32, 115, 116, 121, // <div sty
108, 101, 61, 34, 109, 97, 114, 103, 105, 110, 58, 32, // le="margin:
48, 46, 53, 101, 109, 32, 48, 59, 34, 62, 10, 32, // 0.5em 0;">.
32, 32, 32, 60, 105, 110, 112, 117, 116, 32, 112, 108, // <input pl
97, 99, 101, 104, 111, 108, 100, 101, 114, 61, 34, 116, // aceholder="t
121, 112, 101, 32, 109, 101, 115, 115, 97, 103, 101, 46, // ype message.
46, 46, 34, 32, 115, 116, 121, 108, 101, 61, 34, 119, // .." style="w
105, 100, 116, 104, 58, 32, 49, 48, 48, 37, 34, 32, // idth: 100%"
118, 97, 108, 117, 101, 61, 36, 123, 109, 101, 115, 115, // value=${mess
97, 103, 101, 125, 10, 32, 32, 32, 32, 32, 32, 111, // age}. o
110, 99, 104, 97, 110, 103, 101, 61, 36, 123, 115, 101, // nchange=${se
110, 100, 109, 101, 115, 115, 97, 103, 101, 125, 10, 32, // ndmessage}.
32, 32, 32, 32, 32, 111, 110, 105, 110, 112, 117, 116, // oninput
61, 36, 123, 101, 118, 32, 61, 62, 32, 115, 101, 116, // =${ev => set
77, 101, 115, 115, 97, 103, 101, 40, 101, 118, 46, 116, // Message(ev.t
32, 32, 32, 32, 32, 60, 105, 110, 112, 117, 116, 32, // <input
116, 121, 112, 101, 61, 34, 112, 97, 115, 115, 119, 111, // type="passwo
114, 100, 34, 32, 112, 108, 97, 99, 101, 104, 111, 108, // rd" placehol
100, 101, 114, 61, 34, 80, 97, 115, 115, 119, 111, 114, // der="Passwor
100, 34, 32, 115, 116, 121, 108, 101, 61, 34, 119, 105, // d" style="wi
100, 116, 104, 58, 32, 49, 48, 48, 37, 59, 34, 10, // dth: 100%;".
32, 32, 32, 32, 32, 32, 32, 32, 111, 110, 105, 110, // onin
112, 117, 116, 61, 36, 123, 101, 118, 32, 61, 62, 32, // put=${ev =>
115, 101, 116, 80, 97, 115, 115, 40, 101, 118, 46, 116, // setPass(ev.t
97, 114, 103, 101, 116, 46, 118, 97, 108, 117, 101, 41, // arget.value)
125, 32, 47, 62, 10, 32, 32, 60, 47, 100, 105, 118, // } />. </div
62, 10, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, // >. <div cla
115, 115, 61, 34, 109, 115, 103, 34, 62, 10, 32, 32, // ss="msg">.
32, 32, 67, 104, 97, 116, 32, 100, 101, 109, 111, 110, // Chat demon
115, 114, 97, 116, 101, 115, 10, 32, 32, 32, 32, 114, // srates. r
101, 97, 108, 45, 116, 105, 109, 101, 32, 98, 105, 100, // eal-time bid
105, 114, 101, 99, 116, 105, 111, 110, 97, 108, 32, 100, // irectional d
97, 116, 97, 32, 101, 120, 99, 104, 97, 110, 103, 101, // ata exchange
32, 98, 101, 116, 119, 101, 101, 110, 32, 109, 97, 110, // between man
121, 32, 99, 108, 105, 101, 110, 116, 115, 32, 97, 110, // y clients an
100, 32, 97, 32, 115, 101, 114, 118, 101, 114, 46, 10, // d a server..
32, 32, 60, 47, 100, 105, 118, 62, 10, 60, 47, 100, // </div>.</d
105, 118, 62, 96, 59, 10, 125, 59, 10, 10, 99, 111, // iv>`;.};..co
110, 115, 116, 32, 65, 112, 112, 32, 61, 32, 102, 117, // nst App = fu
110, 99, 116, 105, 111, 110, 40, 112, 114, 111, 112, 115, // nction(props
41, 32, 123, 10, 32, 32, 99, 111, 110, 115, 116, 32, // ) {. const
91, 109, 101, 115, 115, 97, 103, 101, 115, 44, 32, 115, // [messages, s
101, 116, 77, 101, 115, 115, 97, 103, 101, 115, 93, 32, // etMessages]
61, 32, 117, 115, 101, 83, 116, 97, 116, 101, 40, 91, // = useState([
93, 41, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, // ]);. const
91, 117, 115, 101, 114, 44, 32, 115, 101, 116, 85, 115, // [user, setUs
101, 114, 93, 32, 61, 32, 117, 115, 101, 83, 116, 97, // er] = useSta
116, 101, 40, 39, 39, 41, 59, 10, 32, 32, 99, 111, // te('');. co
110, 115, 116, 32, 91, 99, 111, 110, 102, 105, 103, 44, // nst [config,
32, 115, 101, 116, 67, 111, 110, 102, 105, 103, 93, 32, // setConfig]
61, 32, 117, 115, 101, 83, 116, 97, 116, 101, 40, 123, // = useState({
125, 41, 59, 10, 10, 32, 32, 99, 111, 110, 115, 116, // });.. const
32, 114, 101, 102, 114, 101, 115, 104, 32, 61, 32, 40, // refresh = (
41, 32, 61, 62, 10, 32, 32, 32, 32, 32, 32, 102, // ) =>. f
101, 116, 99, 104, 40, 39, 47, 97, 112, 105, 47, 99, // etch('/api/c
111, 110, 102, 105, 103, 47, 103, 101, 116, 39, 41, 46, // onfig/get').
116, 104, 101, 110, 40, 114, 32, 61, 62, 32, 114, 46, // then(r => r.
106, 115, 111, 110, 40, 41, 41, 46, 116, 104, 101, 110, // json()).then
40, 114, 32, 61, 62, 32, 115, 101, 116, 67, 111, 110, // (r => setCon
102, 105, 103, 40, 114, 41, 41, 59, 10, 10, 32, 32, // fig(r));..
99, 111, 110, 115, 116, 32, 108, 111, 103, 105, 110, 32, // const login
61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 117, // = function(u
41, 32, 123, 10, 32, 32, 32, 32, 100, 111, 99, 117, // ) {. docu
109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101, 32, // ment.cookie
61, 32, 96, 97, 99, 99, 101, 115, 115, 95, 116, 111, // = `access_to
107, 101, 110, 61, 36, 123, 117, 46, 116, 111, 107, 101, // ken=${u.toke
110, 125, 59, 112, 97, 116, 104, 61, 47, 59, 109, 97, // n};path=/;ma
120, 45, 97, 103, 101, 61, 51, 54, 48, 48, 96, 59, // x-age=3600`;
10, 32, 32, 32, 32, 115, 101, 116, 85, 115, 101, 114, // . setUser
40, 117, 46, 117, 115, 101, 114, 41, 59, 10, 32, 32, // (u.user);.
32, 32, 114, 101, 102, 114, 101, 115, 104, 40, 41, 59, // refresh();
10, 32, 32, 125, 59, 10, 10, 32, 32, 99, 111, 110, // . };.. con
115, 116, 32, 108, 111, 103, 111, 117, 116, 32, 61, 32, // st logout =
101, 118, 32, 61, 62, 32, 123, 10, 32, 32, 32, 32, // ev => {.
125, 32, 118, 97, 108, 117, 101, 61, 36, 123, 112, 97, // } value=${pa
115, 115, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, // ss}.
111, 110, 99, 104, 97, 110, 103, 101, 61, 36, 123, 108, // onchange=${l
111, 103, 105, 110, 125, 32, 47, 62, 10, 32, 32, 32, // ogin} />.
32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, // </div>.
60, 100, 105, 118, 32, 115, 116, 121, 108, 101, 61, 34, // <div style="
109, 97, 114, 103, 105, 110, 58, 32, 49, 101, 109, 32, // margin: 1em
48, 59, 34, 62, 10, 32, 32, 32, 32, 32, 32, 60, // 0;">. <
98, 117, 116, 116, 111, 110, 32, 99, 108, 97, 115, 115, // button class
61, 34, 98, 116, 110, 34, 32, 115, 116, 121, 108, 101, // ="btn" style
61, 34, 119, 105, 100, 116, 104, 58, 32, 49, 48, 48, // ="width: 100
37, 59, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, // %; backgroun
100, 58, 32, 35, 56, 97, 97, 59, 34, 10, 32, 32, // d: #8aa;".
32, 32, 32, 32, 32, 32, 100, 105, 115, 97, 98, 108, // disabl
101, 100, 61, 36, 123, 33, 117, 115, 101, 114, 32, 124, // ed=${!user |
124, 32, 33, 112, 97, 115, 115, 125, 32, 111, 110, 99, // | !pass} onc
108, 105, 99, 107, 61, 36, 123, 108, 111, 103, 105, 110, // lick=${login
125, 62, 32, 76, 111, 103, 105, 110, 32, 60, 47, 98, // }> Login </b
117, 116, 116, 111, 110, 62, 10, 32, 32, 32, 32, 60, // utton>. <
47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 60, 100, // /div>. <d
105, 118, 32, 115, 116, 121, 108, 101, 61, 34, 99, 111, // iv style="co
108, 111, 114, 58, 32, 35, 55, 55, 55, 59, 32, 109, // lor: #777; m
97, 114, 103, 105, 110, 45, 116, 111, 112, 58, 32, 50, // argin-top: 2
101, 109, 59, 34, 62, 10, 32, 32, 32, 32, 32, 32, // em;">.
86, 97, 108, 105, 100, 32, 108, 111, 103, 105, 110, 115, // Valid logins
58, 32, 97, 100, 109, 105, 110, 58, 112, 97, 115, 115, // : admin:pass
48, 44, 32, 117, 115, 101, 114, 49, 58, 112, 97, 115, // 0, user1:pas
115, 49, 44, 32, 117, 115, 101, 114, 50, 58, 112, 97, // s1, user2:pa
115, 115, 50, 10, 32, 32, 32, 32, 60, 47, 100, 105, // ss2. </di
118, 62, 10, 32, 32, 60, 47, 100, 105, 118, 62, 10, // v>. </div>.
60, 47, 100, 105, 118, 62, 96, 59, 10, 125, 59, 10, // </div>`;.};.
10, 99, 111, 110, 115, 116, 32, 77, 101, 115, 115, 97, // .const Messa
103, 101, 32, 61, 32, 116, 101, 120, 116, 32, 61, 62, // ge = text =>
32, 104, 116, 109, 108, 96, 60, 100, 105, 118, 32, 115, // html`<div s
116, 121, 108, 101, 61, 34, 109, 97, 114, 103, 105, 110, // tyle="margin
58, 32, 48, 46, 53, 101, 109, 32, 48, 59, 34, 62, // : 0.5em 0;">
36, 123, 116, 101, 120, 116, 125, 60, 47, 100, 105, 118, // ${text}</div
62, 96, 59, 10, 10, 99, 111, 110, 115, 116, 32, 67, // >`;..const C
104, 97, 116, 32, 61, 32, 102, 117, 110, 99, 116, 105, // hat = functi
111, 110, 40, 112, 114, 111, 112, 115, 41, 32, 123, 10, // on(props) {.
32, 32, 99, 111, 110, 115, 116, 32, 91, 109, 101, 115, // const [mes
115, 97, 103, 101, 44, 32, 115, 101, 116, 77, 101, 115, // sage, setMes
115, 97, 103, 101, 93, 32, 61, 32, 117, 115, 101, 83, // sage] = useS
116, 97, 116, 101, 40, 39, 39, 41, 59, 10, 32, 32, // tate('');.
99, 111, 110, 115, 116, 32, 115, 101, 110, 100, 109, 101, // const sendme
115, 115, 97, 103, 101, 32, 61, 32, 101, 118, 32, 61, // ssage = ev =
62, 32, 102, 101, 116, 99, 104, 40, 39, 47, 97, 112, // > fetch('/ap
105, 47, 109, 101, 115, 115, 97, 103, 101, 47, 115, 101, // i/message/se
110, 100, 39, 44, 32, 123, 10, 32, 32, 32, 32, 32, // nd', {.
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 109, 101, 116, 104, 111, 100, 58, 32, 39, 112, 111, // method: 'po
115, 116, 39, 44, 10, 32, 32, 32, 32, 32, 32, 32, // st',.
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, // b
111, 100, 121, 58, 32, 96, 109, 101, 115, 115, 97, 103, // ody: `messag
101, 61, 36, 123, 101, 110, 99, 111, 100, 101, 85, 82, // e=${encodeUR
73, 67, 111, 109, 112, 111, 110, 101, 110, 116, 40, 109, // IComponent(m
101, 115, 115, 97, 103, 101, 41, 125, 96, 10, 32, 32, // essage)}`.
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, //
32, 32, 125, 41, 46, 116, 104, 101, 110, 40, 114, 32, // }).then(r
61, 62, 32, 115, 101, 116, 77, 101, 115, 115, 97, 103, // => setMessag
101, 40, 39, 39, 41, 41, 59, 10, 10, 32, 32, 99, // e(''));.. c
111, 110, 115, 116, 32, 109, 101, 115, 115, 97, 103, 101, // onst message
115, 32, 61, 32, 112, 114, 111, 112, 115, 46, 109, 101, // s = props.me
115, 115, 97, 103, 101, 115, 46, 109, 97, 112, 40, 10, // ssages.map(.
32, 32, 32, 32, 32, 32, 116, 101, 120, 116, 32, 61, // text =
62, 32, 104, 116, 109, 108, 96, 60, 100, 105, 118, 32, // > html`<div
115, 116, 121, 108, 101, 61, 34, 109, 97, 114, 103, 105, // style="margi
110, 58, 32, 48, 46, 53, 101, 109, 32, 48, 59, 34, // n: 0.5em 0;"
62, 36, 123, 116, 101, 120, 116, 125, 60, 47, 100, 105, // >${text}</di
118, 62, 96, 41, 59, 10, 32, 32, 114, 101, 116, 117, // v>`);. retu
114, 110, 32, 104, 116, 109, 108, 96, 10, 60, 100, 105, // rn html`.<di
118, 32, 115, 116, 121, 108, 101, 61, 34, 109, 97, 114, // v style="mar
103, 105, 110, 58, 32, 48, 32, 48, 46, 51, 101, 109, // gin: 0 0.3em
59, 34, 62, 10, 32, 32, 60, 104, 51, 32, 115, 116, // ;">. <h3 st
121, 108, 101, 61, 34, 98, 97, 99, 107, 103, 114, 111, // yle="backgro
117, 110, 100, 58, 32, 35, 51, 48, 99, 48, 52, 48, // und: #30c040
59, 32, 99, 111, 108, 111, 114, 58, 32, 35, 102, 102, // ; color: #ff
102, 59, 32, 112, 97, 100, 100, 105, 110, 103, 58, 32, // f; padding:
48, 46, 52, 101, 109, 59, 34, 62, 85, 115, 101, 114, // 0.4em;">User
32, 99, 104, 97, 116, 60, 47, 104, 51, 62, 10, 32, // chat</h3>.
32, 60, 100, 105, 118, 32, 115, 116, 121, 108, 101, 61, // <div style=
34, 104, 101, 105, 103, 104, 116, 58, 32, 49, 48, 101, // "height: 10e
109, 59, 32, 111, 118, 101, 114, 102, 108, 111, 119, 58, // m; overflow:
32, 97, 117, 116, 111, 59, 32, 112, 97, 100, 100, 105, // auto; paddi
110, 103, 58, 32, 48, 46, 53, 101, 109, 59, 32, 34, // ng: 0.5em; "
32, 99, 108, 97, 115, 115, 61, 34, 98, 111, 114, 100, // class="bord
101, 114, 34, 62, 10, 32, 32, 32, 32, 36, 123, 109, // er">. ${m
101, 115, 115, 97, 103, 101, 115, 125, 10, 32, 32, 60, // essages}. <
47, 100, 105, 118, 62, 10, 32, 32, 60, 100, 105, 118, // /div>. <div
32, 115, 116, 121, 108, 101, 61, 34, 109, 97, 114, 103, // style="marg
105, 110, 58, 32, 48, 46, 53, 101, 109, 32, 48, 59, // in: 0.5em 0;
34, 62, 10, 32, 32, 32, 32, 60, 105, 110, 112, 117, // ">. <inpu
116, 32, 112, 108, 97, 99, 101, 104, 111, 108, 100, 101, // t placeholde
114, 61, 34, 116, 121, 112, 101, 32, 109, 101, 115, 115, // r="type mess
97, 103, 101, 46, 46, 46, 34, 32, 115, 116, 121, 108, // age..." styl
101, 61, 34, 119, 105, 100, 116, 104, 58, 32, 49, 48, // e="width: 10
48, 37, 34, 32, 118, 97, 108, 117, 101, 61, 36, 123, // 0%" value=${
109, 101, 115, 115, 97, 103, 101, 125, 10, 32, 32, 32, // message}.
32, 32, 32, 111, 110, 99, 104, 97, 110, 103, 101, 61, // onchange=
36, 123, 115, 101, 110, 100, 109, 101, 115, 115, 97, 103, // ${sendmessag
101, 125, 10, 32, 32, 32, 32, 32, 32, 111, 110, 105, // e}. oni
110, 112, 117, 116, 61, 36, 123, 101, 118, 32, 61, 62, // nput=${ev =>
32, 115, 101, 116, 77, 101, 115, 115, 97, 103, 101, 40, // setMessage(
101, 118, 46, 116, 97, 114, 103, 101, 116, 46, 118, 97, // ev.target.va
108, 117, 101, 41, 125, 32, 47, 62, 10, 32, 32, 60, // lue)} />. <
47, 100, 105, 118, 62, 10, 32, 32, 60, 100, 105, 118, // /div>. <div
32, 99, 108, 97, 115, 115, 61, 34, 109, 115, 103, 34, // class="msg"
62, 10, 32, 32, 32, 32, 67, 104, 97, 116, 32, 100, // >. Chat d
101, 109, 111, 110, 115, 114, 97, 116, 101, 115, 10, 32, // emonsrates.
32, 32, 32, 114, 101, 97, 108, 45, 116, 105, 109, 101, // real-time
32, 98, 105, 100, 105, 114, 101, 99, 116, 105, 111, 110, // bidirection
97, 108, 32, 100, 97, 116, 97, 32, 101, 120, 99, 104, // al data exch
97, 110, 103, 101, 32, 98, 101, 116, 119, 101, 101, 110, // ange between
32, 109, 97, 110, 121, 32, 99, 108, 105, 101, 110, 116, // many client
115, 32, 97, 110, 100, 32, 97, 32, 115, 101, 114, 118, // s and a serv
101, 114, 46, 10, 32, 32, 60, 47, 100, 105, 118, 62, // er.. </div>
10, 60, 47, 100, 105, 118, 62, 96, 59, 10, 125, 59, // .</div>`;.};
10, 10, 99, 111, 110, 115, 116, 32, 65, 112, 112, 32, // ..const App
61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 112, // = function(p
114, 111, 112, 115, 41, 32, 123, 10, 32, 32, 99, 111, // rops) {. co
110, 115, 116, 32, 91, 109, 101, 115, 115, 97, 103, 101, // nst [message
115, 44, 32, 115, 101, 116, 77, 101, 115, 115, 97, 103, // s, setMessag
101, 115, 93, 32, 61, 32, 117, 115, 101, 83, 116, 97, // es] = useSta
116, 101, 40, 91, 93, 41, 59, 10, 32, 32, 99, 111, // te([]);. co
110, 115, 116, 32, 91, 117, 115, 101, 114, 44, 32, 115, // nst [user, s
101, 116, 85, 115, 101, 114, 93, 32, 61, 32, 117, 115, // etUser] = us
101, 83, 116, 97, 116, 101, 40, 39, 39, 41, 59, 10, // eState('');.
32, 32, 99, 111, 110, 115, 116, 32, 91, 99, 111, 110, // const [con
102, 105, 103, 44, 32, 115, 101, 116, 67, 111, 110, 102, // fig, setConf
105, 103, 93, 32, 61, 32, 117, 115, 101, 83, 116, 97, // ig] = useSta
116, 101, 40, 123, 125, 41, 59, 10, 10, 32, 32, 99, // te({});.. c
111, 110, 115, 116, 32, 114, 101, 102, 114, 101, 115, 104, // onst refresh
32, 61, 32, 40, 41, 32, 61, 62, 10, 32, 32, 32, // = () =>.
32, 32, 32, 102, 101, 116, 99, 104, 40, 39, 47, 97, // fetch('/a
112, 105, 47, 99, 111, 110, 102, 105, 103, 47, 103, 101, // pi/config/ge
116, 39, 41, 46, 116, 104, 101, 110, 40, 114, 32, 61, // t').then(r =
62, 32, 114, 46, 106, 115, 111, 110, 40, 41, 41, 46, // > r.json()).
116, 104, 101, 110, 40, 114, 32, 61, 62, 32, 115, 101, // then(r => se
116, 67, 111, 110, 102, 105, 103, 40, 114, 41, 41, 59, // tConfig(r));
10, 10, 32, 32, 99, 111, 110, 115, 116, 32, 108, 111, // .. const lo
103, 105, 110, 32, 61, 32, 102, 117, 110, 99, 116, 105, // gin = functi
111, 110, 40, 117, 41, 32, 123, 10, 32, 32, 32, 32, // on(u) {.
100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, // document.coo
107, 105, 101, 32, 61, 32, 96, 97, 99, 99, 101, 115, // kie = `acces
115, 95, 116, 111, 107, 101, 110, 61, 59, 112, 97, 116, // s_token=;pat
104, 61, 47, 59, 109, 97, 120, 45, 97, 103, 101, 61, // h=/;max-age=
48, 96, 59, 10, 32, 32, 32, 32, 115, 101, 116, 85, // 0`;. setU
115, 101, 114, 40, 39, 39, 41, 59, 10, 32, 32, 125, // ser('');. }
59, 10, 10, 32, 32, 99, 111, 110, 115, 116, 32, 119, // ;.. const w
97, 116, 99, 104, 32, 61, 32, 102, 117, 110, 99, 116, // atch = funct
105, 111, 110, 40, 41, 32, 123, 10, 32, 32, 32, 32, // ion() {.
118, 97, 114, 32, 102, 32, 61, 32, 102, 117, 110, 99, // var f = func
116, 105, 111, 110, 40, 114, 101, 97, 100, 101, 114, 41, // tion(reader)
32, 123, 10, 32, 32, 32, 32, 32, 32, 114, 101, 116, // {. ret
117, 114, 110, 32, 114, 101, 97, 100, 101, 114, 46, 114, // urn reader.r
101, 97, 100, 40, 41, 46, 116, 104, 101, 110, 40, 102, // ead().then(f
117, 110, 99, 116, 105, 111, 110, 40, 114, 101, 115, 117, // unction(resu
108, 116, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, // lt) {.
32, 32, 118, 97, 114, 32, 100, 97, 116, 97, 32, 61, // var data =
32, 83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, // String.from
67, 104, 97, 114, 67, 111, 100, 101, 46, 97, 112, 112, // CharCode.app
108, 121, 40, 110, 117, 108, 108, 44, 32, 114, 101, 115, // ly(null, res
117, 108, 116, 46, 118, 97, 108, 117, 101, 41, 59, 10, // ult.value);.
32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, // var
115, 95, 116, 111, 107, 101, 110, 61, 36, 123, 117, 46, // s_token=${u.
116, 111, 107, 101, 110, 125, 59, 112, 97, 116, 104, 61, // token};path=
47, 59, 109, 97, 120, 45, 97, 103, 101, 61, 51, 54, // /;max-age=36
48, 48, 96, 59, 10, 32, 32, 32, 32, 115, 101, 116, // 00`;. set
85, 115, 101, 114, 40, 117, 46, 117, 115, 101, 114, 41, // User(u.user)
59, 10, 32, 32, 32, 32, 114, 101, 102, 114, 101, 115, // ;. refres
104, 40, 41, 59, 10, 32, 32, 125, 59, 10, 10, 32, // h();. };..
32, 99, 111, 110, 115, 116, 32, 108, 111, 103, 111, 117, // const logou
116, 32, 61, 32, 101, 118, 32, 61, 62, 32, 123, 10, // t = ev => {.
32, 32, 32, 32, 100, 111, 99, 117, 109, 101, 110, 116, // document
46, 99, 111, 111, 107, 105, 101, 32, 61, 32, 96, 97, // .cookie = `a
99, 99, 101, 115, 115, 95, 116, 111, 107, 101, 110, 61, // ccess_token=
59, 112, 97, 116, 104, 61, 47, 59, 109, 97, 120, 45, // ;path=/;max-
97, 103, 101, 61, 48, 96, 59, 10, 32, 32, 32, 32, // age=0`;.
115, 101, 116, 85, 115, 101, 114, 40, 39, 39, 41, 59, // setUser('');
10, 32, 32, 125, 59, 10, 10, 32, 32, 99, 111, 110, // . };.. con
115, 116, 32, 119, 97, 116, 99, 104, 32, 61, 32, 102, // st watch = f
117, 110, 99, 116, 105, 111, 110, 40, 41, 32, 123, 10, // unction() {.
32, 32, 32, 32, 118, 97, 114, 32, 102, 32, 61, 32, // var f =
102, 117, 110, 99, 116, 105, 111, 110, 40, 114, 101, 97, // function(rea
100, 101, 114, 41, 32, 123, 10, 32, 32, 32, 32, 32, // der) {.
32, 114, 101, 116, 117, 114, 110, 32, 114, 101, 97, 100, // return read
101, 114, 46, 114, 101, 97, 100, 40, 41, 46, 116, 104, // er.read().th
101, 110, 40, 102, 117, 110, 99, 116, 105, 111, 110, 40, // en(function(
114, 101, 115, 117, 108, 116, 41, 32, 123, 10, 32, 32, // result) {.
32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 100, 97, // var da
116, 97, 32, 61, 32, 83, 116, 114, 105, 110, 103, 46, // ta = String.
102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, // fromCharCode
46, 97, 112, 112, 108, 121, 40, 110, 117, 108, 108, 44, // .apply(null,
32, 114, 101, 115, 117, 108, 116, 46, 118, 97, 108, 117, // result.valu
101, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, // e);.
118, 97, 114, 32, 110, 111, 116, 105, 102, 105, 99, 97, // var notifica
116, 105, 111, 110, 32, 61, 32, 74, 83, 79, 78, 46, // tion = JSON.
112, 97, 114, 115, 101, 40, 100, 97, 116, 97, 41, 59, // parse(data);
10, 32, 32, 32, 32, 32, 32, 32, 32, 105, 102, 32, // . if
40, 110, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, // (notificatio
110, 46, 110, 97, 109, 101, 32, 61, 61, 32, 39, 99, // n.name == 'c
111, 110, 102, 105, 103, 39, 41, 32, 114, 101, 102, 114, // onfig') refr
101, 115, 104, 40, 41, 59, 10, 32, 32, 32, 32, 32, // esh();.
32, 32, 32, 105, 102, 32, 40, 110, 111, 116, 105, 102, // if (notif
105, 99, 97, 116, 105, 111, 110, 46, 110, 97, 109, 101, // ication.name
32, 61, 61, 32, 39, 109, 101, 115, 115, 97, 103, 101, // == 'message
39, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, // ') {.
32, 32, 32, 115, 101, 116, 77, 101, 115, 115, 97, 103, // setMessag
101, 115, 40, 109, 32, 61, 62, 32, 109, 46, 99, 111, // es(m => m.co
110, 99, 97, 116, 40, 91, 110, 111, 116, 105, 102, 105, // ncat([notifi
99, 97, 116, 105, 111, 110, 46, 100, 97, 116, 97, 93, // cation.data]
41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, // )). }
10, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, // . //
99, 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, 40, // console.log(
110, 111, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, // notification
32, 61, 32, 74, 83, 79, 78, 46, 112, 97, 114, 115, // = JSON.pars
101, 40, 100, 97, 116, 97, 41, 59, 10, 32, 32, 32, // e(data);.
32, 32, 32, 32, 32, 105, 102, 32, 40, 110, 111, 116, // if (not
105, 102, 105, 99, 97, 116, 105, 111, 110, 46, 110, 97, // ification.na
109, 101, 32, 61, 61, 32, 39, 99, 111, 110, 102, 105, // me == 'confi
103, 39, 41, 32, 114, 101, 102, 114, 101, 115, 104, 40, // g') refresh(
41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 105, // );. i
102, 32, 40, 110, 111, 116, 105, 102, 105, 99, 97, 116, // f (notificat
105, 111, 110, 46, 110, 97, 109, 101, 32, 61, 61, 32, // ion.name ==
39, 109, 101, 115, 115, 97, 103, 101, 39, 41, 32, 123, // 'message') {
10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 115, // . s
101, 116, 77, 101, 115, 115, 97, 103, 101, 115, 40, 109, // etMessages(m
32, 61, 62, 32, 109, 46, 99, 111, 110, 99, 97, 116, // => m.concat
40, 91, 110, 111, 116, 105, 102, 105, 99, 97, 116, 105, // ([notificati
111, 110, 46, 100, 97, 116, 97, 93, 41, 41, 10, 32, // on.data])).
32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, // }.
32, 32, 32, 32, 32, 47, 47, 32, 99, 111, 110, 115, // // cons
111, 108, 101, 46, 108, 111, 103, 40, 110, 111, 116, 105, // ole.log(noti
102, 105, 99, 97, 116, 105, 111, 110, 41, 59, 10, 32, // fication);.
32, 32, 32, 32, 32, 32, 32, 105, 102, 32, 40, 33, // if (!
114, 101, 115, 117, 108, 116, 46, 100, 111, 110, 101, 41, // result.done)
32, 114, 101, 116, 117, 114, 110, 32, 102, 40, 114, 101, // return f(re
97, 100, 101, 114, 41, 59, 10, 32, 32, 32, 32, 32, // ader);.
32, 125, 41, 59, 10, 32, 32, 32, 32, 125, 59, 10, // });. };.
102, 32, 40, 33, 114, 101, 115, 117, 108, 116, 46, 100, // f (!result.d
111, 110, 101, 41, 32, 114, 101, 116, 117, 114, 110, 32, // one) return
102, 40, 114, 101, 97, 100, 101, 114, 41, 59, 10, 32, // f(reader);.
32, 32, 32, 32, 32, 125, 41, 59, 10, 32, 32, 32, // });.
32, 125, 59, 10, 32, 32, 32, 32, 102, 101, 116, 99, // };. fetc
104, 40, 39, 47, 97, 112, 105, 47, 119, 97, 116, 99, // h('/api/watc
104, 39, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, // h').
46, 116, 104, 101, 110, 40, 114, 32, 61, 62, 32, 114, // .then(r => r
46, 98, 111, 100, 121, 46, 103, 101, 116, 82, 101, 97, // .body.getRea
100, 101, 114, 40, 41, 41, 10, 32, 32, 32, 32, 32, // der()).
32, 32, 32, 46, 116, 104, 101, 110, 40, 102, 41, 10, // .then(f).
32, 32, 32, 32, 32, 32, 32, 32, 46, 99, 97, 116, // .cat
99, 104, 40, 101, 32, 61, 62, 32, 115, 101, 116, 84, // ch(e => setT
105, 109, 101, 111, 117, 116, 40, 119, 97, 116, 99, 104, // imeout(watch
44, 32, 49, 48, 48, 48, 41, 41, 59, 10, 32, 32, // , 1000));.
125, 59, 10, 10, 32, 32, 117, 115, 101, 69, 102, 102, // };.. useEff
101, 99, 116, 40, 40, 41, 32, 61, 62, 32, 123, 10, // ect(() => {.
32, 32, 32, 32, 102, 101, 116, 99, 104, 40, 39, 47, // fetch('/
97, 112, 105, 47, 119, 97, 116, 99, 104, 39, 41, 10, // api/watch').
97, 112, 105, 47, 108, 111, 103, 105, 110, 39, 41, 10, // api/login').
32, 32, 32, 32, 32, 32, 32, 32, 46, 116, 104, 101, // .the
110, 40, 114, 32, 61, 62, 32, 114, 46, 98, 111, 100, // n(r => r.bod
121, 46, 103, 101, 116, 82, 101, 97, 100, 101, 114, 40, // y.getReader(
41, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 46, // )). .
116, 104, 101, 110, 40, 102, 41, 10, 32, 32, 32, 32, // then(f).
32, 32, 32, 32, 46, 99, 97, 116, 99, 104, 40, 101, // .catch(e
32, 61, 62, 32, 115, 101, 116, 84, 105, 109, 101, 111, // => setTimeo
117, 116, 40, 119, 97, 116, 99, 104, 44, 32, 49, 48, // ut(watch, 10
48, 48, 41, 41, 59, 10, 32, 32, 125, 59, 10, 10, // 00));. };..
32, 32, 117, 115, 101, 69, 102, 102, 101, 99, 116, 40, // useEffect(
40, 41, 32, 61, 62, 32, 123, 10, 32, 32, 32, 32, // () => {.
102, 101, 116, 99, 104, 40, 39, 47, 97, 112, 105, 47, // fetch('/api/
108, 111, 103, 105, 110, 39, 41, 10, 32, 32, 32, 32, // login').
32, 32, 32, 32, 46, 116, 104, 101, 110, 40, 114, 32, // .then(r
61, 62, 32, 114, 46, 106, 115, 111, 110, 40, 41, 41, // => r.json())
10, 32, 32, 32, 32, 32, 32, 32, 32, 46, 116, 104, // . .th
101, 110, 40, 114, 32, 61, 62, 32, 108, 111, 103, 105, // en(r => logi
110, 40, 114, 41, 41, 10, 32, 32, 32, 32, 32, 32, // n(r)).
32, 32, 46, 99, 97, 116, 99, 104, 40, 101, 114, 114, // .catch(err
32, 61, 62, 32, 115, 101, 116, 85, 115, 101, 114, 40, // => setUser(
39, 39, 41, 41, 59, 10, 32, 32, 32, 32, 114, 101, // ''));. re
102, 114, 101, 115, 104, 40, 41, 59, 10, 32, 32, 32, // fresh();.
32, 119, 97, 116, 99, 104, 40, 41, 59, 10, 32, 32, // watch();.
125, 44, 32, 91, 93, 41, 59, 10, 10, 32, 32, 105, // }, []);.. i
102, 32, 40, 33, 117, 115, 101, 114, 41, 32, 114, 101, // f (!user) re
116, 117, 114, 110, 32, 104, 116, 109, 108, 96, 60, 36, // turn html`<$
123, 76, 111, 103, 105, 110, 125, 32, 108, 111, 103, 105, // {Login} logi
110, 61, 36, 123, 108, 111, 103, 105, 110, 125, 32, 47, // n=${login} /
62, 96, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, // >`;. const
97, 100, 109, 105, 110, 32, 61, 32, 117, 115, 101, 114, // admin = user
32, 61, 61, 32, 39, 97, 100, 109, 105, 110, 39, 59, // == 'admin';
10, 32, 32, 99, 111, 110, 115, 116, 32, 99, 111, 108, // . const col
115, 105, 122, 101, 32, 61, 32, 97, 100, 109, 105, 110, // size = admin
32, 63, 32, 39, 99, 52, 39, 32, 58, 32, 39, 99, // ? 'c4' : 'c
54, 39, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, // 6';. const
99, 115, 32, 61, 32, 97, 100, 109, 105, 110, 32, 63, // cs = admin ?
32, 104, 116, 109, 108, 96, 60, 36, 123, 67, 104, 97, // html`<${Cha
110, 103, 101, 83, 101, 116, 116, 105, 110, 103, 115, 125, // ngeSettings}
32, 99, 111, 110, 102, 105, 103, 61, 36, 123, 99, 111, // config=${co
110, 102, 105, 103, 125, 32, 47, 62, 96, 32, 58, 32, // nfig} />` :
39, 39, 59, 10, 32, 32, 114, 101, 116, 117, 114, 110, // '';. return
32, 104, 116, 109, 108, 96, 10, 60, 36, 123, 78, 97, // html`.<${Na
118, 125, 32, 117, 115, 101, 114, 61, 36, 123, 117, 115, // v} user=${us
101, 114, 125, 32, 108, 111, 103, 111, 117, 116, 61, 36, // er} logout=$
123, 108, 111, 103, 111, 117, 116, 125, 32, 47, 62, 10, // {logout} />.
60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, // <div class="
99, 111, 110, 116, 97, 105, 110, 101, 114, 34, 62, 10, // container">.
32, 32, 60, 36, 123, 72, 101, 114, 111, 125, 32, 47, // <${Hero} /
62, 10, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, // >. <div cla
115, 115, 61, 34, 114, 111, 119, 34, 62, 10, 32, 32, // ss="row">.
32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, // <div class
61, 34, 99, 111, 108, 32, 36, 123, 99, 111, 108, 115, // ="col ${cols
105, 122, 101, 125, 34, 62, 60, 36, 123, 83, 104, 111, // ize}"><${Sho
119, 83, 101, 116, 116, 105, 110, 103, 115, 125, 32, 99, // wSettings} c
111, 110, 102, 105, 103, 61, 36, 123, 99, 111, 110, 102, // onfig=${conf
105, 103, 125, 32, 47, 62, 60, 47, 100, 105, 118, 62, // ig} /></div>
110, 40, 114, 32, 61, 62, 32, 114, 46, 106, 115, 111, // n(r => r.jso
110, 40, 41, 41, 10, 32, 32, 32, 32, 32, 32, 32, // n()).
32, 46, 116, 104, 101, 110, 40, 114, 32, 61, 62, 32, // .then(r =>
108, 111, 103, 105, 110, 40, 114, 41, 41, 10, 32, 32, // login(r)).
32, 32, 32, 32, 32, 32, 46, 99, 97, 116, 99, 104, // .catch
40, 101, 114, 114, 32, 61, 62, 32, 115, 101, 116, 85, // (err => setU
115, 101, 114, 40, 39, 39, 41, 41, 59, 10, 32, 32, // ser(''));.
32, 32, 114, 101, 102, 114, 101, 115, 104, 40, 41, 59, // refresh();
10, 32, 32, 32, 32, 119, 97, 116, 99, 104, 40, 41, // . watch()
59, 10, 32, 32, 125, 44, 32, 91, 93, 41, 59, 10, // ;. }, []);.
10, 32, 32, 105, 102, 32, 40, 33, 117, 115, 101, 114, // . if (!user
41, 32, 114, 101, 116, 117, 114, 110, 32, 104, 116, 109, // ) return htm
108, 96, 60, 36, 123, 76, 111, 103, 105, 110, 125, 32, // l`<${Login}
108, 111, 103, 105, 110, 61, 36, 123, 108, 111, 103, 105, // login=${logi
110, 125, 32, 47, 62, 96, 59, 10, 32, 32, 99, 111, // n} />`;. co
110, 115, 116, 32, 97, 100, 109, 105, 110, 32, 61, 32, // nst admin =
117, 115, 101, 114, 32, 61, 61, 32, 39, 97, 100, 109, // user == 'adm
105, 110, 39, 59, 10, 32, 32, 99, 111, 110, 115, 116, // in';. const
32, 99, 111, 108, 115, 105, 122, 101, 32, 61, 32, 97, // colsize = a
100, 109, 105, 110, 32, 63, 32, 39, 99, 52, 39, 32, // dmin ? 'c4'
58, 32, 39, 99, 54, 39, 59, 10, 32, 32, 99, 111, // : 'c6';. co
110, 115, 116, 32, 99, 115, 32, 61, 32, 97, 100, 109, // nst cs = adm
105, 110, 32, 63, 32, 104, 116, 109, 108, 96, 60, 36, // in ? html`<$
123, 67, 104, 97, 110, 103, 101, 83, 101, 116, 116, 105, // {ChangeSetti
110, 103, 115, 125, 32, 99, 111, 110, 102, 105, 103, 61, // ngs} config=
36, 123, 99, 111, 110, 102, 105, 103, 125, 32, 47, 62, // ${config} />
96, 32, 58, 32, 39, 39, 59, 10, 32, 32, 114, 101, // ` : '';. re
116, 117, 114, 110, 32, 104, 116, 109, 108, 96, 10, 60, // turn html`.<
36, 123, 78, 97, 118, 125, 32, 117, 115, 101, 114, 61, // ${Nav} user=
36, 123, 117, 115, 101, 114, 125, 32, 108, 111, 103, 111, // ${user} logo
117, 116, 61, 36, 123, 108, 111, 103, 111, 117, 116, 125, // ut=${logout}
32, 47, 62, 10, 60, 100, 105, 118, 32, 99, 108, 97, // />.<div cla
115, 115, 61, 34, 99, 111, 110, 116, 97, 105, 110, 101, // ss="containe
114, 34, 62, 10, 32, 32, 60, 36, 123, 72, 101, 114, // r">. <${Her
111, 125, 32, 47, 62, 10, 32, 32, 60, 100, 105, 118, // o} />. <div
32, 99, 108, 97, 115, 115, 61, 34, 114, 111, 119, 34, // class="row"
62, 10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, // >. <div c
108, 97, 115, 115, 61, 34, 99, 111, 108, 32, 36, 123, // lass="col ${
99, 111, 108, 115, 105, 122, 101, 125, 34, 62, 60, 36, // colsize}"><$
123, 83, 104, 111, 119, 83, 101, 116, 116, 105, 110, 103, // {ShowSetting
115, 125, 32, 99, 111, 110, 102, 105, 103, 61, 36, 123, // s} config=${
99, 111, 110, 102, 105, 103, 125, 32, 47, 62, 60, 47, // config} /></
100, 105, 118, 62, 10, 32, 32, 32, 32, 60, 100, 105, // div>. <di
118, 32, 99, 108, 97, 115, 115, 61, 34, 99, 111, 108, // v class="col
32, 36, 123, 99, 111, 108, 115, 105, 122, 101, 125, 34, // ${colsize}"
62, 36, 123, 99, 115, 125, 60, 47, 100, 105, 118, 62, // >${cs}</div>
10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, // . <div cl
97, 115, 115, 61, 34, 99, 111, 108, 32, 36, 123, 99, // ass="col ${c
111, 108, 115, 105, 122, 101, 125, 34, 62, 36, 123, 99, // olsize}">${c
115, 125, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, // s}</div>.
32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, // <div class=
34, 99, 111, 108, 32, 36, 123, 99, 111, 108, 115, 105, // "col ${colsi
122, 101, 125, 34, 62, 60, 36, 123, 67, 104, 97, 116, // ze}"><${Chat
125, 32, 109, 101, 115, 115, 97, 103, 101, 115, 61, 36, // } messages=$
123, 109, 101, 115, 115, 97, 103, 101, 115, 125, 32, 47, // {messages} /
62, 60, 47, 100, 105, 118, 62, 10, 32, 32, 60, 47, // ></div>. </
100, 105, 118, 62, 10, 32, 32, 60, 36, 123, 70, 111, // div>. <${Fo
111, 116, 101, 114, 125, 32, 47, 62, 10, 60, 47, 100, // oter} />.</d
105, 118, 62, 96, 59, 10, 125, 59, 10, 10, 119, 105, // iv>`;.};..wi
110, 100, 111, 119, 46, 111, 110, 108, 111, 97, 100, 32, // ndow.onload
61, 32, 40, 41, 32, 61, 62, 32, 114, 101, 110, 100, // = () => rend
101, 114, 40, 104, 40, 65, 112, 112, 41, 44, 32, 100, // er(h(App), d
111, 99, 117, 109, 101, 110, 116, 46, 98, 111, 100, 121, // ocument.body
41, 59, 10, 0 // );.
111, 108, 115, 105, 122, 101, 125, 34, 62, 60, 36, 123, // olsize}"><${
67, 104, 97, 116, 125, 32, 109, 101, 115, 115, 97, 103, // Chat} messag
101, 115, 61, 36, 123, 109, 101, 115, 115, 97, 103, 101, // es=${message
115, 125, 32, 47, 62, 60, 47, 100, 105, 118, 62, 10, // s} /></div>.
32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 60, // </div>. <
36, 123, 70, 111, 111, 116, 101, 114, 125, 32, 47, 62, // ${Footer} />
10, 60, 47, 100, 105, 118, 62, 96, 59, 10, 125, 59, // .</div>`;.};
10, 10, 119, 105, 110, 100, 111, 119, 46, 111, 110, 108, // ..window.onl
111, 97, 100, 32, 61, 32, 40, 41, 32, 61, 62, 32, // oad = () =>
114, 101, 110, 100, 101, 114, 40, 104, 40, 65, 112, 112, // render(h(App
41, 44, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, // ), document.
98, 111, 100, 121, 41, 59, 10, 0 // body);.
};
static const unsigned char v3[] = {
118, 97, 114, 32, 101, 44, 110, 44, 95, 44, 116, 44, // var e,n,_,t,
@ -2336,7 +2345,7 @@ static const struct packed_file {
time_t mtime;
} packed_files[] = {
{"/web_root/index.html", v1, sizeof(v1), 1652432837},
{"/web_root/main.js", v2, sizeof(v2), 1652468289},
{"/web_root/main.js", v2, sizeof(v2), 1652514584},
{"/web_root/preact.min.js", v3, sizeof(v3), 1652374364},
{"/web_root/style.css", v4, sizeof(v4), 1652467860},
{"/web_root/user.png", v5, sizeof(v5), 1626172939},

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -91,24 +91,24 @@ const ChangeSettings = function(props) {
method: 'post',
body: `${name}=${encodeURIComponent(val)}`
}).catch(err => err);
const updatevalue1 = ev => update('value1', value1);
const updatevalue2 = ev => update('value2', value2);
return html`
<div style="margin: 0 0.3em;">
<h3 style="background: #c03434; color: #fff; padding: 0.4em;">
Change configuration</h3>
<div style="margin: 0.5em 0;">
<span class="addon">value1:</span>
<input type="text" value=${value1}
<input type="text" value=${value1} onchange=${updatevalue1}
oninput=${ev => setValue1(ev.target.value)} />
<button class="btn" disabled=${!value1}
onclick=${ev => update('value1', value1)}
<button class="btn" disabled=${!value1} onclick=${updatevalue1}
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<div style="margin: 0.5em 0;">
<span class="addon">value2:</span>
<input type="text" value=${value2}
<input type="text" value=${value2} onchange=${updatevalue2}
oninput=${ev => setValue2(ev.target.value)} />
<button class="btn" disabled=${!value2}
onclick=${ev => update('value2', value2)}
<button class="btn" disabled=${!value2} onclick=${updatevalue2}
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<div class="msg">

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1,10 +0,0 @@
PROG ?= example
all: $(PROG)
$(DEBUGGER) ./$(PROG)
$(PROG):
$(CC) ../../mongoose.c -I../.. $(CFLAGS) $(EXTRA) -o $(PROG) main.c
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb

View File

@ -1,90 +0,0 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
// Authenticated user.
// A user can be authenticated by:
// - a name:pass pair
// - a token
// When a user is shown a login screen, she enters a user:pass. If successful,
// a server returns user info which includes token. From that point on,
// client can use token for authentication. Tokens could be refreshed/changed
// on a server side, forcing clients to re-login.
struct user {
const char *name, *pass, *token;
};
// Parse HTTP requests, return authenticated user or NULL
static struct user *getuser(struct mg_http_message *hm) {
// In production, make passwords strong and tokens randomly generated
// In this example, user list is kept in RAM. In production, it can
// be backed by file, database, or some other method.
static struct user users[] = {
{"admin", "admin", "admin_token"},
{"user1", "pass1", "user1_token"},
{"user2", "pass2", "user2_token"},
{NULL, NULL, NULL},
};
char user[256], pass[256];
struct user *u;
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
if (user[0] != '\0' && pass[0] != '\0') {
// Both user and password is set, search by user/password
for (u = users; u->name != NULL; u++)
if (strcmp(user, u->name) == 0 && strcmp(pass, u->pass) == 0) return u;
} else if (user[0] == '\0') {
// Only password is set, search by token
for (u = users; u->name != NULL; u++)
if (strcmp(pass, u->token) == 0) return u;
}
return NULL;
}
// HTTP request handler function
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct user *u = getuser(hm);
// MG_INFO(("[%.*s] auth %s", (int) hm->uri.len, hm->uri.ptr,
// u ? u->name : "NULL"));
if (u == NULL && mg_http_match_uri(hm, "/api/#")) {
// All URIs starting with /api/ must be authenticated
mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n");
} else if (mg_http_match_uri(hm, "/api/user/data")) {
// Data visible to both users and administrators
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
mg_http_printf_chunk(c, "{\"data\":\"%s\"}\n", "this is user data");
mg_http_printf_chunk(c, "");
} else if (mg_http_match_uri(hm, "/api/admin/data")) {
// Data visible only to administrators
if (strcmp(u->name, "admin") == 0) {
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
mg_http_printf_chunk(c, "{\"data\":\"%s\"}\n", "this is admin data");
mg_http_printf_chunk(c, "");
} else {
mg_printf(c, "%s", "HTTP/1.1 403 Denied\r\nContent-Length: 0\r\n\r\n");
}
} else if (mg_http_match_uri(hm, "/api/login")) {
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
mg_http_printf_chunk(c, "{\"user\":\"%s\",\"token\":\"%s\"}\n", u->name,
u->token);
mg_http_printf_chunk(c, "");
} else {
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
}
}
}
int main(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://localhost:8000", cb, NULL);
for (;;) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
return 0;
}

View File

@ -1,178 +0,0 @@
const {h, render, Component} = preact;
const html = htm.bind(h);
class Login extends Component {
state = {user: '', pass: ''};
onclick = (app) => {
const authhdr = 'Basic ' + btoa(this.state.user + ':' + this.state.pass);
const headers = {Authorization: authhdr};
return axios.get('/api/login', {headers})
.then(res => app.setUser(res.data.token))
.catch(err => alert('Login failed'));
};
onpassinput = (ev) => this.setState({pass: ev.target.value});
onuserinput = (ev) => this.setState({user: ev.target.value});
render({app}, {user, pass, signup}) {
return html`
<div class='mx-auto bg-light rounded border my-5' style='max-width: 480px;'>
<div class='form p-5 rounded form-sm'>
<h4 class="text-muted mb-4">Login </h4>
<input type='email' placeholder='Email' class='my-2 form-control'
oninput=${this.onuserinput} value=${user} />
<input type="password" placeholder="Password" class="my-2 form-control"
oninput=${this.onpassinput} value=${pass}
onchange=${ev => this.onclick(app)} />
<div class="mb-4">
<button class="btn btn-info btn-block"
disabled=${!user || !pass} onclick="${ev => this.onclick(app)}"
> Login </button>
</div>
<div class="text-muted small">
Valid logins: admin:admin, user1:pass1, user2:pass2
</div>
</div>
</div>
`
}
};
class Dropdown extends Component {
state = {show: false};
render(props, state) {
const onclick = x => this.setState({show: x});
const show = state.show ? 'show' : '';
return html`
<div class="dropdown autoexpand ${props.cls}">
<div type="buttonx" onclick=${() => onclick(!state.show)}
class="dropdown-toggle my-0 ${props.bcls}"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${props.title}
</div>
<div onclick=${() => onclick(false)} style=${props.dstyle}
class="dropdown-menu ${props.dcls} ${show}">
${props.children}
</div>
</div>`;
}
};
const NavLink = ({href, title, url}) => html`<a class="nav-item nav-link
${url == href ? 'active' : ''}"
target=${href.match(/^http/) ? '_blank' : ''}
href="${href.match(/^http/) ? '' : '#'}${href}">${title}</a>`;
class Header extends Component {
state = {expanded: false};
ontoggle = () => this.setState({expanded: !this.state.expanded});
render(props, state) {
const u = props.app.state.user || {};
return html`
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<a class="navbar-brand" href="#">
<img src="images/logo.png" width="26" height="26" alt="" class="mr-2" />
MyProduct
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"
onclick=${() => this.ontoggle()} >
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse ${state.expanded ? '' : 'collapse'}"
id="navbarNav">
<div class="nav navbar-nav mr-auto">
<${NavLink} href="/" title="Dashboard" url=${props.url} />
<${NavLink} href="/settings" title="Settings" url=${props.url} />
</div>
</div>
<form class="form-inline">
<${Dropdown} title="${u.user}" cls="mr-2"
bcls="btn btn-sm btn-outline-light pointer" dcls="m-0 dropdown-menu-right">
<div onclick=${() => props.app.setUser('')}
class="dropdown-item small pointer text-center">logout</div>
</${Dropdown}>
<img src="images/user.png" class="rounded-circle nav-item mr-2" width="30" />
</form>
</nav>
`
}
};
class AdminDashboard extends Component {
state = {udata: '', adata: ''};
componentDidMount() {
axios.get('/api/user/data').then(r => this.setState({udata: r.data}));
axios.get('/api/admin/data').then(r => this.setState({adata: r.data}));
}
render(props, state) {
console.log('-->', state);
return html`<div class="container-fluid">
this is admin page
<div>admin data, returned by server:
<code class="mx-2">${JSON.stringify(state.adata)}</code></div>
<div>user data, returned by server:
<code class="mx-2">${JSON.stringify(state.udata)}</code></div>
</div>`
}
}
class UserDashboard extends Component {
state = {udata: ''};
componentDidMount() {
axios.get('/api/user/data').then(r => this.setState({udata: r.data}));
axios.get('/api/admin/data').then(r => this.setState({adata: r.data}));
}
render(props, state) {
return html`<div class="container-fluid">
<div>admin data, returned by server (must be empty and dev tools should show 403):
<code class="mx-2">${JSON.stringify(state.adata)}</code></div>
<div>user data, returned by server:
<code class="mx-2">${JSON.stringify(state.udata)}</code></div>
</div>`
}
}
class Settings extends Component {
state = {};
render(props, state) {
return html`<div class="container-fluid">settings page</div>`
}
}
class App extends Component {
state = {user: null, url: '/'};
setUser(token) {
const maxAge = token ? 86400 : 0;
document.cookie = `access_token=${token};path=/;max-age=${maxAge}`;
// this.setState({token});
return axios.get('/api/login')
.then(res => this.setState({user: res.data}))
.catch(err => this.setState({user: {}}));
}
componentDidMount() {
const getCookie = name => {
var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return v ? v[2] : '';
};
this.setUser(getCookie('access_token')); // Move from state1 to 2 or 3
}
render(props, state) {
// We have three states:
// 1. Beginning. We don't know if we have a valid auth token or not
// 2. We sent an /api/login request, and failed to login
// 3. We sent an /api/login request, and succeeded to login
if (!state.user) return ''; // State 1
if (!state.user.user) return h(Login, {app: this}); // State 2
return h( // State 3
'div', {}, h(Header, {url: state.url, app: this}),
h(preactRouter.Router, {
history: History.createHashHistory(),
onChange: ev => this.setState({url: ev.url}),
},
h(state.user.user == 'admin' ? AdminDashboard : UserDashboard,
{default: true, app: this}),
h(Settings, {path: 'settings', app: this})));
}
};
window.onload = () => render(h(App), document.body);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
!function(){function n(n){var t=e.get(this);return t||(t=new Map,e.set(this,t)),1<(t=a(this,t.get(n)||(t.set(n,t=function(n){function t(n){1===u&&(n||(r=r.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?p.push(0,n,r):3===u&&(n||r)?(p.push(3,n,r),u=2):2===u&&"..."===r&&n?p.push(4,n,0):2===u&&r&&!n?p.push(5,0,!0,r):5<=u&&((r||!n&&5===u)&&(p.push(u,0,r,s),u=6),n&&(p.push(u,n,0,s),u=6)),r=""}for(var e,s,u=1,r="",h="",p=[0],a=0;a<n.length;a++){a&&(1===u&&t(),t(a));for(var o=0;o<n[a].length;o++)e=n[a][o],1===u?"<"===e?(t(),p=[p],u=3):r+=e:4===u?r="--"===r&&">"===e?(u=1,""):e+r[0]:h?e===h?h="":r+=e:'"'===e||"'"===e?h=e:">"===e?(t(),u=1):u&&("="===e?(u=5,s=r,r=""):"/"===e&&(u<5||">"===n[a][o+1])?(t(),3===u&&(p=p[0]),(p=(u=p)[0]).push(2,0,u),u=0):" "===e||"\t"===e||"\n"===e||"\r"===e?(t(),u=2):r+=e),3===u&&"!--"===r&&(u=4,p=p[0])}return t(),p}(n)),t),arguments,[])).length?t:t[0]}var a=function(n,t,e,s){var u;t[0]=0;for(var r=1;r<t.length;r++){var h=t[r++],p=t[r]?(t[0]|=h?1:2,e[t[r++]]):t[++r];3===h?s[0]=p:4===h?s[1]=Object.assign(s[1]||{},p):5===h?(s[1]=s[1]||{})[t[++r]]=p:6===h?s[1][t[++r]]+=p+"":h?(u=n.apply(p,a(n,p,e,["",null])),s.push(u),p[0]?t[0]|=2:(t[r-2]=0,t[r]=u)):s.push(p)}return s},e=new Map;"undefined"!=typeof module?module.exports=n:self.htm=n}();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>example</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="bootstrap.min.css" />
<link rel="stylesheet" href="style.css" />
<script src="preact.min.js"></script>
<script src="preact-router.min.js"></script>
<script src="htm.min.js"></script>
<script src="history.min.js"></script>
<script src="axios.min.js"></script>
<script src="app.js"></script>
</head>
<body></body>
</html>

View File

@ -1 +0,0 @@
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("preact")):"function"==typeof define&&define.amd?define(["preact"],e):t.preactRouter=e(t.preact)}(this,function(p){function i(t,e){for(var n in e)t[n]=e[n];return t}function u(t,e,n){var r,o=/(?:\?([^#]*))?(#.*)?$/,i=t.match(o),u={};if(i&&i[1])for(var a=i[1].split("&"),p=0;p<a.length;p++){var c=a[p].split("=");u[decodeURIComponent(c[0])]=decodeURIComponent(c.slice(1).join("="))}t=y(t.replace(o,"")),e=y(e||"");for(var f=Math.max(t.length,e.length),l=0;l<f;l++)if(e[l]&&":"===e[l].charAt(0)){var s=e[l].replace(/(^:|[+*?]+$)/g,""),h=(e[l].match(/[+*?]+$/)||v)[0]||"",d=~h.indexOf("+"),g=~h.indexOf("*"),m=t[l]||"";if(!m&&!g&&(h.indexOf("?")<0||d)){r=!1;break}if(u[s]=decodeURIComponent(m),d||g){u[s]=t.slice(l).map(decodeURIComponent).join("/");break}}else if(e[l]!==t[l]){r=!1;break}return(!0===n.default||!1!==r)&&u}function e(t,e){return t.rank<e.rank?1:t.rank>e.rank?-1:t.index-e.index}function n(t,e){return t.index=e,t.rank=(n=t).props.default?0:function(t){return y(t).map(r).join("")}(n.props.path),t.props;var n}function y(t){return t.replace(/(^\/+|\/+$)/g,"").split("/")}function r(t){return":"==t.charAt(0)?1+"*+?".indexOf(t.charAt(t.length-1))||4:5}function o(){var t;return""+((t=g&&g.location?g.location:g&&g.getCurrentLocation?g.getCurrentLocation():"undefined"!=typeof location?location:b).pathname||"")+(t.search||"")}function a(t,e){return void 0===e&&(e=!1),"string"!=typeof t&&t.url&&(e=t.replace,t=t.url),function(t){for(var e=m.length;e--;)if(m[e].canRoute(t))return 1;return}(t)&&(n=t,void 0===(r=e?"replace":"push")&&(r="push"),g&&g[r]?g[r](n):"undefined"!=typeof history&&history[r+"State"]&&history[r+"State"](null,null,n)),c(t);var n,r}function c(t){for(var e=!1,n=0;n<m.length;n++)!0===m[n].routeTo(t)&&(e=!0);for(var r=C.length;r--;)C[r](t);return e}function f(t){if(t&&t.getAttribute){var e=t.getAttribute("href"),n=t.getAttribute("target");if(e&&e.match(/^\//g)&&(!n||n.match(/^_?self$/i)))return a(e)}}function l(t){if(!(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||0!==t.button))return f(t.currentTarget||t.target||this),s(t)}function s(t){return t&&(t.stopImmediatePropagation&&t.stopImmediatePropagation(),t.stopPropagation&&t.stopPropagation(),t.preventDefault()),!1}function h(t){if(!(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||0!==t.button)){var e=t.target;do{if("A"===(e.nodeName+"").toUpperCase()&&e.getAttribute("href")){if(e.hasAttribute("native"))return;if(f(e))return s(t)}}while(e=e.parentNode)}}var d,v={},g=null,m=[],C=[],b={},U=!1,t=((d=p.Component)&&(k.__proto__=d),((k.prototype=Object.create(d&&d.prototype)).constructor=k).prototype.shouldComponentUpdate=function(t){return!0!==t.static||t.url!==this.props.url||t.onChange!==this.props.onChange},k.prototype.canRoute=function(t){return 0<this.getMatchingChildren(p.toChildArray(this.props.children),t,!1).length},k.prototype.routeTo=function(t){this.setState({url:t});var e=this.canRoute(t);return this.updating||this.forceUpdate(),e},k.prototype.componentWillMount=function(){m.push(this),this.updating=!0},k.prototype.componentDidMount=function(){var e=this;g&&(this.unlisten=g.listen(function(t){e.routeTo(""+(t.pathname||"")+(t.search||""))})),this.updating=!1},k.prototype.componentWillUnmount=function(){"function"==typeof this.unlisten&&this.unlisten(),m.splice(m.indexOf(this),1)},k.prototype.componentWillUpdate=function(){this.updating=!0},k.prototype.componentDidUpdate=function(){this.updating=!1},k.prototype.getMatchingChildren=function(t,r,o){return t.filter(n).sort(e).map(function(t){var e=u(r,t.props.path,t.props);if(e){if(!1===o)return t;var n={url:r,matches:e};return i(n,e),delete n.ref,delete n.key,p.cloneElement(t,n)}}).filter(Boolean)},k.prototype.render=function(t,e){var n=t.children,r=t.onChange,o=e.url,i=this.getMatchingChildren(p.toChildArray(n),o,!0),u=i[0]||null,a=this.previousUrl;return o!==a&&(this.previousUrl=o,"function"==typeof r&&r({router:this,url:o,previous:a,active:i,current:u})),u},k);function k(t){d.call(this,t),t.history&&(g=t.history),this.state={url:t.url||o()},U||("function"==typeof addEventListener&&(g||addEventListener("popstate",function(){c(o())}),addEventListener("click",h)),U=!0)}return t.subscribers=C,t.getCurrentUrl=o,t.route=a,(t.Router=t).Route=function(t){return p.createElement(t.component,t)},t.Link=function(t){return p.createElement("a",i({onClick:l},t))},t.exec=u,t});

File diff suppressed because one or more lines are too long

View File

@ -1,6 +0,0 @@
html, body { margin: 0; padding: 0; height: 100%; }
select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
.btn:focus,.btn:active:focus,.btn.active:focus,
.btn.focus,.btn:active.focus,.btn.active.focus { outline: none; box-shadow:none !important; }
.dropdown.autoexpand:hover>.dropdown-menu { display: block; }
.pointer { cursor: pointer; }