mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-23 11:44:53 +00:00
Add DHCP settings
This commit is contained in:
1 parent
a8309ef705
commit
923f2c7635
3 files changed
+164
-14
No files matched your search
@@ -20,6 +20,16 @@ struct event {
|
||||
const char *text;
|
||||
};
|
||||
|
||||
// DHCP configuration
|
||||
struct dhcp {
|
||||
bool enabled;
|
||||
uint8_t address_begin;
|
||||
uint8_t address_end;
|
||||
unsigned long lease_time_sec;
|
||||
};
|
||||
|
||||
static struct dhcp s_dhcp = {true, 10, 255, 86400};
|
||||
|
||||
// Mocked events
|
||||
static struct event s_events[] = {
|
||||
{.type = 0, .prio = 0, .text = "here goes event 1"},
|
||||
@@ -31,12 +41,6 @@ static struct event s_events[] = {
|
||||
{.type = 1, .prio = 1, .text = "oops. it happened again"},
|
||||
};
|
||||
|
||||
static int event_next(int no, struct event *e) {
|
||||
if (no < 0 || no >= (int) (sizeof(s_events) / sizeof(s_events[0]))) return 0;
|
||||
*e = s_events[no];
|
||||
return no + 1;
|
||||
}
|
||||
|
||||
static const char *s_json_header =
|
||||
"Content-Type: application/json\r\n"
|
||||
"Cache-Control: no-cache\r\n";
|
||||
@@ -62,6 +66,12 @@ static const char *s_ssl_key =
|
||||
"6YbyU/ZGtdGfbaGYYJwatKNMX00OIwtb8A==\n"
|
||||
"-----END EC PRIVATE KEY-----\n";
|
||||
|
||||
static int event_next(int no, struct event *e) {
|
||||
if (no < 0 || no >= (int) (sizeof(s_events) / sizeof(s_events[0]))) return 0;
|
||||
*e = s_events[no];
|
||||
return no + 1;
|
||||
}
|
||||
|
||||
// SNTP connection event handler. When we get a response from an SNTP server,
|
||||
// adjust s_boot_timestamp. We'll get a valid time from that point on
|
||||
static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
@@ -160,10 +170,10 @@ static size_t print_events(void (*out)(char, void *), void *ptr, va_list *ap) {
|
||||
int no = 0;
|
||||
while ((no = event_next(no, &e)) != 0) {
|
||||
len += mg_xprintf(out, ptr, "%s{%m:%lu,%m:%d,%m:%d,%m:%m}", //
|
||||
len == 0 ? "" : ",", //
|
||||
MG_ESC("time"), e.timestamp, //
|
||||
MG_ESC("type"), e.type, //
|
||||
MG_ESC("prio"), e.prio, //
|
||||
len == 0 ? "" : ",", //
|
||||
MG_ESC("time"), e.timestamp, //
|
||||
MG_ESC("type"), e.type, //
|
||||
MG_ESC("prio"), e.prio, //
|
||||
MG_ESC("text"), MG_ESC(e.text));
|
||||
}
|
||||
(void) ap;
|
||||
@@ -174,6 +184,28 @@ static void handle_events_get(struct mg_connection *c) {
|
||||
mg_http_reply(c, 200, s_json_header, "[%M]", print_events);
|
||||
}
|
||||
|
||||
static void handle_dhcp_set(struct mg_connection *c, struct mg_str body) {
|
||||
struct dhcp dhcp = {};
|
||||
mg_json_get_bool(body, "$.enabled", &dhcp.enabled);
|
||||
dhcp.address_begin = mg_json_get_long(body, "$.address_begin", 0);
|
||||
dhcp.address_end = mg_json_get_long(body, "$.address_end", 0);
|
||||
dhcp.lease_time_sec = mg_json_get_long(body, "$.lease_time_sec", 0);
|
||||
s_dhcp = dhcp; // Save to the device flash, too
|
||||
bool ok = true;
|
||||
mg_http_reply(c, 200, s_json_header,
|
||||
"{%m:%s,%m:%m}", //
|
||||
MG_ESC("status"), ok ? "true" : "false", //
|
||||
MG_ESC("message"), MG_ESC(ok ? "Success" : "Failed"));
|
||||
}
|
||||
|
||||
static void handle_dhcp_get(struct mg_connection *c) {
|
||||
mg_http_reply(c, 200, s_json_header, "{%m:%s,%m:%hhu,%m:%hhu,%m:%lu}", //
|
||||
MG_ESC("enabled"), s_dhcp.enabled ? "true" : "false", //
|
||||
MG_ESC("address_begin"), s_dhcp.address_begin, //
|
||||
MG_ESC("address_end"), s_dhcp.address_end, //
|
||||
MG_ESC("lease_time_sec"), s_dhcp.lease_time_sec);
|
||||
}
|
||||
|
||||
// HTTP request handler function
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (ev == MG_EV_ACCEPT && fn_data != NULL) {
|
||||
@@ -195,6 +227,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
handle_stats_get(c);
|
||||
} else if (mg_http_match_uri(hm, "/api/events/get")) {
|
||||
handle_events_get(c);
|
||||
} else if (mg_http_match_uri(hm, "/api/dhcp/get")) {
|
||||
handle_dhcp_get(c);
|
||||
} else if (mg_http_match_uri(hm, "/api/dhcp/set")) {
|
||||
handle_dhcp_set(c, hm->body);
|
||||
} else {
|
||||
struct mg_http_serve_opts opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
|
||||
@@ -1 +1 @@
|
||||
/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter var,Helvetica,sans-serif;font-feature-settings:"cv11","ss01";font-variation-settings:"opsz" 32}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.bottom-0{bottom:0}.left-0{left:0}.right-auto{right:auto}.top-0{top:0}.z-10{z-index:10}.z-\[48\]{z-index:48}.z-\[60\]{z-index:60}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.mb-1{margin-bottom:.25rem}.mt-1{margin-top:.25rem}.mt-5{margin-top:1.25rem}.mt-7{margin-top:1.75rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-96{width:24rem}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-\[85rem\]{max-width:85rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.border-separate{border-collapse:initial}.border-spacing-0{--tw-border-spacing-x:0px;--tw-border-spacing-y:0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.translate-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear iLine truncated
|
||||
/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter var,Helvetica,sans-serif;font-feature-settings:"cv11","ss01";font-variation-settings:"opsz" 32}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-0{left:0}.right-auto{right:auto}.top-0{top:0}.z-10{z-index:10}.z-\[48\]{z-index:48}.z-\[60\]{z-index:60}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.mb-1{margin-bottom:.25rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-7{margin-top:1.75rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-0{width:0}.w-11{width:2.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-96{width:24rem}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-\[85rem\]{max-width:85rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-separate{border-collapse:initial}.border-spacing-0{--tw-border-spacing-x:0px;--tw-border-spacing-y:0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.translate-x-0{transform:translate(var(--tw-Line truncated
|
||||
@@ -55,6 +55,44 @@ function Button({title, onclick, disabled, cls, icon, ref, colors, hovercolor, d
|
||||
<//>`
|
||||
};
|
||||
|
||||
function Toast({ok, text, close}) {
|
||||
const closebtn = useRef(null);
|
||||
const from = 'translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2';
|
||||
const to = 'translate-y-0 opacity-100 sm:translate-x-0';
|
||||
const [tr, setTr] = useState(from);
|
||||
useEffect(function() {
|
||||
setTr(to);
|
||||
setTimeout(ev => closebtn && closebtn.current.click && closebtn.current.click(), 1500);
|
||||
}, []);
|
||||
const onclose = ev => { setTr(from); setTimeout(close, 300); };
|
||||
return html`
|
||||
<div aria-live="assertive" class="z-10 pointer-events-none absolute inset-0 flex items-end px-4 py-6 sm:items-start sm:p-6">
|
||||
<div class="flex w-full flex-col items-center space-y-4 sm:items-end">
|
||||
<div class="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 transform ease-out duration-300 transition ${tr}">
|
||||
<div class="p-4">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0">
|
||||
<${ok ? IconOk : IconFailed} class="h-6 w-6 ${ok ? 'text-green-400' : 'text-red-400'}" />
|
||||
<//>
|
||||
<div class="ml-3 w-0 flex-1 pt-0.5">
|
||||
<p class="text-sm font-medium text-gray-900">${text}</p>
|
||||
<p class="hidden mt-1 text-sm text-gray-500">Anyone with a link can now view this file.</p>
|
||||
<//>
|
||||
<div class="ml-4 flex flex-shrink-0">
|
||||
<button type="button" ref=${closebtn} onclick=${onclose} class="inline-flex rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none">
|
||||
<span class="sr-only">Close</span>
|
||||
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
|
||||
<//>
|
||||
<//>
|
||||
<//>
|
||||
<//>
|
||||
<//>
|
||||
<//>
|
||||
<//>
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function Login({login}) {
|
||||
const [user, setUser] = useState('');
|
||||
const [pass, setPass] = useState('');
|
||||
@@ -298,8 +336,84 @@ function Main({}) {
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function Devices({}) {
|
||||
return html`<div class="p-2">Devices<//>`;
|
||||
function TextValue({value, setfn, disabled, placeholder, type, addonRight, addonLeft, attr}) {
|
||||
const f = type == 'number' ? x => setfn(parseInt(x)) : setfn;
|
||||
return html`
|
||||
<div class="flex w-full items-center rounded border shadow-sm">
|
||||
${ addonLeft && html`<span class="inline-flex font-normal truncate py-1 border-r bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs">${addonLeft}</>` }
|
||||
<input type=${type || 'text'} disabled=${disabled}
|
||||
oninput=${ev => f(ev.target.value)} ...${attr}
|
||||
class="font-normal text-sm rounded w-full flex-1 py-0.5 px-2 text-gray-700 placeholder:text-gray-400 focus:outline-none disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500" placeholder=${placeholder} value=${value} />
|
||||
${ addonRight && html`<span class="inline-flex font-normal truncate py-1 border-l bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs">${addonRight}</>` }
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function SelectValue({value, setfn, options}) {
|
||||
const toInt = x => x == parseInt(x) ? parseInt(x) : x;
|
||||
const onchange = ev => setfn(toInt(ev.target.value));
|
||||
return html`
|
||||
<select onchange=${onchange} class="w-full rounded font-normal border py-0.5 px-1 text-gray-600 focus:outline-none text-sm">
|
||||
${options.map(v => html`<option value=${v[0]} selected=${v[0] == value}>${v[1]}<//>`) }
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function SwitchValue({value, setfn}) {
|
||||
const onclick = ev => setfn(!value);
|
||||
const bg = !!value ? 'bg-blue-600' : 'bg-gray-200';
|
||||
const tr = !!value ? 'translate-x-5' : 'translate-x-0';
|
||||
return html`
|
||||
<button type="button" onclick=${onclick} class="${bg} inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-0 ring-0" role="switch" aria-checked=${!!value}>
|
||||
<span aria-hidden="true" class="${tr} pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 focus:ring-0 transition duration-200 ease-in-out"></span>
|
||||
</button>`;
|
||||
};
|
||||
|
||||
function Setting(props) {
|
||||
return html`
|
||||
<div class=${props.cls || 'grid grid-cols-2 gap-2 my-1'}>
|
||||
<label class="flex items-center text-sm text-gray-700 mr-2 font-medium">${props.title}<//>
|
||||
<div class="flex items-center">
|
||||
${props.type == 'switch' ? h(SwitchValue, props) :
|
||||
props.type == 'select' ? h(SelectValue, props) :
|
||||
h(TextValue, props) }
|
||||
<//>
|
||||
<//>`;
|
||||
};
|
||||
|
||||
function DHCP({}) {
|
||||
const [dhcp, setDhcp] = useState(null);
|
||||
const [saveResult, setSaveResult] = useState(null);
|
||||
const refresh = () => fetch('/api/dhcp/get')
|
||||
.then(r => r.json())
|
||||
.then(r => setDhcp(r));
|
||||
useEffect(refresh, []);
|
||||
|
||||
const mksetfn = k => (v => setDhcp(x => Object.assign({}, x, {[k]: v})));
|
||||
const onsave = ev => fetch('/api/dhcp/set', {
|
||||
method: 'post', body: JSON.stringify(dhcp)
|
||||
}).then(r => r.json())
|
||||
.then(r => setSaveResult(r))
|
||||
.then(refresh);
|
||||
|
||||
if (!dhcp) return '';
|
||||
return html`
|
||||
<div class="m-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
|
||||
<div class="py-1 divide-y border rounded bg-white flex flex-col">
|
||||
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
|
||||
DHCP Server Settings
|
||||
<//>
|
||||
<div class="py-2 px-5 flex-1 flex flex-col relative">
|
||||
${saveResult && html`<${Toast} ok=${saveResult.status}
|
||||
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
|
||||
|
||||
<${Setting} title="Enabled" value=${dhcp.enabled} setfn=${mksetfn('enabled')} type="switch" />
|
||||
<${Setting} title="Start Address" value=${dhcp.address_begin} setfn=${mksetfn('address_begin')} type="number" addonLeft="192.168.0." disabled=${!dhcp.enabled} />
|
||||
<${Setting} title="End Address" value=${dhcp.address_end} setfn=${mksetfn('address_end')} type="number" addonLeft="192.168.0." disabled=${!dhcp.enabled} />
|
||||
<${Setting} title="Lease Time" value=${dhcp.lease_time_sec} setfn=${mksetfn('lease_time_sec')} type="number" addonRight="seconds" disabled=${!dhcp.enabled} />
|
||||
<div class="mb-1 mt-3 flex place-content-end"><${Button} icon=${IconSave} onclick=${onsave} title="Save Settings" /><//>
|
||||
<//>
|
||||
<//>
|
||||
<//>`;
|
||||
};
|
||||
|
||||
const App = function({}) {
|
||||
@@ -325,7 +439,7 @@ const App = function({}) {
|
||||
<div class="${showSidebar && 'pl-72'} transition-all duration-300 transform">
|
||||
<${Router} onChange=${ev => setUrl(ev.url)} history=${History.createHashHistory()} >
|
||||
<${Main} default=${true} />
|
||||
<${Devices} path="aa" />
|
||||
<${DHCP} path="dhcp" />
|
||||
<//>
|
||||
<//>
|
||||
<//>`;
|
||||
|
||||
Reference in new issue
Block a user