Implement layout and device list page
This commit is contained in:
1 parent
f33717acb0
commit
1c9061ab5a
19 files changed
+1151
-30
No files matched your search
+1
-3
@@ -6,12 +6,10 @@
|
|||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"sourceType": "script"
|
"sourceType": "script"
|
||||||
},
|
},
|
||||||
"plugins": ["prettier"],
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-shadow": ["error", { "allow": ["err"] }],
|
"no-shadow": ["error", { "allow": ["err"] }],
|
||||||
"handle-callback-err": "error",
|
"handle-callback-err": "error",
|
||||||
"prefer-arrow-callback": "error",
|
"prefer-arrow-callback": "error",
|
||||||
"curly": ["error", "multi", "consistent"],
|
"curly": ["error", "multi", "consistent"]
|
||||||
"prettier/prettier": "error"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true
|
||||||
"commonjs": true
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+40
-2
@@ -1,5 +1,43 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const m = require("mithril");
|
import m from "mithril";
|
||||||
|
|
||||||
m.render(document.body, "hello world");
|
import layout from "./layout";
|
||||||
|
import * as overviewPage from "./overview-page";
|
||||||
|
import * as devicesPage from "./devices-page";
|
||||||
|
import * as errorPage from "./error-page";
|
||||||
|
|
||||||
|
let state;
|
||||||
|
|
||||||
|
function pagify(pageName, page) {
|
||||||
|
const component = {
|
||||||
|
render: () => {
|
||||||
|
if (state && state.error) return m(errorPage.component, state);
|
||||||
|
else return m(layout, { page: pageName }, m(page.component, state));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (page.init)
|
||||||
|
component.onmatch = args => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
page
|
||||||
|
.init(args)
|
||||||
|
.then(st => {
|
||||||
|
if (!st) return m.reoute.set("/");
|
||||||
|
state = st;
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
state = { error: err.message };
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.route(document.body, "/overview", {
|
||||||
|
"/overview": pagify("overview", overviewPage),
|
||||||
|
"/devices": pagify("devices", devicesPage)
|
||||||
|
});
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
pageSize: 10,
|
||||||
|
index: [
|
||||||
|
{
|
||||||
|
label: "Serial number",
|
||||||
|
parameter: "DeviceID.SerialNumber"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Product class",
|
||||||
|
parameter: "DeviceID.ProductClass"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Software version",
|
||||||
|
parameter: "InternetGatewayDevice.DeviceInfo.SoftwareVersion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "MAC",
|
||||||
|
parameter:
|
||||||
|
"InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
@import "normalize.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--base-font-size: 13px;
|
||||||
|
--base-line-height: 16px;
|
||||||
|
|
||||||
|
--color1: #ebe0bf;
|
||||||
|
--color2: #b9b098;
|
||||||
|
--color3: #f2ebd5;
|
||||||
|
--color4: #046380;
|
||||||
|
--color5: #002f2f;
|
||||||
|
|
||||||
|
--fade: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
option,
|
||||||
|
textarea {
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: var(--color5);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color4);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color var(--fade), color var(--fade);
|
||||||
|
&:visited {
|
||||||
|
color: var(--color4);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: var(--color4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong,
|
||||||
|
b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
background-color: #f2eddf;
|
||||||
|
border-bottom: 1px solid var(--color1);
|
||||||
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > nav {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: visible;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -1px;
|
||||||
|
|
||||||
|
& > ul {
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
margin: 0 2px;
|
||||||
|
background-color: var(--color1);
|
||||||
|
display: inline-block;
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
transition: background-color var(--fade);
|
||||||
|
&.active,
|
||||||
|
&:hover {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > a,
|
||||||
|
& > a:visited,
|
||||||
|
& > a:hover {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 140px;
|
||||||
|
color: var(--color4);
|
||||||
|
text-decoration: none;
|
||||||
|
background: none;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > .logo {
|
||||||
|
margin: 10px;
|
||||||
|
display: inline;
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
margin: 10px;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.table {
|
||||||
|
border-spacing: 0;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
& th {
|
||||||
|
color: var(--color4);
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 2px solid var(--color4);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
border-bottom: 1px dotted var(--color1);
|
||||||
|
& .action a {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& tbody tr:hover td {
|
||||||
|
& .action {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
transition: background-color var(--fade);
|
||||||
|
background-color: var(--color3);
|
||||||
|
}
|
||||||
|
|
||||||
|
& tfoot td > a {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: var(--color4);
|
||||||
|
font-size: 130%;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: var(--color5);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: var(--color5);
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove button padding in FF */
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="button"],
|
||||||
|
button {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 6em;
|
||||||
|
position: relative;
|
||||||
|
top: 0px;
|
||||||
|
vertical-align: baseline;
|
||||||
|
margin: 0 0.1em;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.1em 0.6em;
|
||||||
|
border-top-left-radius: 1em;
|
||||||
|
border-bottom-right-radius: 1em;
|
||||||
|
border-top-right-radius: 0.2em;
|
||||||
|
border-bottom-left-radius: 0.2em;
|
||||||
|
|
||||||
|
color: var(--color4);
|
||||||
|
transition: background-color var(--fade), color var(--fade);
|
||||||
|
|
||||||
|
&:link,
|
||||||
|
&:visited {
|
||||||
|
color: var(--color4);
|
||||||
|
background-color: var(--color1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
color: var(--color1);
|
||||||
|
background-color: var(--color4);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--color1);
|
||||||
|
color: var(--color2);
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+450
@@ -0,0 +1,450 @@
|
|||||||
|
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in
|
||||||
|
* IE on Windows Phone and in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-ms-text-size-adjust: 100%; /* 2 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
nav,
|
||||||
|
section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
* 1. Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
main {
|
||||||
|
/* 1 */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct margin in IE 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 1em 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the gray background on active links in IE 10.
|
||||||
|
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent; /* 1 */
|
||||||
|
-webkit-text-decoration-skip: objects; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font style in Android 4.3-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct background and color in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background-color: #ff0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
video {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in iOS 4-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the overflow in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers (opinionated).
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
/* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
/* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||||
|
* controls in Android 4.
|
||||||
|
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
html [type="button"],
|
||||||
|
/* 1 */ [type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct display in IE 9-.
|
||||||
|
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
display: inline-block; /* 1 */
|
||||||
|
vertical-align: baseline; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10-.
|
||||||
|
* 2. Remove the padding in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-cancel-button,
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details,
|
||||||
|
/* 1 */ menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scripting
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hidden
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import m from "mithril";
|
||||||
|
|
||||||
|
import config from "./config";
|
||||||
|
|
||||||
|
function load(filter, limit) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
m
|
||||||
|
.request({
|
||||||
|
url: "/api/devices/",
|
||||||
|
data: { filter: JSON.stringify(filter), limit: limit }
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
err.message = err.message || "Unknown error";
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function count(filter, limit) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
function extract(xhr) {
|
||||||
|
return +xhr.getResponseHeader("x-total-count");
|
||||||
|
}
|
||||||
|
m
|
||||||
|
.request({
|
||||||
|
method: "HEAD",
|
||||||
|
url: "/api/devices/",
|
||||||
|
data: { filter: JSON.stringify(filter), limit: limit },
|
||||||
|
extract: extract
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
err.message = err.message || "Unknown error";
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(args) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Promise.all([count(args.filter), load(args.filter, config.pageSize)])
|
||||||
|
.then(res => {
|
||||||
|
resolve({ filter: args.filter, total: res[0], devices: res[1] });
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTable(devices, parameters, total, showMoreCallback) {
|
||||||
|
let labels = [];
|
||||||
|
for (let param of parameters) labels.push(m("th", param.label));
|
||||||
|
|
||||||
|
let rows = [];
|
||||||
|
for (let device of devices)
|
||||||
|
rows.push(
|
||||||
|
m(
|
||||||
|
"tr",
|
||||||
|
parameters.map(p => {
|
||||||
|
if (!device[p.parameter] || !device[p.parameter].value)
|
||||||
|
return m("td.na");
|
||||||
|
if (!device[p.parameter].value[0] === "") return m("td.blank");
|
||||||
|
return m("td", device[p.parameter].value[0]);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let footerElements = [`${devices.length}/${total}`];
|
||||||
|
|
||||||
|
if (devices.length < total)
|
||||||
|
footerElements.push(m("a", { onclick: showMoreCallback }, "Show more"));
|
||||||
|
|
||||||
|
let tfoot = m(
|
||||||
|
"tfoot",
|
||||||
|
m("tr", m("td", { colspan: labels.length }, footerElements))
|
||||||
|
);
|
||||||
|
|
||||||
|
return m("table.table", [
|
||||||
|
m("thead", m("tr", labels)),
|
||||||
|
m("tbody", rows),
|
||||||
|
tfoot
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const component = {
|
||||||
|
view: vnode => {
|
||||||
|
document.title = "Devices - GenieACS";
|
||||||
|
function showMore() {
|
||||||
|
const lastDevice = vnode.attrs.devices[vnode.attrs.devices.length - 1];
|
||||||
|
let f = Object.assign({}, vnode.attrs.filter, {
|
||||||
|
"DeviceID.ID>": lastDevice["DeviceID.ID"].value[0]
|
||||||
|
});
|
||||||
|
load(f, config.pageSize).then(res => {
|
||||||
|
vnode.attrs.devices = vnode.attrs.devices.concat(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
m("h1", "Listing devices"),
|
||||||
|
renderTable(
|
||||||
|
vnode.attrs.devices,
|
||||||
|
config.index,
|
||||||
|
vnode.attrs.total,
|
||||||
|
showMore
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { init, component };
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const component = {
|
||||||
|
view: function(vnode) {
|
||||||
|
document.title = "Error! - GenieACS";
|
||||||
|
return vnode.attrs.error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { component };
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import m from "mithril";
|
||||||
|
|
||||||
|
import menu from "./menu";
|
||||||
|
|
||||||
|
const layout = {
|
||||||
|
view: vnode => {
|
||||||
|
return [
|
||||||
|
m("#header", [
|
||||||
|
m("img.logo", { src: "logo.svg" }),
|
||||||
|
m(menu, { page: vnode.attrs.page })
|
||||||
|
]),
|
||||||
|
m("#content", [vnode.children])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default layout;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import m from "mithril";
|
||||||
|
|
||||||
|
const menu = {
|
||||||
|
view: vnode => {
|
||||||
|
const active = { [vnode.attrs.page]: "active" };
|
||||||
|
|
||||||
|
return m(
|
||||||
|
"nav",
|
||||||
|
m("ul", [
|
||||||
|
m(
|
||||||
|
"li",
|
||||||
|
{ class: active["overview"] },
|
||||||
|
m("a", { href: "#!/overview" }, "Overview")
|
||||||
|
),
|
||||||
|
m(
|
||||||
|
"li",
|
||||||
|
{ class: active["devices"] },
|
||||||
|
m("a", { href: "#!/devices" }, "Devices")
|
||||||
|
)
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default menu;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import m from "mithril";
|
||||||
|
|
||||||
|
const init = function() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
resolve({ message: "Hello world!" });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const component = {
|
||||||
|
view: vnode => {
|
||||||
|
document.title = "Overview - GenieACS";
|
||||||
|
return m(
|
||||||
|
"a",
|
||||||
|
{ href: "#!/devices/202BC1-BM632w-000000" },
|
||||||
|
vnode.attrs.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { init, component };
|
||||||
+7
-4
@@ -4,9 +4,9 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"lint": "eslint --fix server/*.js client/*.js",
|
"lint": "eslint --fix server/*.js client/*.js;prettier --write server/*.js client/*.js client/css/*.css",
|
||||||
"build": "webpack",
|
"build": "npm run lint;webpack;postcss -c postcss.config.js client/css/app.css -o public/app.css",
|
||||||
"start": "npm run lint;npm run build;node server/server"
|
"start": "node server/server"
|
||||||
},
|
},
|
||||||
"author": "Zaid Abdulla <zaid@genieacs.com>",
|
"author": "Zaid Abdulla <zaid@genieacs.com>",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -17,8 +17,11 @@
|
|||||||
"mithril": "^1.1.5"
|
"mithril": "^1.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"cssnano": "^3.10.0",
|
||||||
"eslint": "^4.12.1",
|
"eslint": "^4.12.1",
|
||||||
"eslint-plugin-prettier": "^2.3.1",
|
"postcss-cli": "^4.1.1",
|
||||||
|
"postcss-cssnext": "^3.0.2",
|
||||||
|
"postcss-import": "^11.0.0",
|
||||||
"prettier": "^1.8.2",
|
"prettier": "^1.8.2",
|
||||||
"webpack": "^3.9.1"
|
"webpack": "^3.9.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require("postcss-import"),
|
||||||
|
require("postcss-cssnext")({warnForDuplicates: false}),
|
||||||
|
require("cssnano")
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@@ -1,5 +1,8 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<title>GenieACS</title>
|
||||||
|
<link rel="shortcut icon" type="image/png" href="favicon.png" />
|
||||||
|
<link rel="stylesheet" href="app.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="310" height="64" viewBox="0 0 82.020832 16.933334"><defs><linearGradient id="b"><stop offset="0" stop-color="#737373"/><stop offset="1" stop-color="#4b4b4b"/></linearGradient><linearGradient id="a"><stop offset="0" stop-color="#b72f5f"/><stop offset="1" stop-color="#872346"/></linearGradient><linearGradient xlink:href="#a" id="c" gradientUnits="userSpaceOnUse" gradientTransform="translate(173.233 -461.003) scale(.21343)" x1="16.045" y1="132.803" x2="16.045" y2="-81.164"/><linearGradient xlink:href="#a" id="d" gradientUnits="userSpaceOnUse" x1="46.459" y1="281.163" x2="46.459" y2="296.66" gradientTransform="translate(1.09 16.9) scale(.94143)"/><linearGradient xlink:href="#b" id="e" gradientUnits="userSpaceOnUse" gradientTransform="translate(11.105 -994.648) scale(3.55816)" x1="46.459" y1="281.163" x2="46.459" y2="296.66"/></defs><path d="M173.233-461.003l1.953-3.714c-.868-1.509-2.042-2.814-3.522-3.917-2.419-1.43-4.386-1.199-5.901.694 2.931 1.323 5.421 3.635 7.47 6.937m-7.577 11.151a6.095 6.095 0 0 0 .673-3.49 23.76 23.76 0 0 0-1.473-6.285 20.998 20.998 0 0 1-1.206-4.855 6.766 6.766 0 0 1 .48-3.426c.463-1.117 1.298-2.074 2.508-2.87-1.302-.392-2.44-.499-3.415-.32-1.707.326-2.82 1.354-3.34 3.083a10.152 10.152 0 0 0-.267 5.08 33.38 33.38 0 0 0 1.238 4.674 32.993 32.993 0 0 1 1.227 4.856c.107.626.118 1.277.032 1.953a4.72 4.72 0 0 1-.779 2.006 4.498 4.498 0 0 1-1.707 1.44c.697.129 1.387.235 2.07.32a4.124 4.124 0 0 0 2.07-.351c.84-.37 1.47-.975 1.89-1.815m22.463 5.123l.63-.62-5.25-4.77 1.579-1.579 4.77 5.25.619-.629-5.24-4.77 1.483-1.483 4.77 5.24.63-.62-5.25-4.77 1.58-1.58 5.015 5.016 1.067-1.045-4.503-4.504h.01l-5.87-5.859-3.563-1.462-5.102 3.831-.02.022-.022-.022.01-.02 3.394-4.526-1.131-.458-4.973 4.973 1.26 3.073 4.652-3.49.01-.01.033.02-.01.012-3.95 5.26.64 1.57 4.814 4.802h-.01l5.559 5.549 1.057-1.046-5.027-5.026 1.569-1.569 4.77 5.24m4.14-28.173c4.405 4.403 6.607 9.722 6.607 15.954 0 6.218-2.202 11.536-6.606 15.954-4.418 4.404-9.736 6.606-15.954 6.606-6.233 0-11.55-2.202-15.955-6.606-2.703-2.71-4.574-5.756-5.613-9.135l.448.245c2.241 1.181 4.226.577 5.955-1.814-1.565-.32-2.82-.658-3.767-1.014a5.467 5.467 0 0 1-1.43-.821c-.96-.762-1.565-1.73-1.814-2.903a11.315 11.315 0 0 1 .032-5.005c.81-4.34 2.874-8.16 6.19-11.461 4.403-4.411 9.721-6.617 15.954-6.617 6.225 0 11.543 2.206 15.954 6.617" fill="url(#c)" transform="matrix(.35176 0 0 -.35176 -53.595 -152.27)"/><path style="line-height:6.61458302px;-inkscape-font-specification:Norwegian" d="M47.104 281.834c-.456 0-.816.388-.816.849 0 .46.355.816.816.816a.82.82 0 0 0 .833-.816.85.85 0 0 0-.833-.849zm-21.502 1.404c-.205 0-.382.046-.5.13-.118.085-.187.201-.187.4v.981l-.151-.031a9.89 9.89 0 0 0-1.872-.173c-1.288 0-2.148.066-2.657.374a1.264 1.264 0 0 0-.548.676c-.119.313-.177.73-.177 1.28v6.123c0 .982.25 1.508.787 1.824.539.315 1.407.41 2.628.41 1.308 0 2.178-.13 2.701-.484.524-.354.745-.932.745-1.942 0-.821-.147-1.37-.543-1.722-.395-.352-1.075-.528-2.186-.528h-2.787v-1.234l.148.03c.478.092 1.094.11 1.89.11 1.292 0 2.148-.112 2.658-.453.51-.34.724-.915.724-1.958v-3.284c0-.198-.07-.314-.186-.399a.843.843 0 0 0-.487-.13zm6.112 1.29c-1.226 0-2.038.137-2.526.495-.488.357-.697.945-.697 1.947v2.84c0 .935.23 1.54.743 1.932.512.39 1.337.571 2.544.571 1.392 0 2.17-.058 2.567-.196.199-.069.298-.15.355-.246.058-.097.078-.227.078-.405 0-.208-.04-.378-.117-.485-.076-.108-.186-.171-.396-.171-.794 0-1.057.19-2.408.19-.593 0-1.06-.078-1.395-.255-.335-.177-.53-.473-.53-.84v-.89h4.301a.717.717 0 0 0 .539-.213c.13-.138.213-.342.213-.62v-1.212c0-1.037-.179-1.623-.654-1.97-.476-.348-1.3-.472-2.617-.472zm8.83 0c-1.303 0-2.117.086-2.59.49-.472.403-.665 1.168-.665 2.623v4.002c0 .215.074.338.197.426.123.088.31.134.524.134.223 0 .412-.046.537-.134.125-.088.198-.211.198-.426v-3.81c0-.424.006-.754.04-1.019.036-.265.102-.47.236-.623.135-.152.328-.236.574-.28.246-.044.552-.053.949-.053.4 0 .712.01.963.053.25.044.447.126.587.278.139.151.211.358.25.623.038.265.046.597.046 1.02v3.81c0 .217.07.34.19.427.122.088.308.134.53.134.215 0 .401-.046.525-.134.123-.088.196-.211.196-.426v-4.002c0-1.455-.201-2.219-.682-2.623-.48-.404-1.303-.49-2.605-.49zm12.996 0c-1.227 0-2.038.137-2.527.495-.488.357-.696.945-.696 1.947v2.84c0 .935.23 1.54.742 1.932.513.39 1.338.571 2.545.571 1.392 0 2.17-.058 2.567-.196.2-.069.298-.15.355-.246.058-.097.079-.227.079-.405 0-.208-.04-.378-.117-.485-.077-.108-.188-.171-.398-.171-.793 0-1.056.19-2.406.19-.594 0-1.062-.078-1.396-.255-.335-.177-.53-.473-.53-.84v-.89h4.301a.716.716 0 0 0 .539-.213c.13-.138.213-.342.213-.62v-1.212c0-1.037-.18-1.623-.655-1.97-.475-.348-1.3-.472-2.616-.472zm-6.436.13c-.212 0-.398.047-.522.14-.124.09-.198.22-.198.435v6.41c0 .215.073.338.196.426.123.088.31.134.524.134.223 0 .414-.046.54-.134.124-.088.198-.211.198-.426v-6.41c0-.215-.075-.342-.201-.434-.126-.092-.316-.142-.537-.142zm-24.212 1.135c.662 0 1.4.065 1.918.146l.105.016v.92c0 .246-.01.453-.058.632a.757.757 0 0 1-Line truncated
|
||||||
|
After Width: | Height: | Size: 8.5 KiB |
+51
-4
@@ -6,6 +6,7 @@ const url = require("url");
|
|||||||
const querystring = require("querystring");
|
const querystring = require("querystring");
|
||||||
|
|
||||||
const config = require("./config");
|
const config = require("./config");
|
||||||
|
const device = require("./device");
|
||||||
|
|
||||||
const commaAscii = ",".charCodeAt(0);
|
const commaAscii = ",".charCodeAt(0);
|
||||||
const newLineAscii = "\n".charCodeAt(0);
|
const newLineAscii = "\n".charCodeAt(0);
|
||||||
@@ -19,6 +20,7 @@ function filtersToQuery(filters) {
|
|||||||
for (let [k, v] of Object.entries(filter)) {
|
for (let [k, v] of Object.entries(filter)) {
|
||||||
let [param, op] = k.split(/([^a-zA-Z0-9\-_.].*)/, 2);
|
let [param, op] = k.split(/([^a-zA-Z0-9\-_.].*)/, 2);
|
||||||
if (!op || op === "=") q[param] = v;
|
if (!op || op === "=") q[param] = v;
|
||||||
|
else if (op === ">") q[param] = { $gt: v };
|
||||||
else throw new Error(`Operator "${op}" not recognized`);
|
else throw new Error(`Operator "${op}" not recognized`);
|
||||||
}
|
}
|
||||||
queries.push(q);
|
queries.push(q);
|
||||||
@@ -28,22 +30,63 @@ function filtersToQuery(filters) {
|
|||||||
else return { $or: queries };
|
else return { $or: queries };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function count(resource, filters, limit) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let qs = {};
|
||||||
|
if (filters) {
|
||||||
|
let q = filtersToQuery(filters);
|
||||||
|
if (resource === "devices") q = device.transposeQuery(q);
|
||||||
|
qs.query = JSON.stringify(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit) qs.limit = limit;
|
||||||
|
|
||||||
|
let options = url.parse(
|
||||||
|
`${config.GENIEACS_NBI}${resource}?${querystring.stringify(qs)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
options.method = "HEAD";
|
||||||
|
|
||||||
|
let _http = options.protocol === "https:" ? https : http;
|
||||||
|
|
||||||
|
_http
|
||||||
|
.request(options, res => {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
reject(new Error(`Unexpected status code ${res.statusCode}`));
|
||||||
|
res.resume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(+res.headers["total"]);
|
||||||
|
})
|
||||||
|
.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function query(resource, filters, limit, callback) {
|
function query(resource, filters, limit, callback) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let ret;
|
let ret;
|
||||||
if (!callback) ret = [];
|
if (!callback) ret = [];
|
||||||
let q = {};
|
let qs = {};
|
||||||
if (filters) q.query = JSON.stringify(filtersToQuery(filters));
|
if (filters) {
|
||||||
|
let q = filtersToQuery(filters);
|
||||||
|
if (resource === "devices") q = device.transposeQuery(q);
|
||||||
|
qs.query = JSON.stringify(q);
|
||||||
|
}
|
||||||
|
|
||||||
if (limit) q.limit = limit;
|
if (limit) qs.limit = limit;
|
||||||
|
|
||||||
let options = url.parse(
|
let options = url.parse(
|
||||||
`${config.GENIEACS_NBI}${resource}?${querystring.stringify(q)}`
|
`${config.GENIEACS_NBI}${resource}?${querystring.stringify(qs)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let _http = options.protocol === "https:" ? https : http;
|
let _http = options.protocol === "https:" ? https : http;
|
||||||
|
|
||||||
_http.get(options, res => {
|
_http.get(options, res => {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
reject(new Error(`Unexpected status code ${res.statusCode}`));
|
||||||
|
res.resume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
let chunks = [];
|
let chunks = [];
|
||||||
let bytes = 0;
|
let bytes = 0;
|
||||||
res.on("data", chunk => {
|
res.on("data", chunk => {
|
||||||
@@ -69,6 +112,7 @@ function query(resource, filters, limit, callback) {
|
|||||||
|
|
||||||
if (buf.length > 1) {
|
if (buf.length > 1) {
|
||||||
let obj = JSON.parse(buf);
|
let obj = JSON.parse(buf);
|
||||||
|
if (resource === "devices") obj = device.transpose(obj);
|
||||||
if (ret) ret.push(obj);
|
if (ret) ret.push(obj);
|
||||||
else callback(obj);
|
else callback(obj);
|
||||||
}
|
}
|
||||||
@@ -89,6 +133,8 @@ function query(resource, filters, limit, callback) {
|
|||||||
}
|
}
|
||||||
if (buf.length > 1) {
|
if (buf.length > 1) {
|
||||||
let obj = JSON.parse(buf);
|
let obj = JSON.parse(buf);
|
||||||
|
if (resource === "devices") obj = device.transpose(obj);
|
||||||
|
|
||||||
if (ret) ret.push(obj);
|
if (ret) ret.push(obj);
|
||||||
else callback(obj);
|
else callback(obj);
|
||||||
}
|
}
|
||||||
@@ -104,3 +150,4 @@ function query(resource, filters, limit, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exports.query = query;
|
exports.query = query;
|
||||||
|
exports.count = count;
|
||||||
+15
-15
@@ -16,35 +16,35 @@ const resources = {
|
|||||||
|
|
||||||
for (let [resource, update] of Object.entries(resources)) {
|
for (let [resource, update] of Object.entries(resources)) {
|
||||||
router.head(`/${resource}`, async ctx => {
|
router.head(`/${resource}`, async ctx => {
|
||||||
let filters, limit;
|
let filter, limit;
|
||||||
if (ctx.request.query.query)
|
if (ctx.request.query.filter)
|
||||||
if (Array.isArray(ctx.request.query.query))
|
if (Array.isArray(ctx.request.query.filter))
|
||||||
filters = ctx.request.query.query.map(q => JSON.parse(q));
|
filter = ctx.request.query.query.map(q => JSON.parse(q));
|
||||||
else {
|
else {
|
||||||
filters = JSON.parse(ctx.request.query.query);
|
filter = JSON.parse(ctx.request.query.filter);
|
||||||
if (!Array.isArray(filters)) filters = [filters];
|
if (!Array.isArray(filter)) filter = [filter];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.request.query.limit) limit = +ctx.request.query.limit;
|
if (ctx.request.query.limit) limit = +ctx.request.query.limit;
|
||||||
|
|
||||||
let res = await apiFunctions.query(resource, filters, limit);
|
let count = await apiFunctions.count(resource, filter, limit);
|
||||||
ctx.set("X-Total-Count", res.length);
|
ctx.set("X-Total-Count", count);
|
||||||
ctx.body = "";
|
ctx.body = "";
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get(`/${resource}`, async ctx => {
|
router.get(`/${resource}`, async ctx => {
|
||||||
let filters, limit;
|
let filter, limit;
|
||||||
if (ctx.request.query.query)
|
if (ctx.request.query.filter)
|
||||||
if (Array.isArray(ctx.request.query.query))
|
if (Array.isArray(ctx.request.query.filter))
|
||||||
filters = ctx.request.query.query.map(q => JSON.parse(q));
|
filter = ctx.request.query.query.map(q => JSON.parse(q));
|
||||||
else {
|
else {
|
||||||
filters = JSON.parse(ctx.request.query.query);
|
filter = JSON.parse(ctx.request.query.filter);
|
||||||
if (!Array.isArray(filters)) filters = [filters];
|
if (!Array.isArray(filter)) filter = [filter];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.request.query.limit) limit = +ctx.request.query.limit;
|
if (ctx.request.query.limit) limit = +ctx.request.query.limit;
|
||||||
|
|
||||||
ctx.body = await apiFunctions.query(resource, filters, limit);
|
ctx.body = await apiFunctions.query(resource, filter, limit);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.head(`/${resource}/:id`, async (ctx, next) => {
|
router.head(`/${resource}/:id`, async (ctx, next) => {
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
function recursive(input, root, output, timestamp) {
|
||||||
|
for (let [name, tree] of Object.entries(input)) {
|
||||||
|
if (root.length === 0)
|
||||||
|
if (name === "_lastInform") {
|
||||||
|
output["Events.Inform"] = {
|
||||||
|
value: [timestamp, "xsd:dateTime"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
} else if (name === "_registered") {
|
||||||
|
output["Events.Registered"] = {
|
||||||
|
value: [timestamp, "xsd:dateTime"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
} else if (name === "_lastBoot") {
|
||||||
|
output["Events.1_BOOT"] = {
|
||||||
|
value: [timestamp, "xsd:dateTime"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
} else if (name === "_lastBootstrap") {
|
||||||
|
output["Events.0_BOOTSTRAP"] = {
|
||||||
|
value: [timestamp, "xsd:dateTime"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
} else if (name === "_id") {
|
||||||
|
output["DeviceID.ID"] = {
|
||||||
|
value: [tree, "xsd:string"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
} else if (name === "_deviceId") {
|
||||||
|
output["DeviceID.Manufacturer"] = {
|
||||||
|
value: [tree["_Manufacturer"], "xsd:string"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
output["DeviceID.OUI"] = {
|
||||||
|
value: [tree["_OUI"], "xsd:string"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
output["DeviceID.ProductClass"] = {
|
||||||
|
value: [tree["_ProductClass"], "xsd:string"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
output["DeviceID.SerialNumber"] = {
|
||||||
|
value: [tree["_SerialNumber"], "xsd:string"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
} else if (name === "_tags") {
|
||||||
|
output["Tags"] = {
|
||||||
|
writable: true,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: true,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let t of tree)
|
||||||
|
output[`Tags.${t}`] = {
|
||||||
|
value: [true, "xsd:boolean"],
|
||||||
|
valueTimestamp: timestamp,
|
||||||
|
writable: false,
|
||||||
|
writableTimestamp: timestamp,
|
||||||
|
object: false,
|
||||||
|
objectTimestamp: timestamp
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("_")) continue;
|
||||||
|
|
||||||
|
let childrenTimestamp = timestamp;
|
||||||
|
|
||||||
|
if (root.length === 0) childrenTimestamp = input["_timestamp"] || 1;
|
||||||
|
else if (input["_timestamp"] > timestamp)
|
||||||
|
childrenTimestamp = input["_timestamp"];
|
||||||
|
|
||||||
|
const attrs = {};
|
||||||
|
if (tree["_value"]) {
|
||||||
|
attrs["value"] = [tree["_value"], tree["_type"]];
|
||||||
|
attrs["valueTimestamp"] = childrenTimestamp;
|
||||||
|
attrs["object"] = false;
|
||||||
|
attrs["objectTimestamp"] = childrenTimestamp;
|
||||||
|
} else if (tree["_object"]) {
|
||||||
|
attrs["object"] = tree["_object"];
|
||||||
|
attrs["objectTimestamp"] = childrenTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree["_writable"]) {
|
||||||
|
attrs["writable"] = tree["_writable"];
|
||||||
|
attrs["writableTimestamp"] = childrenTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = root.concat(name);
|
||||||
|
output[r.join(".")] = attrs;
|
||||||
|
|
||||||
|
if (attrs["object"]) recursive(tree, r, output, childrenTimestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function transpose(device) {
|
||||||
|
const newDevice = {};
|
||||||
|
const timestamp = new Date(device["_lastInform"] || 1).getTime();
|
||||||
|
recursive(device, [], newDevice, timestamp);
|
||||||
|
return newDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transposeQuery(query) {
|
||||||
|
const newQuery = {};
|
||||||
|
|
||||||
|
for (let [key, val] of Object.entries(query))
|
||||||
|
if (key.startsWith("$")) newQuery[key] = transposeQuery(val);
|
||||||
|
else if (key === "DeviceID.ID") newQuery["_id"] = val;
|
||||||
|
|
||||||
|
return newQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.transpose = transpose;
|
||||||
|
exports.transposeQuery = transposeQuery;
|
||||||
Reference in new issue
Block a user