@ntlab/sipd-tu-bridge-ui 1.4.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/controller/security.js +5 -0
- package/controller/ui.js +37 -18
- package/helper/app.js +2 -0
- package/index.js +41 -31
- package/package.json +4 -5
- package/public/css/app.css +16 -3
- package/views/layout/default.ejs +3 -3
- package/views/slot/mainmenu.ejs +1 -1
- package/views/ui/bridge.ejs +24 -6
- package/views/ui/error.ejs +17 -17
- package/views/ui/index.ejs +21 -8
- package/views/ui/info.ejs +18 -18
- package/views/ui/queue.ejs +40 -9
- package/views/ui/status.ejs +12 -8
- package/views/ui/ticker.ejs +2 -2
- package/views/ui/util.ejs +24 -0
package/controller/security.js
CHANGED
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
const Controller = require('@ntlab/express-controller');
|
|
26
26
|
const Express = require('express').application;
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Security controller provides user authentication mechanism.
|
|
30
|
+
*
|
|
31
|
+
* @author Toha <tohenk@yahoo.com>
|
|
32
|
+
*/
|
|
28
33
|
class SecurityController extends Controller {
|
|
29
34
|
|
|
30
35
|
buildRoutes() {
|
package/controller/ui.js
CHANGED
|
@@ -27,6 +27,11 @@ const path = require('path');
|
|
|
27
27
|
const Controller = require('@ntlab/express-controller');
|
|
28
28
|
const Express = require('express').application;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Ui controller provides user interface interaction.
|
|
32
|
+
*
|
|
33
|
+
* @author Toha <tohenk@yahoo.com>
|
|
34
|
+
*/
|
|
30
35
|
class UiController extends Controller {
|
|
31
36
|
|
|
32
37
|
buildRoutes() {
|
|
@@ -38,6 +43,7 @@ class UiController extends Controller {
|
|
|
38
43
|
bridge.stat = await bridge.getStats();
|
|
39
44
|
bridge.last = await bridge.getLast();
|
|
40
45
|
bridge.current = await bridge.getCurrent();
|
|
46
|
+
bridge.logs = await bridge.getLogFiles();
|
|
41
47
|
}
|
|
42
48
|
const socketOptions = {};
|
|
43
49
|
if (req.app.get('root') !== '/') {
|
|
@@ -109,6 +115,37 @@ class UiController extends Controller {
|
|
|
109
115
|
result.pages = req.app.locals.pager(result.count, result.size, result.page);
|
|
110
116
|
res.json(result);
|
|
111
117
|
});
|
|
118
|
+
this.addRoute('task', 'post', '/task/:op', async (req, res, next) => {
|
|
119
|
+
const result = {
|
|
120
|
+
success: false
|
|
121
|
+
}
|
|
122
|
+
let retval;
|
|
123
|
+
/** @type {import('..').SipdApi} */
|
|
124
|
+
const api = req.app.api;
|
|
125
|
+
switch (req.params.op) {
|
|
126
|
+
case 'log':
|
|
127
|
+
if (req.body.seq) {
|
|
128
|
+
if (req.body.log) {
|
|
129
|
+
retval = await api.query({cmd: 'log-file', log: req.body.log, seq: req.body.seq});
|
|
130
|
+
} else {
|
|
131
|
+
retval = await api.query({cmd: 'log-file', seq: req.body.seq});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
case 'remove':
|
|
136
|
+
if (req.body.queue) {
|
|
137
|
+
retval = await api.query({cmd: 'remove-queue', queue: req.body.queue});
|
|
138
|
+
}
|
|
139
|
+
if (req.body.error) {
|
|
140
|
+
retval = await api.query({cmd: 'clean-err', error: req.body.error});
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case 'restart':
|
|
144
|
+
retval = await api.query({cmd: 'restart'});
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
res.json({...result, ...(retval || {})});
|
|
148
|
+
});
|
|
112
149
|
this.addRoute('about', 'get', '/about', (req, res, next) => {
|
|
113
150
|
let about;
|
|
114
151
|
if (req.app.about) {
|
|
@@ -125,24 +162,6 @@ class UiController extends Controller {
|
|
|
125
162
|
}
|
|
126
163
|
res.json(about);
|
|
127
164
|
});
|
|
128
|
-
this.addRoute('task', 'post', '/task/:op', async (req, res, next) => {
|
|
129
|
-
const result = {
|
|
130
|
-
success: false
|
|
131
|
-
}
|
|
132
|
-
/** @type {import('..').SipdApi} */
|
|
133
|
-
const api = req.app.api;
|
|
134
|
-
switch (req.params.op) {
|
|
135
|
-
case 'remove':
|
|
136
|
-
if (req.body.error) {
|
|
137
|
-
Object.assign(result, await api.query({cmd: 'clean-err', error: req.body.error}));
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
|
-
case 'restart':
|
|
141
|
-
Object.assign(result, await api.query({cmd: 'restart'}));
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
res.json(result);
|
|
145
|
-
});
|
|
146
165
|
}
|
|
147
166
|
|
|
148
167
|
/**
|
package/helper/app.js
CHANGED
package/index.js
CHANGED
|
@@ -22,7 +22,20 @@
|
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const createError = require('http-errors');
|
|
26
|
+
const express = require('express');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
const logger = require('morgan');
|
|
29
|
+
const session = require('express-session');
|
|
30
|
+
const FileStore = require('session-file-store')(session);
|
|
31
|
+
const { Helper, Security, Factory } = require('@ntlab/express-middleware');
|
|
32
|
+
const { ScriptManager, ScriptAsset } = require('@ntlab/ntjs');
|
|
33
|
+
const { Assets, CDN } = require('@ntlab/ntjs-assets');
|
|
34
|
+
|
|
35
|
+
// register script repository
|
|
36
|
+
require('@ntlab/ntjs-repo')();
|
|
37
|
+
|
|
38
|
+
/* --- BEGIN API V1 --- */
|
|
26
39
|
|
|
27
40
|
/**
|
|
28
41
|
* SIPD Penatausahaan Bridge main application.
|
|
@@ -35,10 +48,10 @@
|
|
|
35
48
|
* @property {object} config Configuration
|
|
36
49
|
* @property {SipdBridge[]} bridges Bridges
|
|
37
50
|
* @property {AuthenticateFunction} authenticate Perform usename and password authentication
|
|
38
|
-
* @property {
|
|
39
|
-
* @property {
|
|
40
|
-
* @property {
|
|
41
|
-
* @property {
|
|
51
|
+
* @property {PagedObjectsFunction} getQueues Get queues
|
|
52
|
+
* @property {ActivityFunction} getActivity Get activity logs
|
|
53
|
+
* @property {ObjectFunction} getCount Get activity count
|
|
54
|
+
* @property {PagedObjectsFunction} getErrors Get captured errors
|
|
42
55
|
* @property {QueryFunction} query Perform API query
|
|
43
56
|
*/
|
|
44
57
|
|
|
@@ -48,10 +61,11 @@
|
|
|
48
61
|
* @typedef {Object} SipdBridge
|
|
49
62
|
* @property {string} name Name
|
|
50
63
|
* @property {number} year Year
|
|
51
|
-
* @property {
|
|
52
|
-
* @property {
|
|
53
|
-
* @property {
|
|
54
|
-
* @property {
|
|
64
|
+
* @property {ObjectFunction} getStats Get bridge stats
|
|
65
|
+
* @property {ActivityFunction} getLogs Get bridge logs
|
|
66
|
+
* @property {LogFilesFunction} getLogFiles Get bridge addiitonal log files
|
|
67
|
+
* @property {ObjectFunction} getLast Get last queue
|
|
68
|
+
* @property {ObjectFunction} getCurrent Get current processing queue
|
|
55
69
|
*/
|
|
56
70
|
|
|
57
71
|
/**
|
|
@@ -74,50 +88,46 @@
|
|
|
74
88
|
*/
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
|
-
*
|
|
91
|
+
* Get objects at specified page with size of limit. If none specified
|
|
92
|
+
* it returns first page with default size limit (either 10 or 25).
|
|
78
93
|
*
|
|
79
|
-
* @callback
|
|
80
|
-
* @param {number} page Page number
|
|
81
|
-
* @param {number} size Page size
|
|
94
|
+
* @callback PagedObjectsFunction
|
|
95
|
+
* @param {?number} page Page number
|
|
96
|
+
* @param {?number} size Page size
|
|
82
97
|
* @returns {Promise<object[]>}
|
|
83
98
|
*/
|
|
84
99
|
|
|
85
100
|
/**
|
|
86
|
-
*
|
|
101
|
+
* Get miscellanous object such as queue or log.
|
|
87
102
|
*
|
|
88
|
-
* @callback
|
|
103
|
+
* @callback ObjectFunction
|
|
89
104
|
* @returns {Promise<object>}
|
|
90
105
|
*/
|
|
91
106
|
|
|
92
107
|
/**
|
|
93
|
-
*
|
|
108
|
+
* Get string content such as activity logs.
|
|
94
109
|
*
|
|
95
|
-
* @callback
|
|
110
|
+
* @callback ActivityFunction
|
|
111
|
+
* @param {?string} seq Sequence number
|
|
96
112
|
* @returns {Promise<string>}
|
|
97
113
|
*/
|
|
98
114
|
|
|
99
115
|
/**
|
|
100
|
-
*
|
|
116
|
+
* Query api and return result object.
|
|
101
117
|
*
|
|
102
118
|
* @callback QueryFunction
|
|
103
119
|
* @param {object} data Query data
|
|
104
120
|
* @returns {Promise<object>}
|
|
105
121
|
*/
|
|
106
122
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const session = require('express-session');
|
|
114
|
-
const FileStore = require('session-file-store')(session);
|
|
115
|
-
const { Helper, Security, Factory } = require('@ntlab/express-middleware');
|
|
116
|
-
const { ScriptManager, ScriptAsset } = require('@ntlab/ntjs');
|
|
117
|
-
const { Assets, CDN } = require('@ntlab/ntjs-assets');
|
|
123
|
+
/**
|
|
124
|
+
* Get additional log files.
|
|
125
|
+
*
|
|
126
|
+
* @callback LogFilesFunction
|
|
127
|
+
* @returns {Promise<[{name: string, seq: string, time: number}]>}
|
|
128
|
+
*/
|
|
118
129
|
|
|
119
|
-
|
|
120
|
-
require('@ntlab/ntjs-repo')();
|
|
130
|
+
/* --- END API --- */
|
|
121
131
|
|
|
122
132
|
/**
|
|
123
133
|
* Express application.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ntlab/sipd-tu-bridge-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "SIPD Penatausahaan Bridge Web Interface",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -31,17 +31,16 @@
|
|
|
31
31
|
},
|
|
32
32
|
"homepage": "https://github.com/tohenk/node-sipd-tu-bridge-ui#readme",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ntlab/express-controller": "^1.
|
|
35
|
-
"@ntlab/express-middleware": "^2.
|
|
34
|
+
"@ntlab/express-controller": "^1.3.0",
|
|
35
|
+
"@ntlab/express-middleware": "^2.6.0",
|
|
36
36
|
"@ntlab/ntjs": "^3.1.0",
|
|
37
|
-
"@ntlab/ntjs-assets": "^2.
|
|
37
|
+
"@ntlab/ntjs-assets": "^2.144.0",
|
|
38
38
|
"@ntlab/ntjs-repo": "^3.1.0",
|
|
39
39
|
"@ntlab/ntlib": "^2.9.1",
|
|
40
40
|
"ejs": "^3.1.10",
|
|
41
41
|
"express": "^5.2.1",
|
|
42
42
|
"express-session": "^1.19.0",
|
|
43
43
|
"http-errors": "^2.0.1",
|
|
44
|
-
"moment": "^2.30.1",
|
|
45
44
|
"morgan": "^1.10.1",
|
|
46
45
|
"session-file-store": "^1.5.0",
|
|
47
46
|
"terser": "^5.46.0"
|
package/public/css/app.css
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--viewport-h-limit: 70vh;
|
|
3
|
+
--viewport-w-limit: 70vw;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
body.with-menu {
|
|
2
7
|
padding-top: 5em;
|
|
3
8
|
}
|
|
@@ -15,6 +20,14 @@ img.logo {
|
|
|
15
20
|
margin-right: 1em !important;
|
|
16
21
|
}
|
|
17
22
|
|
|
23
|
+
.v-max {
|
|
24
|
+
max-height: var(--viewport-h-limit) !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.v-min {
|
|
28
|
+
min-height: var(--viewport-h-limit) !important;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
@media only screen and (min-width: 1200px) {
|
|
19
32
|
.ui.container.queue-name {
|
|
20
33
|
max-width: 20em;
|
|
@@ -47,10 +60,10 @@ img.logo {
|
|
|
47
60
|
|
|
48
61
|
@media only screen and (min-width: 768px) and (max-width: 991.98px) {
|
|
49
62
|
.ui.container.queue-name {
|
|
50
|
-
max-width:
|
|
63
|
+
max-width: 8em;
|
|
51
64
|
}
|
|
52
65
|
.ui.container.queue-result {
|
|
53
|
-
max-width:
|
|
66
|
+
max-width: 10em;
|
|
54
67
|
}
|
|
55
68
|
.ui.container.err-message {
|
|
56
69
|
max-width: 12.5em;
|
|
@@ -65,7 +78,7 @@ img.logo {
|
|
|
65
78
|
.ui.container.queue-result,
|
|
66
79
|
.ui.container.err-message,
|
|
67
80
|
.ui.container.err-data {
|
|
68
|
-
max-width:
|
|
81
|
+
max-width: var(--viewport-w-limit);
|
|
69
82
|
margin-left: 0 !important;
|
|
70
83
|
margin-right: 0 !important;
|
|
71
84
|
}
|
package/views/layout/default.ejs
CHANGED
|
@@ -15,17 +15,17 @@
|
|
|
15
15
|
<body<% if (mainmenu) { %> class="with-menu"<% } %>>
|
|
16
16
|
<%_ if (mainmenu) { -%>
|
|
17
17
|
<section class="ui fixed inverted menu">
|
|
18
|
-
|
|
18
|
+
<%- indent(mainmenu, 4) -%>
|
|
19
19
|
</section>
|
|
20
20
|
<%_ } -%>
|
|
21
21
|
<main class="ui container">
|
|
22
22
|
<%_ if (title.length) { -%>
|
|
23
23
|
<h1 class="ui dividing header"><%= title %></h1>
|
|
24
24
|
<%_ } -%>
|
|
25
|
-
<%- content -%>
|
|
25
|
+
<%- indent(content, 4) -%>
|
|
26
26
|
</main>
|
|
27
27
|
<%_ if ((addons = slot('addons')) && (addons = include(addons))) { -%>
|
|
28
|
-
<%- addons -%>
|
|
28
|
+
<%- indent(addons, 2) -%>
|
|
29
29
|
<%_ } -%>
|
|
30
30
|
<%_ javascripts().forEach(js => { -%>
|
|
31
31
|
<script type="text/javascript" src="<%- path(js) %>"></script>
|
package/views/slot/mainmenu.ejs
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
} -%>
|
|
35
|
-
<%- menu(menus, {mainmenu: true
|
|
35
|
+
<%- menu(menus, {mainmenu: true}) %>
|
|
36
36
|
<%_ script.create('JQuery')
|
|
37
37
|
.useDependencies(['JQuery/Util', 'SemanticUI/Notification', 'SemanticUI/Dialog/Message', 'SemanticUI/Dialog/Confirm'])
|
|
38
38
|
.add(`
|
package/views/ui/bridge.ejs
CHANGED
|
@@ -11,13 +11,30 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
<div class="twelve wide column">
|
|
13
13
|
<%_ bridges.forEach(bridge => { -%>
|
|
14
|
-
<div class="ui form bridge <%= bridge.name %>" style="display: none;">
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
<div class="ui form bridge <%= bridge.name %>" data-bridge="<%= bridge.name %>" style="display: none;">
|
|
15
|
+
<div class="ui two column stackable grid">
|
|
16
|
+
<div class="column">
|
|
17
|
+
<%_ Object.keys(bridge.stat).forEach(stat => { -%>
|
|
18
|
+
<div class="two fields">
|
|
19
|
+
<div class="field"><label><%= _(bridge.stat[stat].label) %></label></div>
|
|
20
|
+
<div class="field"><input type="text" value="<%- bridge.stat[stat].value %>" data-key="<%= stat %>" readonly></div>
|
|
21
|
+
</div>
|
|
22
|
+
<%_ }) -%>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="column">
|
|
25
|
+
<div class="two fields">
|
|
26
|
+
<div class="field"><label><%= _('Archived logs') %></label></div>
|
|
27
|
+
<div class="field">
|
|
28
|
+
<select class="ui dropdown" data-log="<%= bridge.name %>" data-title="<%= _('Bridge <span class="ui info text">%NAME%</span> log %LOG%') %>">
|
|
29
|
+
<option value=""><%= _('- Select log -') %></option>
|
|
30
|
+
<%_ bridge.logs.forEach(log => { -%>
|
|
31
|
+
<option value="<%= log.seq %>"><%= log.name %></option>
|
|
32
|
+
<%_ }) -%>
|
|
33
|
+
</select>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
19
37
|
</div>
|
|
20
|
-
<%_ }) -%>
|
|
21
38
|
<div class="field">
|
|
22
39
|
<label><%= _('Logs') %></label>
|
|
23
40
|
<textarea class="log <%= bridge.name %>" rows="20" readonly></textarea>
|
|
@@ -39,4 +56,5 @@ $('a[data-bridge]').on('click', function(e) {
|
|
|
39
56
|
$(\`.ui.form.bridge\`).hide();
|
|
40
57
|
$(\`.ui.form.bridge.\${a.data('bridge')}\`).show();
|
|
41
58
|
}).filter(':first').click();
|
|
59
|
+
$('.ui.dropdown').dropdown();
|
|
42
60
|
`) -%>
|
package/views/ui/error.ejs
CHANGED
|
@@ -39,21 +39,21 @@ $.error = $.loader($('div[data-tab="error"] table'), {
|
|
|
39
39
|
});
|
|
40
40
|
$.error.toRow = function(data) {
|
|
41
41
|
return $(\`
|
|
42
|
-
<tr><td>\${data.nr}</td>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
</tr>\`);
|
|
42
|
+
<tr><td>\${data.nr}</td>
|
|
43
|
+
<td>\${this.toImg($.toStr(data.image), data.filename)}</td>
|
|
44
|
+
<td>\${this.toPayload(data.error, '${_('Error Message')}')}</td>
|
|
45
|
+
<td>\${this.toPayload(data.data, '${_('Error Data')}')}</td>
|
|
46
|
+
<td>
|
|
47
|
+
<a href="#" class="err-clicker" data-op="delete" data-filename="\${data.filename}" role="button"><i class="trash alternate outline red icon"></i></a>
|
|
48
|
+
</td>
|
|
49
|
+
</tr>\`);
|
|
50
50
|
}
|
|
51
51
|
$.error.toImg = function(data, alt) {
|
|
52
52
|
if (data) {
|
|
53
53
|
return \`
|
|
54
|
-
<a href="#" class="err-clicker" data-op="view" data-title="\${alt}" data-tooltip="\${alt}" data-position="right center">
|
|
55
|
-
|
|
56
|
-
</a>\`;
|
|
54
|
+
<a href="#" class="err-clicker" data-op="view" data-title="\${alt}" data-tooltip="\${alt}" data-position="right center">
|
|
55
|
+
<img class="ui medium rounded bordered image" src="\${data}" alt="\${alt}">
|
|
56
|
+
</a>\`;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
$.error.toPayload = function(data, title) {
|
|
@@ -79,10 +79,10 @@ $.error.toPayload = function(data, title) {
|
|
|
79
79
|
excerpt += '…';
|
|
80
80
|
}
|
|
81
81
|
return \`
|
|
82
|
-
<a href="#" class="err-clicker" data-op="view" data-title="\${title}">
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
</a>\`;
|
|
82
|
+
<a href="#" class="err-clicker" data-op="view" data-title="\${title}">
|
|
83
|
+
<span>\${excerpt}</span>
|
|
84
|
+
<pre style="display: none;">\${payload}</pre>
|
|
85
|
+
</a>\`;
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
$.error.handle = function(el) {
|
|
@@ -92,7 +92,7 @@ $.error.handle = function(el) {
|
|
|
92
92
|
let content, size = 'large';
|
|
93
93
|
const img = el.find('img'), pre = el.find('pre');
|
|
94
94
|
if (img.length) {
|
|
95
|
-
content = \`<img class="ui fluid image" src="\${img.attr('src')}"
|
|
95
|
+
content = \`<img class="ui fluid image v-max" src="\${img.attr('src')}">\`;
|
|
96
96
|
size = 'fullscreen';
|
|
97
97
|
}
|
|
98
98
|
if (pre.length) {
|
|
@@ -113,7 +113,7 @@ $.error.handle = function(el) {
|
|
|
113
113
|
$.ntdlg.confirm(
|
|
114
114
|
'err-delete-confirm-dlg',
|
|
115
115
|
'${_('Confirm')}',
|
|
116
|
-
'${_('Are you sure want to remove error <
|
|
116
|
+
'${_('Are you sure want to remove error <span class="ui error text">%ERR%</span>?')}'.replace(/%ERR%/g, el.data('filename')),
|
|
117
117
|
$.ntdlg.ICON_QUESTION,
|
|
118
118
|
function() {
|
|
119
119
|
$.post('${route('Ui', {name: 'task', op: 'remove'})}', {error: el.data('filename')})
|
package/views/ui/index.ejs
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
<div class="ui top attached stackable tabular menu">
|
|
2
2
|
<a class="item" data-tab="status"><%= _('Status') %></a>
|
|
3
3
|
<a class="item" data-tab="activity"><%= _('Activity') %></a>
|
|
4
|
-
<a class="item" data-tab="queue"><%= _('Queue') %> <span class="ui hidden tiny label"></span></a>
|
|
4
|
+
<a class="item" data-tab="queue"><%= _('Queue') %> <span class="ui hidden blue tiny label"></span></a>
|
|
5
5
|
<%_ if (api.bridges.length) { -%>
|
|
6
|
-
<a class="item" data-tab="bridge"><%= _('Bridge') %> <span class="ui tiny label"><%= api.bridges.length %></span></a>
|
|
6
|
+
<a class="item" data-tab="bridge"><%= _('Bridge') %> <span class="ui green tiny label"><%= api.bridges.length %></span></a>
|
|
7
7
|
<%_ } -%>
|
|
8
|
-
<a class="item" data-tab="error"><%= _('Error') %> <span class="ui hidden tiny label"></span></a>
|
|
8
|
+
<a class="item" data-tab="error"><%= _('Error') %> <span class="ui hidden red tiny label"></span></a>
|
|
9
9
|
</div>
|
|
10
10
|
<div class="ui bottom attached tab segment" data-tab="status">
|
|
11
|
-
<%- include('status') -%>
|
|
11
|
+
<%- indent(include('status'), 2) -%>
|
|
12
12
|
</div>
|
|
13
13
|
<div class="ui bottom attached tab segment" data-tab="activity">
|
|
14
|
-
<%- include('activity') -%>
|
|
14
|
+
<%- indent(include('activity'), 2) -%>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="ui bottom attached tab segment" data-tab="queue">
|
|
17
|
-
<%- include('queue') -%>
|
|
17
|
+
<%- indent(include('queue'), 2) -%>
|
|
18
18
|
</div>
|
|
19
19
|
<%- include('bridgehandler') -%>
|
|
20
20
|
<%_ if (api.bridges.length) { -%>
|
|
21
21
|
<div class="ui bottom attached tab segment" data-tab="bridge">
|
|
22
|
-
<%- include('bridge') -%>
|
|
22
|
+
<%- indent(include('bridge'), 2) -%>
|
|
23
23
|
</div>
|
|
24
24
|
<%_ } -%>
|
|
25
25
|
<div class="ui bottom attached tab segment" data-tab="error">
|
|
26
|
-
<%- include('error') -%>
|
|
26
|
+
<%- indent(include('error'), 2) -%>
|
|
27
27
|
</div>
|
|
28
28
|
<%- include('ticker') -%>
|
|
29
29
|
<%- include('util') -%>
|
|
@@ -84,5 +84,18 @@ $('.menu [data-tab]').tab({
|
|
|
84
84
|
window.location.hash = path;
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
|
+
$('select[data-log]').on('change', function(e) {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
const el = $(this);
|
|
90
|
+
const seq = el.val();
|
|
91
|
+
if (seq !== '') {
|
|
92
|
+
const log = el.data('log');
|
|
93
|
+
const logname = el.find(\`option[value="\${seq}"]\`).text();
|
|
94
|
+
const title = el.data('title')
|
|
95
|
+
.replace(/%LOG%/g, logname)
|
|
96
|
+
.replace(/%NAME%/g, log?.toUpperCase());
|
|
97
|
+
$.viewLog(title, log, seq);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
87
100
|
$.uiCon.init();
|
|
88
101
|
`) -%>
|
package/views/ui/info.ejs
CHANGED
|
@@ -3,27 +3,27 @@
|
|
|
3
3
|
<div class="item header"><i class="ui info circle icon"></i><%- _('General Information') %></div>
|
|
4
4
|
</div>
|
|
5
5
|
<div class="extra content">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
</div>
|
|
13
|
-
<div class="fields">
|
|
14
|
-
<div class="eight wide field"><label><%= _('Mode:') %></label></div>
|
|
15
|
-
<div class="eight wide field"><label><%= api.mode %></label></div>
|
|
16
|
-
</div>
|
|
6
|
+
<div class="ui form">
|
|
7
|
+
<div class="ui two column stackable grid">
|
|
8
|
+
<div class="column">
|
|
9
|
+
<div class="fields">
|
|
10
|
+
<div class="eight wide field"><label><%= _('Protocol:') %></label></div>
|
|
11
|
+
<div class="eight wide field"><label><%= api.proto %></label></div>
|
|
17
12
|
</div>
|
|
18
|
-
<div class="
|
|
19
|
-
|
|
20
|
-
<div class="
|
|
21
|
-
<div class="eight wide field"><label><%= _(api.counter[counter].label) %></label></div>
|
|
22
|
-
<div class="eight wide field"><label data-counter="<%= counter %>"><%= _(api.counter[counter].value) %></label></div>
|
|
23
|
-
</div>
|
|
24
|
-
<%_ }) -%>
|
|
13
|
+
<div class="fields">
|
|
14
|
+
<div class="eight wide field"><label><%= _('Mode:') %></label></div>
|
|
15
|
+
<div class="eight wide field"><label><%= api.mode %></label></div>
|
|
25
16
|
</div>
|
|
26
17
|
</div>
|
|
18
|
+
<div class="column">
|
|
19
|
+
<%_ Object.keys(api.counter).forEach(counter => { -%>
|
|
20
|
+
<div class="fields">
|
|
21
|
+
<div class="eight wide field"><label><%= _(api.counter[counter].label) %></label></div>
|
|
22
|
+
<div class="eight wide field"><label data-counter="<%= counter %>"><%= _(api.counter[counter].value) %></label></div>
|
|
23
|
+
</div>
|
|
24
|
+
<%_ }) -%>
|
|
25
|
+
</div>
|
|
27
26
|
</div>
|
|
27
|
+
</div>
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
package/views/ui/queue.ejs
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
<th><%= _('Status') %></th>
|
|
10
10
|
<th><%= _('Result') %></th>
|
|
11
11
|
<th><%= _('Time') %></th>
|
|
12
|
+
<th><%= _('Action') %></th>
|
|
12
13
|
</tr>
|
|
13
14
|
</thead>
|
|
14
15
|
</table>
|
|
15
16
|
<%_ script.create('JQuery')
|
|
16
|
-
.useDependencies('SemanticUI/Loader')
|
|
17
|
+
.useDependencies(['SemanticUI/Loader', 'SemanticUI/Dialog/Confirm'])
|
|
17
18
|
.add(`
|
|
18
19
|
$.queue = $.loader($('div[data-tab="queue"] table'), {
|
|
19
20
|
url: '${route('Ui', {name: 'queue', page: 'PAGE'})}',
|
|
@@ -30,6 +31,11 @@ $.queue = $.loader($('div[data-tab="queue"] table'), {
|
|
|
30
31
|
counter.addClass('hidden');
|
|
31
32
|
}
|
|
32
33
|
}
|
|
34
|
+
$('[data-status!="processing"] [data-op="delete"]').hide();
|
|
35
|
+
$('a.queue-clicker').on('click', function(e) {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
self.handle($(this));
|
|
38
|
+
});
|
|
33
39
|
if (self.loading) {
|
|
34
40
|
self.loading = false;
|
|
35
41
|
}
|
|
@@ -37,14 +43,17 @@ $.queue = $.loader($('div[data-tab="queue"] table'), {
|
|
|
37
43
|
});
|
|
38
44
|
$.queue.toRow = function(data) {
|
|
39
45
|
return $(\`
|
|
40
|
-
<tr><td>\${data.nr}</td>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
<tr data-status="\${data.status}"><td>\${data.nr}</td>
|
|
47
|
+
<td>\${$.toStr(data.id)}</td>
|
|
48
|
+
<td>\${$.toStr(data.type)}</td>
|
|
49
|
+
<td><div class="ui scrolling container queue-name">\${$.hidePayload($.toStr(data.name))}</div></td>
|
|
50
|
+
<td>\${this.toStatus(data.status)}</td>
|
|
51
|
+
<td><div class="ui scrolling container queue-result">\${$.hidePayload($.toStr(data.result))}</div></td>
|
|
52
|
+
<td>\${$.toStr(data.time)}</td>
|
|
53
|
+
<td>
|
|
54
|
+
<a href="#" class="queue-clicker" data-op="delete" data-queue="\${data.id}" role="button"><i class="trash alternate outline red icon"></i></a>
|
|
55
|
+
</td>
|
|
56
|
+
</tr>\`);
|
|
48
57
|
}
|
|
49
58
|
$.queue.toStatus = function(data) {
|
|
50
59
|
const icon = {
|
|
@@ -58,6 +67,28 @@ $.queue.toStatus = function(data) {
|
|
|
58
67
|
return icon ? \`<div data-tooltip="\${data}" data-position="right center"><i class="\${icon} icon"></i></div>\` :
|
|
59
68
|
$.toStr(data);
|
|
60
69
|
}
|
|
70
|
+
$.queue.handle = function(el) {
|
|
71
|
+
const self = this;
|
|
72
|
+
switch (el.data('op')) {
|
|
73
|
+
case 'delete':
|
|
74
|
+
$.ntdlg.confirm(
|
|
75
|
+
'queue-delete-confirm-dlg',
|
|
76
|
+
'${_('Confirm')}',
|
|
77
|
+
'${_('Are you sure want to remove queue <span class="ui error text">%QUEUE%</span>?')}'.replace(/%QUEUE%/g, el.data('queue')),
|
|
78
|
+
$.ntdlg.ICON_QUESTION,
|
|
79
|
+
function() {
|
|
80
|
+
$.post('${route('Ui', {name: 'task', op: 'remove'})}', {queue: el.data('queue')})
|
|
81
|
+
.done(function(json) {
|
|
82
|
+
$.tasks.notify(json);
|
|
83
|
+
if (json.success) {
|
|
84
|
+
self.reload();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
61
92
|
$.queue.reload = function() {
|
|
62
93
|
const self = this;
|
|
63
94
|
if (!self.loading) {
|
package/views/ui/status.ejs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="row"></div>
|
|
3
3
|
<div class="row">
|
|
4
4
|
<div class="twelve wide column">
|
|
5
|
-
<%- include('info') -%>
|
|
5
|
+
<%- indent(include('info'), 6) -%>
|
|
6
6
|
</div>
|
|
7
7
|
</div>
|
|
8
8
|
<%_ if (api.bridges.length) { -%>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<div class="twelve wide column">
|
|
19
19
|
<div class="ui fluid card">
|
|
20
20
|
<div class="content">
|
|
21
|
-
<div class="ui right floated tiny label"><%= bridge.year %></div>
|
|
21
|
+
<div class="ui red right floated tiny label"><%= bridge.year %></div>
|
|
22
22
|
<div class="item header"><i class="ui laptop code icon"></i><%= bridge.name.toUpperCase() %></div>
|
|
23
23
|
</div>
|
|
24
24
|
<div class="extra content" data-bridge="<%= bridge.name %>">
|
|
@@ -71,12 +71,16 @@
|
|
|
71
71
|
</div>
|
|
72
72
|
<%_ }) -%>
|
|
73
73
|
<%_ } else { -%>
|
|
74
|
-
<div class="
|
|
75
|
-
<div class="
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
<div class="row">
|
|
75
|
+
<div class="twelve wide column">
|
|
76
|
+
<div class="ui placeholder segment v-min">
|
|
77
|
+
<div class="ui icon header">
|
|
78
|
+
<i class="laptop code icon"></i>
|
|
79
|
+
<div class="ui big hidden divider"></div>
|
|
80
|
+
<%= _('Currently, no configured bridge available.') %><br/>
|
|
81
|
+
<%= _('Once a bridge is configured, it will appear here.') %>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
80
84
|
</div>
|
|
81
85
|
</div>
|
|
82
86
|
<%_ } -%>
|
package/views/ui/ticker.ejs
CHANGED
|
@@ -35,11 +35,11 @@ $.ticker = {
|
|
|
35
35
|
'restart-timer-dlg',
|
|
36
36
|
'${_('Please Wait')}',
|
|
37
37
|
\`<div class="ui grid container">
|
|
38
|
-
|
|
38
|
+
<div class="row">
|
|
39
39
|
<span class="ui large red text"><i class="clock outline icon"></i><span class="elapsed">00:00:00</span></span>
|
|
40
40
|
<span> </span>
|
|
41
41
|
<span class="ui large text">\${message}</span>
|
|
42
|
-
|
|
42
|
+
</div>
|
|
43
43
|
</div>\`,
|
|
44
44
|
{
|
|
45
45
|
size: 'small',
|
package/views/ui/util.ejs
CHANGED
|
@@ -38,6 +38,30 @@ $.addLog = function(el, message) {
|
|
|
38
38
|
}
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
|
+
$.viewLog = function(title, log, seq) {
|
|
42
|
+
$.post('${route('Ui', {name: 'task', op: 'log'})}', {log, seq})
|
|
43
|
+
.done(function(json) {
|
|
44
|
+
if (json.success) {
|
|
45
|
+
const logs = document.createTextNode(json.logs);
|
|
46
|
+
const content = \`<div class="ui fluid scrolling container v-min v-max"><pre></pre></div>\`;
|
|
47
|
+
const dlg = $.ntdlg.create('log-view-dlg', title, content, {
|
|
48
|
+
size: 'fullscreen',
|
|
49
|
+
show() {
|
|
50
|
+
dlg.find('pre').append(logs);
|
|
51
|
+
},
|
|
52
|
+
buttons: {
|
|
53
|
+
okay: {
|
|
54
|
+
type: 'green approve',
|
|
55
|
+
caption: '<i class="check icon"></i>${_('Ok')}',
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
$.ntdlg.show(dlg);
|
|
60
|
+
} else {
|
|
61
|
+
$.tasks.notify(json);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
41
65
|
$.hidePayload = function(message) {
|
|
42
66
|
if (message) {
|
|
43
67
|
message = message.replace(
|