Open node in form and map.
This commit is contained in:
parent
5b9d2e615b
commit
03083b819b
12 changed files with 80 additions and 19 deletions
|
|
@ -43,6 +43,7 @@
|
|||
<script src="js/app.js"></script>
|
||||
<script src="js/validation/constraints.js"></script>
|
||||
<script src="js/views/taskActionButton.js"></script>
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<script src="/config.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
196
admin/js/main.js
Normal file
196
admin/js/main.js
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, RestangularProvider, Constraints, config) {
|
||||
RestangularProvider.addFullRequestInterceptor(function(element, operation, what, url, headers, params) {
|
||||
if (operation === 'getList') {
|
||||
if (params._filters) {
|
||||
// flatten filter query params
|
||||
|
||||
for (var filter in params._filters) {
|
||||
params[filter] = params._filters[filter];
|
||||
}
|
||||
delete params._filters;
|
||||
}
|
||||
}
|
||||
return { params: params };
|
||||
});
|
||||
|
||||
function formatMoment(unix) {
|
||||
return unix ? moment.unix(unix).fromNow() : 'N/A';
|
||||
}
|
||||
|
||||
function nodeConstraint(field) {
|
||||
var constraint = Constraints.node[field];
|
||||
var result = {
|
||||
required: !constraint.optional
|
||||
};
|
||||
if (constraint.type === 'string') {
|
||||
result.pattern = constraint.regex;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var nga = NgAdminConfigurationProvider;
|
||||
var admin = nga.application('Knotenverwaltung - Admin-Panel');
|
||||
|
||||
admin
|
||||
.baseApiUrl('/internal/api/')
|
||||
.debug(true);
|
||||
|
||||
function nodeClasses(node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var nodes = nga.entity('nodes').label('Nodes').identifier(nga.field('token'));
|
||||
nodes
|
||||
.listView()
|
||||
.title('Nodes')
|
||||
.perPage(30)
|
||||
.sortDir('ASC')
|
||||
.sortField('hostname')
|
||||
.actions([])
|
||||
.batchActions([])
|
||||
.exportFields([])
|
||||
.fields([
|
||||
nga.field('hostname').cssClasses(nodeClasses),
|
||||
nga.field('nickname').cssClasses(nodeClasses),
|
||||
nga.field('email').cssClasses(nodeClasses),
|
||||
nga.field('token').cssClasses(nodeClasses),
|
||||
nga.field('mac').cssClasses(nodeClasses),
|
||||
nga.field('key').label('VPN').cssClasses(nodeClasses).template(function (node) {
|
||||
return node.values.key
|
||||
? '<i class="fa fa-lock vpn-key-set" aria-hidden="true" title="VPN key set"></i>'
|
||||
: '<i class="fa fa-times vpn-key-unset" aria-hidden="true" title="no VPN key"></i>';
|
||||
}),
|
||||
nga.field('coords').label('GPS').cssClasses(nodeClasses).template(function (node) {
|
||||
return node.values.coords
|
||||
? '<i class="fa fa-map-marker coords-set" aria-hidden="true" title="coordinates set"></i>'
|
||||
: '<i class="fa fa-times coords-unset" aria-hidden="true" title="no coordinates"></i>';
|
||||
}),
|
||||
nga.field('monitoringState').cssClasses(nodeClasses).template(function (node) {
|
||||
switch (node.values.monitoringState) {
|
||||
case 'active':
|
||||
return '<i class="fa fa-check monitoring-active" title="active"></i>';
|
||||
|
||||
case 'pending':
|
||||
return '<i class="fa fa-envelope monitoring-confirmation-pending" title="confirmation pending"></i>';
|
||||
|
||||
default:
|
||||
return '<i class="fa fa-times monitoring-disabled" title="disabled"></i>';
|
||||
}
|
||||
})
|
||||
])
|
||||
.filters([
|
||||
nga.field('q')
|
||||
.label('')
|
||||
.pinned(true)
|
||||
.template(
|
||||
'<div class="input-group">' +
|
||||
'<input type="text" ng-model="value" placeholder="Search" class="form-control"></input>' +
|
||||
'<span class="input-group-addon"><i class="fa fa-search"></i></span></div>'),
|
||||
])
|
||||
.listActions(
|
||||
'<ma-edit-button entry="entry" entity="entity" size="sm"></ma-edit-button> ' +
|
||||
'<ma-delete-button entry="entry" entity="entity" size="sm"></ma-delete-button> ' +
|
||||
'<form style="display: inline-block" action="/#!/update" method="POST" target="_blank">' +
|
||||
'<input type="hidden" name="token" value="{{entry.values.token}}"/>' +
|
||||
'<button class="btn btn-primary btn-sm" type="submit"><i class="fa fa-external-link"></i> Open</button>' +
|
||||
'</form> ' +
|
||||
'<a class="btn btn-success btn-sm" href="' + config.map.mapUrl +
|
||||
'/#!v:m;n:{{entry.values.mapId}}" target="_blank"><i class="fa fa-map-o"></i> Map</a>'
|
||||
)
|
||||
;
|
||||
|
||||
nodes
|
||||
.editionView()
|
||||
.title('Edit node')
|
||||
.actions(['list', 'delete'])
|
||||
.addField(nga.field('token').editable(false))
|
||||
.addField(nga.field('hostname').label('Name').validation(nodeConstraint('hostname')))
|
||||
.addField(nga.field('key').label('Key').validation(nodeConstraint('key')))
|
||||
.addField(nga.field('mac').label('MAC').validation(nodeConstraint('mac')))
|
||||
.addField(nga.field('coords').label('GPS').validation(nodeConstraint('coords')))
|
||||
.addField(nga.field('nickname').validation(nodeConstraint('nickname')))
|
||||
.addField(nga.field('email').validation(nodeConstraint('email')))
|
||||
.addField(nga.field('monitoring', 'boolean').validation(nodeConstraint('monitoring')))
|
||||
.addField(nga.field('monitoringConfirmed').label('Monitoring confirmation').editable(false).map(
|
||||
function (monitoringConfirmed, node) {
|
||||
if (!node.monitoring) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
return monitoringConfirmed ? 'confirmed' : 'pending';
|
||||
}
|
||||
))
|
||||
;
|
||||
|
||||
admin.addEntity(nodes);
|
||||
|
||||
function taskClasses(task) {
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
return task.values.enabled ? 'task-enabled' : 'task-disabled';
|
||||
}
|
||||
|
||||
var tasks = nga.entity('tasks').label('Background-Jobs');
|
||||
tasks
|
||||
.listView()
|
||||
.title('Background-Jobs')
|
||||
.perPage(30)
|
||||
.sortDir('ASC')
|
||||
.sortField('id')
|
||||
.actions([])
|
||||
.batchActions([])
|
||||
.exportFields([])
|
||||
.fields([
|
||||
nga.field('id').cssClasses(taskClasses),
|
||||
nga.field('name').cssClasses(taskClasses),
|
||||
nga.field('schedule').cssClasses(taskClasses),
|
||||
nga.field('state').cssClasses(taskClasses),
|
||||
nga.field('runningSince').map(formatMoment).cssClasses(taskClasses),
|
||||
nga.field('lastRunStarted').map(formatMoment).cssClasses(taskClasses)
|
||||
])
|
||||
.filters([
|
||||
nga.field('q')
|
||||
.label('')
|
||||
.pinned(true)
|
||||
.template(
|
||||
'<div class="input-group">' +
|
||||
'<input type="text" ng-model="value" placeholder="Search" class="form-control"></input>' +
|
||||
'<span class="input-group-addon"><i class="fa fa-search"></i></span></div>'),
|
||||
])
|
||||
.listActions(
|
||||
'<fa-task-action-button action="run" task="entry" button="primary" label="run" size="sm"></fa-task-action-button> ' +
|
||||
'<fa-task-action-button ng-if="!entry.values.enabled" button="success" action="enable" icon="power-off" task="entry" label="enable" size="sm"></fa-task-action-button> ' +
|
||||
'<fa-task-action-button ng-if="entry.values.enabled" button="warning" action="disable" icon="power-off" task="entry" label="disable" size="sm"></fa-task-action-button>'
|
||||
)
|
||||
;
|
||||
|
||||
admin.addEntity(tasks);
|
||||
|
||||
admin.menu(
|
||||
nga.menu()
|
||||
.addChild(nga
|
||||
.menu(nodes)
|
||||
.icon('<i class="fa fa-dot-circle-o"></i>')
|
||||
)
|
||||
.addChild(nga
|
||||
.menu(tasks)
|
||||
.icon('<span class="fa fa-cog"></span>')
|
||||
)
|
||||
.addChild(nga
|
||||
.menu()
|
||||
.template(
|
||||
'<a href="/internal/logs" target="_blank">' +
|
||||
'<span class="fa fa-list"></span> Logs' +
|
||||
'</a>'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
nga.configure(admin);
|
||||
});
|
||||
|
|
@ -31,4 +31,10 @@ angular.module('ffffng')
|
|||
$scope.cancel = function () {
|
||||
Navigator.home();
|
||||
};
|
||||
|
||||
if (window.__nodeToken) {
|
||||
var token = window.__nodeToken;
|
||||
window.__nodeToken = undefined;
|
||||
$scope.onSubmitToken(token);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
1
client
Symbolic link
1
client
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
app
|
||||
|
|
@ -24,6 +24,7 @@ angular.module('ffffng').factory('app', function (fs, config, _) {
|
|||
app.use('/internal', auth.connect(internalAuth));
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
var adminDir = __dirname + '/../admin';
|
||||
var clientDir = __dirname + '/../client';
|
||||
|
|
|
|||
|
|
@ -76,6 +76,4 @@ var config = deepExtend({}, defaultConfig, configJSON);
|
|||
|
||||
module.exports = config;
|
||||
|
||||
angular.module('ffffng').factory('config', function () {
|
||||
return config;
|
||||
});
|
||||
angular.module('ffffng').constant('config', config);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ require('./utils/resources');
|
|||
require('./utils/strings');
|
||||
require('./utils/urlBuilder');
|
||||
|
||||
require('./resources/frontendResource');
|
||||
require('./resources/taskResource');
|
||||
require('./resources/nodeResource');
|
||||
require('./resources/monitoringResource');
|
||||
|
|
|
|||
31
server/resources/frontendResource.js
Normal file
31
server/resources/frontendResource.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('ffffng').factory('FrontendResource', function (
|
||||
Logger,
|
||||
Resources,
|
||||
ErrorTypes,
|
||||
fs
|
||||
) {
|
||||
var indexHtml = __dirname + '/../../client/index.html';
|
||||
|
||||
return {
|
||||
render: function (req, res) {
|
||||
var data = Resources.getData(req);
|
||||
|
||||
fs.readFile(indexHtml, 'utf8', function (err, body) {
|
||||
if (err) {
|
||||
Logger.tag('frontend').error('Could not read file: ', indexHtml, err);
|
||||
return Resources.error(res, {data: 'Internal error.', type: ErrorTypes.internalError});
|
||||
}
|
||||
|
||||
return Resources.successHtml(
|
||||
res,
|
||||
body.replace(
|
||||
/<body/,
|
||||
'<script>window.__nodeToken = \''+ data.token + '\';</script><body'
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -2,12 +2,15 @@
|
|||
|
||||
angular.module('ffffng').factory('Router', function (
|
||||
app,
|
||||
FrontendResource,
|
||||
NodeResource,
|
||||
MonitoringResource,
|
||||
TaskResource
|
||||
) {
|
||||
return {
|
||||
init: function () {
|
||||
app.post('/', FrontendResource.render);
|
||||
|
||||
app.post('/api/node', NodeResource.create);
|
||||
app.put('/api/node/:token', NodeResource.update);
|
||||
app.delete('/api/node/:token', NodeResource.delete);
|
||||
|
|
|
|||
|
|
@ -212,7 +212,10 @@ angular.module('ffffng')
|
|||
}
|
||||
|
||||
_.each(entries, function (value, key) {
|
||||
if (key === 'monitoring') {
|
||||
if (key === 'mac') {
|
||||
node['mac'] = value;
|
||||
node['mapId'] = _.toLower(value).replace(/:/g, '')
|
||||
} else if (key === 'monitoring') {
|
||||
var active = value === 'aktiv';
|
||||
var pending = value === 'pending';
|
||||
node.monitoring = active || pending;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('ffffng')
|
||||
.factory('config', function () {
|
||||
return <%= JSON.stringify(config) %>;
|
||||
});
|
||||
angular.module('ffffng').constant('config', <%= JSON.stringify(config) %>);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('ffffng').factory('Resources', function (_, Constraints, Validator, ErrorTypes) {
|
||||
function respond(res, httpCode, data) {
|
||||
res.writeHead(httpCode, {'Content-Type': 'application/json'});
|
||||
res.end(JSON.stringify(data));
|
||||
function respond(res, httpCode, data, type) {
|
||||
switch (type) {
|
||||
case 'html':
|
||||
res.writeHead(httpCode, {'Content-Type': 'text/html'});
|
||||
res.end(data);
|
||||
break;
|
||||
|
||||
default:
|
||||
res.writeHead(httpCode, {'Content-Type': 'application/json'});
|
||||
res.end(JSON.stringify(data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -84,11 +93,15 @@ angular.module('ffffng').factory('Resources', function (_, Constraints, Validato
|
|||
},
|
||||
|
||||
success: function (res, data) {
|
||||
respond(res, 200, data);
|
||||
respond(res, 200, data, 'json');
|
||||
},
|
||||
|
||||
successHtml: function (res, html) {
|
||||
respond(res, 200, html, 'html');
|
||||
},
|
||||
|
||||
error: function (res, err) {
|
||||
respond(res, err.type.code, err.data);
|
||||
respond(res, err.type.code, err.data, 'json');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue