diff --git a/.gitignore b/.gitignore index 1377554..b5c8ca2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.swp +Release/ +Staging/ diff --git a/build/build.sh b/build/build.sh index 557625d..ae8822e 100755 --- a/build/build.sh +++ b/build/build.sh @@ -6,10 +6,11 @@ if [ `basename $THISDIR` != 'build' ]; then exit 1 fi -SRC_DIR=../src -BUILD_DIR=`pwd` -PKG_DIR=Release -STAGING_DIR=Staging +BASE_DIR=`dirname $THISDIR` +SRC_DIR=$BASE_DIR/src +BUILD_DIR=$BASE_DIR/build +PKG_DIR=$BASE_DIR/Release +STAGING_DIR=$BASE_DIR/Staging NAME='ztncui' DESCRIPTION='ZeroTier network controller user interface' diff --git a/src/app.js b/src/app.js index 4147d79..6145cba 100644 --- a/src/app.js +++ b/src/app.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 5dc53e7..a5099c2 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ diff --git a/src/controllers/networkController.js b/src/controllers/networkController.js index 0363704..65e0634 100644 --- a/src/controllers/networkController.js +++ b/src/controllers/networkController.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ @@ -13,52 +13,68 @@ storage.initSync({dir: 'etc/storage'}); // ZT network controller home page exports.index = async function(req, res) { - const page = 'controller_home'; + const nav = + { + active: 'controller_home', + } try { zt_address = await zt.get_zt_address(); - res.render('index', {title: 'ztncui', page: page, zt_address: zt_address}); + res.render('index', {title: 'ztncui', nav: nav, zt_address: zt_address}); } catch (err) { res.render('index', {title: 'ztncui', - page: page, error: 'ERROR resolving ZT address: ' + err}); + nav: nav, error: 'ERROR resolving ZT address: ' + err}); } }; // Display list of all networks on this ZT network controller exports.network_list = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + } try { networks = await zt.network_list(); - res.render('networks', {title: 'Networks on this controller', page: page, networks: networks}); + res.render('networks', {title: 'Networks on this controller', nav: nav, networks: networks}); } catch (err) { - res.render('networks', {title: 'Networks on this controller', page: page, error: 'Error retrieving list of networks on this controller: ' + err}); + res.render('networks', {title: 'Networks on this controller', nav: nav, error: 'Error retrieving list of networks on this controller: ' + err}); } }; // Display detail page for specific network exports.network_detail = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '/controller/networks' + } try { const network = await zt.network_detail(req.params.nwid); const members = await zt.members(req.params.nwid); - res.render('network_detail', {title: 'Detail for network', page: page, network: network, members: members}); + res.render('network_detail', {title: 'Detail for network', nav: nav, network: network, members: members}); } catch (err) { - res.render('network_detail', {title: 'Detail for network', page: page, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); + res.render('network_detail', {title: 'Detail for network', nav: nav, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); } }; // Display Network create form on GET exports.network_create_get = function(req, res) { - const page = 'add_network'; + const nav = + { + active: 'add_network', + } - res.render('network_create', {title: 'Create network', page: page}); + res.render('network_create', {title: 'Create network', nav: nav}); }; // Handle Network create on POST exports.network_create_post = async function(req, res) { - const page = 'add_network'; + const nav = + { + active: 'add_network', + } req.checkBody('name', 'Network name required').notEmpty(); @@ -70,66 +86,83 @@ exports.network_create_post = async function(req, res) { const name = { name: req.body.name }; if (errors) { - res.render('network_create', {title: 'Create Network', page: page, name: name, errors: errors}); + res.render('network_create', {title: 'Create Network', nav: nav, name: name, errors: errors}); return; } else { try { const network = await zt.network_create(name); res.redirect('/controller/networks'); } catch (err) { - res.render('network_detail', {title: 'Create Network - error', page: page, error: 'Error creating network ' + name.name}); + res.render('network_detail', {title: 'Create Network - error', nav: nav, error: 'Error creating network ' + name.name}); } } }; // Display Network delete form on GET exports.network_delete_get = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '/controller/networks' + } try { const network = await zt.network_detail(req.params.nwid); - res.render('network_delete', {title: 'Delete network', page: page, + res.render('network_delete', {title: 'Delete network', nav: nav, nwid: req.params.nwid, network: network}); } catch (err) { - res.render('network_delete', {title: 'Delete network', page: page, error: 'Error resolving network ' + req.params.nwid + ': ' + err}); + res.render('network_delete', {title: 'Delete network', nav: nav, error: 'Error resolving network ' + req.params.nwid + ': ' + err}); } }; // Handle Network delete on POST exports.network_delete_post = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '/controller/networks' + } try { const network = await zt.network_delete(req.params.nwid); - res.render('network_delete', {title: 'Delete network', page: page, network: network}); + res.render('network_delete', {title: 'Delete network', nav: nav, network: network}); } catch (err) { - res.render('network_delete', {title: 'Delete network', page: page, error: 'Error deleting network ' + req.params.nwid + ': ' + err}); + res.render('network_delete', {title: 'Delete network', nav: nav, error: 'Error deleting network ' + req.params.nwid + ': ' + err}); } }; // Network object GET exports.network_object = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } try { const network = await zt.network_detail(req.params.nwid); - res.render(req.params.object, {title: req.params.object, page: page, network: network}, function(err, html) { + nav.whence = '/controller/network/' + network.nwid; + res.render(req.params.object, {title: req.params.object, nav: nav, network: network}, function(err, html) { if (err) { if (err.message.indexOf('Failed to lookup view') !== -1 ) { - return res.render('not_implemented', {title: req.params.object, page: page, network: network}); + return res.render('not_implemented', {title: req.params.object, nav: nav, network: network}); } throw err; } res.send(html); }); } catch (err) { - res.render(req.params.object, {title: req.params.object, page: page, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); + res.render(req.params.object, {title: req.params.object, nav: nav, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); } } // Handle Network rename form on POST exports.name = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '/controller/networks' + } req.checkBody('name', 'Network name required').notEmpty(); req.sanitize('name').escape(); @@ -142,16 +175,16 @@ exports.name = async function(req, res) { if (errors) { try { const network = await zt.network_detail(req.params.nwid); - res.render('name', {title: 'Rename network', page: page, network: network, name: name, errors: errors}); + res.render('name', {title: 'Rename network', nav: nav, network: network, name: name, errors: errors}); } catch (err) { - res.render('name', {title: 'Rename network', page: page, error: 'Error resolving network detail for network ' + req.params.nwid + ': ' + err}); + res.render('name', {title: 'Rename network', nav: nav, error: 'Error resolving network detail for network ' + req.params.nwid + ': ' + err}); } } else { try { const network = await zt.network_object(req.params.nwid, name); res.redirect('/controller/networks'); } catch ( err) { - res.render('name', {title: 'Rename network', page: page, error: 'Error renaming network ' + req.params.nwid + ': ' + err}); + res.render('name', {title: 'Rename network', nav: nav, error: 'Error renaming network ' + req.params.nwid + ': ' + err}); } } @@ -159,7 +192,11 @@ exports.name = async function(req, res) { // ipAssignmentPools POST exports.ipAssignmentPools = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } req.checkBody('ipRangeStart', 'IP range start required').notEmpty(); req.checkBody('ipRangeStart', 'IP range start needs a valid IPv4 or IPv6 address').isIP(); @@ -181,16 +218,18 @@ exports.ipAssignmentPools = async function(req, res) { if (errors) { try { const network = await zt.network_detail(req.params.nwid); - res.render('ipAssignmentPools', {title: 'ipAssignmentPools', page: page, ipAssignmentPool: ipAssignmentPool, network: network, errors: errors}); + nav.whence = '/controller/network/' + network.nwid; + res.render('ipAssignmentPools', {title: 'ipAssignmentPools', nav: nav, ipAssignmentPool: ipAssignmentPool, network: network, errors: errors}); } catch (err) { - res.render('ipAssignmentPools', {title: 'ipAssignmentPools', page: page, error: 'Error resolving network detail for network ' + req.params.nwid + ': ' + err}); + res.render('ipAssignmentPools', {title: 'ipAssignmentPools', nav: nav, error: 'Error resolving network detail for network ' + req.params.nwid + ': ' + err}); } } else { try { const network = await zt.ipAssignmentPools(req.params.nwid, ipAssignmentPool, 'add'); - res.render('ipAssignmentPools', {title: 'ipAssignmentPools', page: page, ipAssignmentPool: ipAssignmentPool, network: network}); + nav.whence = '/controller/network/' + network.nwid; + res.render('ipAssignmentPools', {title: 'ipAssignmentPools', nav: nav, ipAssignmentPool: ipAssignmentPool, network: network}); } catch (err) { - res.render('ipAssignmentPools', {title: 'ipAssignmentPools', page: page, error: 'Error applying IP Assignment Pools for network ' + req.params.nwid + ': ' + err}); + res.render('ipAssignmentPools', {title: 'ipAssignmentPools', nav: nav, error: 'Error applying IP Assignment Pools for network ' + req.params.nwid + ': ' + err}); } } } @@ -202,7 +241,11 @@ isValidPrefix = function(str, max) { // routes POST exports.routes = async function (req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } req.checkBody('target', 'Target network is required').notEmpty(); req.sanitize('target').trim(); @@ -242,16 +285,18 @@ exports.routes = async function (req, res) { if (errors) { try { const network = await zt.network_detail(req.params.nwid); - res.render('routes', {title: 'routes', page: page, route: route, network: network, errors: errors}); + nav.whence = '/controller/network/' + network.nwid; + res.render('routes', {title: 'routes', nav: nav, route: route, network: network, errors: errors}); } catch (err) { - res.render('routes', {title: 'routes', page: page, error: 'Error resolving network detail'}); + res.render('routes', {title: 'routes', nav: nav, error: 'Error resolving network detail'}); } } else { try { const network = await zt.routes(req.params.nwid, route, 'add'); - res.render('routes', {title: 'routes', page: page, route: route, network: network}); + nav.whence = '/controller/network/' + network.nwid; + res.render('routes', {title: 'routes', nav: nav, route: route, network: network}); } catch (err) { - res.render('routes', {title: 'routes', page: page, error: 'Error adding route for network ' + req.params.nwid + ': ' + err}); + res.render('routes', {title: 'routes', nav: nav, error: 'Error adding route for network ' + req.params.nwid + ': ' + err}); } } @@ -259,7 +304,11 @@ exports.routes = async function (req, res) { // route_delete GET exports.route_delete = async function (req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } const route = { @@ -270,15 +319,20 @@ exports.route_delete = async function (req, res) { try { const network = await zt.routes(req.params.nwid, route, 'delete'); - res.render('routes', {title: 'routes', page: page, route: route, network: network}); + nav.whence = '/controller/network/' + network.nwid; + res.render('routes', {title: 'routes', nav: nav, route: route, network: network}); } catch (err) { - res.render('routes', {title: 'routes', page: page, error: 'Error deleting route for network ' + req.params.nwid + ': ' + err}); + res.render('routes', {title: 'routes', nav: nav, error: 'Error deleting route for network ' + req.params.nwid + ': ' + err}); } } // ipAssignmentPool_delete GET exports.ipAssignmentPool_delete = async function (req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } const ipAssignmentPool = { @@ -289,15 +343,20 @@ exports.ipAssignmentPool_delete = async function (req, res) { try { const network = await zt.ipAssignmentPools(req.params.nwid, ipAssignmentPool, 'delete'); - res.render('ipAssignmentPools', {title: 'ipAssignmentPools', page: page, ipAssignmentPool: ipAssignmentPool, network: network}); + nav.whence = '/controller/network/' + network.nwid; + res.render('ipAssignmentPools', {title: 'ipAssignmentPools', nav: nav, ipAssignmentPool: ipAssignmentPool, network: network}); } catch (err) { - res.render('ipAssignmentPools', {title: 'ipAssignmentPools', page: page, error: 'Error deleting IP Assignment Pool for network ' + req.params.nwid + ': ' + err}); + res.render('ipAssignmentPools', {title: 'ipAssignmentPools', nav: nav, error: 'Error deleting IP Assignment Pool for network ' + req.params.nwid + ': ' + err}); } } // v4AssignMode POST exports.v4AssignMode = async function (req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } const v4AssignMode = { @@ -306,15 +365,20 @@ exports.v4AssignMode = async function (req, res) { try { const network = await zt.network_object(req.params.nwid, v4AssignMode); - res.render('v4AssignMode', {title: 'v4AssignMode', page: page, network: network}); + nav.whence = '/controller/network/' + network.nwid; + res.render('v4AssignMode', {title: 'v4AssignMode', nav: nav, network: network}); } catch (err) { - res.render('v4AssignMode', {title: 'v4AssignMode', page: page, error: 'Error applying v4AssignMode for network ' + req.params.nwid + ': ' + err}); + res.render('v4AssignMode', {title: 'v4AssignMode', nav: nav, error: 'Error applying v4AssignMode for network ' + req.params.nwid + ': ' + err}); } } // v6AssignMode POST exports.v6AssignMode = async function (req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } const v6AssignMode = { @@ -328,76 +392,103 @@ exports.v6AssignMode = async function (req, res) { try { const network = await zt.network_object(req.params.nwid, v6AssignMode); - res.render('v6AssignMode', {title: 'v6AssignMode', page: page, network: network}); + nav.whence = '/controller/network/' + network.nwid; + res.render('v6AssignMode', {title: 'v6AssignMode', nav: nav, network: network}); } catch (err) { - res.render('v6AssignMode', {title: 'v6AssignMode', page: page, error: 'Error applying v6AssignMode for network ' + req.params.nwid + ': ' + err}); + res.render('v6AssignMode', {title: 'v6AssignMode', nav: nav, error: 'Error applying v6AssignMode for network ' + req.params.nwid + ': ' + err}); } } // Display detail page for specific member exports.member_detail = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } try { const network = await zt.network_detail(req.params.nwid); const member = await zt.member_detail(req.params.nwid, req.params.id); - res.render('member_detail', {title: 'Network member detail', page: page, network: network, member: member}); + nav.whence = '/controller/network/' + network.nwid + '/members'; + res.render('member_detail', {title: 'Network member detail', nav: nav, network: network, member: member}); } catch (err) { - res.render(req.params.object, {title: req.params.object, page: page, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err}); + res.render(req.params.object, {title: req.params.object, nav: nav, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err}); } }; // Member object GET exports.member_object = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } try { const network = await zt.network_detail(req.params.nwid); const member = await zt.member_detail(req.params.nwid, req.params.id); - res.render(req.params.object, {title: req.params.object, page: page, network: network, member: member}, function(err, html) { + const name = await storage.getItem(member.id); + if (!name) name = ''; + member.name = name; + nav.whence = '/controller/network/' + network.nwid + '/members'; + res.render(req.params.object, {title: req.params.object, nav: nav, network: network, member: member}, function(err, html) { if (err) { if (err.message.indexOf('Failed to lookup view') !== -1 ) { - return res.render('not_implemented', {title: req.params.object, page: page, network: network, member: member}); + return res.render('not_implemented', {title: req.params.object, nav: nav, network: network, member: member}); } throw err; } res.send(html); }); } catch (err) { - res.render(req.params.object, {title: req.params.object, page: page, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err}); + res.render(req.params.object, {title: req.params.object, nav: nav, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err}); } } // Member authorized POST exports.member_authorized = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } const authorized = { authorized: req.body.authorized }; try { const network = await zt.network_detail(req.params.nwid); const member = await zt.member_object(req.params.nwid, req.params.id, authorized); - res.render('authorized', {title: 'authorized', page: page, network: network, member: member}); + nav.whence = '/controller/network/' + network.nwid + '/members'; + res.render('authorized', {title: 'authorized', nav: nav, network: network, member: member}); } catch (err) { - res.render('authorized', {title: 'authorized', page: page, error: 'Error authorizing member ' + req.params.id + ' on network ' + req.params.nwid + ': ' + err}); + res.render('authorized', {title: 'authorized', nav: nav, error: 'Error authorizing member ' + req.params.id + ' on network ' + req.params.nwid + ': ' + err}); } } // Easy network setup GET exports.easy_get = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '/controller/networks' + } try { const network = await zt.network_detail(req.params.nwid); - res.render('network_easy', {title: 'Easy setup of network', page: page, network: network}); + res.render('network_easy', {title: 'Easy setup of network', nav: nav, network: network}); } catch (err) { - res.render('network_easy', {title: 'Easy setup of network', page: page, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); + res.render('network_easy', {title: 'Easy setup of network', nav: nav, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); } } // Easy network setup POST exports.easy_post = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '/controller/networks' + } req.checkBody('networkCIDR', 'Network address is required').notEmpty(); req.sanitize('networkCIDR').trim(); @@ -447,23 +538,27 @@ exports.easy_post = async function(req, res) { v4AssignMode: v4AssignMode }; - res.render('network_easy', {title: 'Easy setup of network', page: page, network: network, errors: errors}); + res.render('network_easy', {title: 'Easy setup of network', nav: nav, network: network, errors: errors}); } else { try { const network = await zt.network_easy_setup(req.params.nwid, routes, ipAssignmentPools, v4AssignMode); - res.render('network_easy', {title: 'Easy setup of network', page: page, network: network, message: 'Network setup succeeded'}); + res.render('network_easy', {title: 'Easy setup of network', nav: nav, network: network, message: 'Network setup succeeded'}); } catch (err) { - res.render('network_easy', {title: 'Easy setup of network', page: page, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); + res.render('network_easy', {title: 'Easy setup of network', nav: nav, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); } } } // Easy members auth GET or POST exports.members = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '/controller/networks' + } let errors = null; @@ -520,18 +615,22 @@ exports.members = async function(req, res) { members.push(member); } - res.render('members', {title: 'Members of this network', page: page, + res.render('members', {title: 'Members of this network', nav: nav, network: network, members: members, errors: errors}); } catch (err) { - res.render('members', {title: 'Members of this network', page: page, + res.render('members', {title: 'Members of this network', nav: nav, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err}); } } -// Member delete GET and POST +// Member delete GET or POST exports.member_delete = async function(req, res) { - const page = 'networks'; + const nav = + { + active: 'networks', + whence: '' + } try { const network = await zt.network_detail(req.params.nwid); @@ -548,12 +647,109 @@ exports.member_delete = async function(req, res) { } if (!name) name = ''; member.name = name; + + nav.whence = '/controller/network/' + network.nwid + '/members'; res.render('member_delete', {title: 'Delete member from ' + network.name, network: network, member: member}); } catch (err) { - res.render('member_delete', {title: 'Delete member from network', page: page, + res.render('member_delete', {title: 'Delete member from network', nav: nav, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err}); } } +// ipAssignment delete GET +exports.delete_ip = async function(req, res) { + const nav = + { + active: 'networks', + whence: '' + } + + try { + const network = await zt.network_detail(req.params.nwid); + let member = await zt.member_detail(req.params.nwid, req.params.id); + nav.whence = '/controller/network/' + network.nwid + '/members'; + const name = await storage.getItem(member.id); + if (!name) name = ''; + member.name = name; + if (req.params.index) { + member = await zt.ipAssignmentDelete(network.nwid, member.id, + req.params.index); + res.redirect('/controller/network/' + network.nwid + '/member/' + + member.id + '/ipAssignments'); + } + res.render('ipAssignments', {title: 'ipAssignments ' + network.name, + index: req.params.index, network: network, member: member}); + } catch (err) { + res.render('ipAssignments', {title: 'ipAssignments', nav: nav, + error: 'Error resolving detail for member ' + req.params.id + + ' of network ' + req.params.nwid + ': ' + err}); + } +} + +// ipAssignments POST +exports.assign_ip = async function(req, res) { + const nav = + { + active: 'networks', + whence: '' + } + + try { + var network = await zt.network_detail(req.params.nwid); + } catch (err) { + throw err; + } + + req.checkBody('ipAddress', 'IP address required').notEmpty(); + req.checkBody('ipAddress', 'IP address must be a valid IPv4 or IPv6 address').isIP(); + req.checkBody('ipAddress', 'IP address must fall within a managed route') + .custom(value => { + let ipAddressInManagedRoute = false; + network.routes.forEach(function(item) { + let ipv4 = new ipaddr.Address4(value); + console.log('ipv4 = ' + JSON.stringify(ipv4)); + let target4 = new ipaddr.Address4(item.target); + console.log('target4 = ' + JSON.stringify(target4)); + if (ipv4.isValid() && target4.isValid()) { + if (ipv4.isInSubnet(target4)) ipAddressInManagedRoute = true; + } + let ipv6 = new ipaddr.Address6(value); + console.log('ipv6 = ' + JSON.stringify(ipv6)); + let target6 = new ipaddr.Address6(item.target); + console.log('target6 = ' + JSON.stringify(target6)); + if (ipv6.isValid() && target6.isValid()) { + if (ipv6.isInSubnet(target6)) ipAddressInManagedRoute = true; + } + }); + return ipAddressInManagedRoute; + }); + req.sanitize('ipAddress').escape(); + req.sanitize('ipAddress').trim(); + + const errors = req.validationErrors(); + + const ipAssignment = { ipAddress: req.body.ipAddress }; + + try { + let member = await zt.member_detail(req.params.nwid, req.params.id); + nav.whence = '/controller/network/' + network.nwid + '/members'; + + if (!errors) { + member = await zt.ipAssignmentAdd(network.nwid, member.id, ipAssignment); + } + + const name = await storage.getItem(member.id); + if (!name) name = ''; + member.name = name; + + res.render('ipAssignments', {title: 'ipAssignments', nav: nav, + ipAssignment: ipAssignment, network: network, member: member, + errors: errors}); + } catch (err) { + res.render('ipAssignments', {title: 'ipAssignments', nav: nav, + error: 'Error resolving detail for member ' + req.params.id + + ' of network ' + req.params.nwid + ': ' + err}); + } +} diff --git a/src/controllers/token.js b/src/controllers/token.js index 061e9e2..0bbdbe1 100644 --- a/src/controllers/token.js +++ b/src/controllers/token.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ diff --git a/src/controllers/usersController.js b/src/controllers/usersController.js index 7695b64..1d5a2e5 100644 --- a/src/controllers/usersController.js +++ b/src/controllers/usersController.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ @@ -44,18 +44,24 @@ update_users = async function(users) { } exports.users_list = async function(req, res) { - const page = 'users'; + const nav = + { + active: 'users', + } try { const users = await get_users(); - res.render('users', { title: 'Admin users', page: page, message: 'List of users with admin priviledges', users: users }); + res.render('users', { title: 'Admin users', nav: nav, message: 'List of users with admin priviledges', users: users }); } catch (err) { - res.render('users', { title: 'Admin users', page: page, message: 'Error', users: null, error: 'Error returning list of users: ' + err }); + res.render('users', { title: 'Admin users', nav: nav, message: 'Error', users: null, error: 'Error returning list of users: ' + err }); } } exports.password_get = async function(req, res) { - const page = 'users'; + const nav = + { + active: 'users', + } const user = { @@ -63,11 +69,14 @@ exports.password_get = async function(req, res) { password1: null, password2: null }; - res.render('password', { title: 'Set password', page: page, user: user, readonly: true, message: '' }); + res.render('password', { title: 'Set password', nav: nav, user: user, readonly: true, message: '' }); } exports.password_post = async function(req, res) { - const page = 'users'; + const nav = + { + active: 'users', + } req.checkBody('username', 'Username required').notEmpty(); req.sanitize('username').escape(); @@ -90,7 +99,7 @@ exports.password_post = async function(req, res) { password2: req.body.password2 }; const message = 'Please check errors below'; - res.render('password', { title: 'Set password', page: page, user: user, readonly: true, message: message, errors: errors }); + res.render('password', { title: 'Set password', nav: nav, user: user, readonly: true, message: message, errors: errors }); } else { let pass_set = true; if (req.body.pass_set === 'check') pass_set = false; @@ -115,12 +124,15 @@ exports.password_post = async function(req, res) { users = await update_users(users); const message = 'Successfully set password for ' + req.body.username; - res.render('password', { title: 'Set password', page: page, user: user, readonly: true, message: message }); + res.render('password', { title: 'Set password', nav: nav, user: user, readonly: true, message: message }); } } exports.user_create_get = async function(req, res) { - const page = 'create_user'; + const nav = + { + active: 'create_user', + } const user = { @@ -129,17 +141,23 @@ exports.user_create_get = async function(req, res) { password2: null }; - res.render('password', { title: 'Create new admin user', page: page, user: user, readonly: false}); + res.render('password', { title: 'Create new admin user', nav: nav, user: user, readonly: false}); } exports.user_create_post = async function(req, res) { - const page = 'create_user'; + const nav = + { + active: 'create_user', + } res.redirect(307, '/users/' + req.body.username + '/password'); } exports.user_delete = async function(req, res) { - const page = 'users'; + const nav = + { + active: 'users', + } try { var users = await get_users(); @@ -150,7 +168,7 @@ exports.user_delete = async function(req, res) { const user = users[req.params.name]; if (user && (req.session.user.name === user.name)) { - res.render('user_delete', { title: 'Delete user', page: page, user: user, self_delete: true }); + res.render('user_delete', { title: 'Delete user', nav: nav, user: user, self_delete: true }); } if (req.body.delete === 'delete') { @@ -158,15 +176,15 @@ exports.user_delete = async function(req, res) { const deleted_user = { name: user.name }; delete users[user.name]; users = await update_users(users); - res.render('user_delete', { title: 'Deleted user', page: page, user: deleted_user, deleted: true }); + res.render('user_delete', { title: 'Deleted user', nav: nav, user: deleted_user, deleted: true }); } else { - res.render('user_delete', { title: 'Delete user', page: page, user: null }); + res.render('user_delete', { title: 'Delete user', nav: nav, user: null }); } } else { if (user) { - res.render('user_delete', { title: 'Delete user', page: page, user: user }); + res.render('user_delete', { title: 'Delete user', nav: nav, user: user }); } else { - res.render('user_delete', { title: 'Delete user', page: page, user: null }); + res.render('user_delete', { title: 'Delete user', nav: nav, user: null }); } } } diff --git a/src/controllers/zt.js b/src/controllers/zt.js index 474f57a..cf770ff 100644 --- a/src/controllers/zt.js +++ b/src/controllers/zt.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ @@ -139,6 +139,42 @@ exports.ipAssignmentPools = async function(nwid, ipAssignmentPool, action) { } } +exports.ipAssignmentDelete = async function(nwid, id, ipAssignmentIndex) { + const options = await init_options(); + options.method = 'POST'; + + try { + const member = await member_detail(nwid, id); + const ipAssignments = member.ipAssignments; + ipAssignments.splice(ipAssignmentIndex, 1); + options.body = { ipAssignments: ipAssignments }; + const response = await got(ZT_ADDR + '/controller/network/' + + nwid + '/member/' + id, options); + response.body.deleted = true; + return response.body; + } catch(err) { + throw(err); + } +} + +exports.ipAssignmentAdd = async function(nwid, id, ipAssignment) { + const options = await init_options(); + options.method = 'POST'; + + try { + const member = await member_detail(nwid, id); + const ipAssignments = member.ipAssignments; + ipAssignments.push(ipAssignment.ipAddress); + options.body = { ipAssignments: ipAssignments }; + const response = await got(ZT_ADDR + '/controller/network/' + + nwid + '/member/' + id, options); + response.body.added = true; + return response.body; + } catch(err) { + throw(err); + } +} + exports.routes = async function(nwid, route, action) { const options = await init_options(); options.method = 'POST'; @@ -205,7 +241,7 @@ exports.members = async function(nwid) { } } -exports.member_detail = async function(nwid, id) { +member_detail = async function(nwid, id) { const options = await init_options(); try { @@ -216,6 +252,7 @@ exports.member_detail = async function(nwid, id) { throw(err); } } +exports.member_detail = member_detail; exports.member_object = async function(nwid, id, object) { const options = await init_options(); diff --git a/src/package-lock.json b/src/package-lock.json index f8cf779..a1784d8 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -1,6 +1,6 @@ { "name": "ztncui", - "version": "0.3.2", + "version": "0.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/package.json b/src/package.json index 2bd29e7..7d22d7f 100644 --- a/src/package.json +++ b/src/package.json @@ -1,6 +1,6 @@ { "name": "ztncui", - "version": "0.3.2", + "version": "0.4.0", "private": true, "scripts": { "start": "node ./bin/www", diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index 496f442..a4c5c7b 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -11,6 +11,20 @@ input[type=radio] { transform: scale(1.5); } +.table > tbody > tr > td { + vertical-align: middle; +} + +.left { + float: left; + text-align: left; +} + +.right { + float: right; + text-align: right; +} + .navbar-inverse { background-color: #315b80; border-color: #23415c; diff --git a/src/routes/index.js b/src/routes/index.js index 4872270..b178ab1 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ diff --git a/src/routes/users.js b/src/routes/users.js index fbc1168..11abd03 100644 --- a/src/routes/users.js +++ b/src/routes/users.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ diff --git a/src/routes/zt_controller.js b/src/routes/zt_controller.js index b74a5b9..f015545 100644 --- a/src/routes/zt_controller.js +++ b/src/routes/zt_controller.js @@ -1,6 +1,6 @@ /* ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. */ @@ -76,6 +76,12 @@ router.get('/network/:nwid/members', restrict, networkController.members); // POST request for easy member (de)authorization router.post('/network/:nwid/members', restrict, networkController.members); +// GET request for member ipAssignment delete +router.get('/network/:nwid/member/:id/ipAssignments/:index/delete', restrict, networkController.delete_ip); + +// POST request for member ipAssignment add +router.post('/network/:nwid/member/:id/ipAssignments', restrict, networkController.assign_ip); + // GET request for any network object diff --git a/src/views/authorized.pug b/src/views/authorized.pug index dcd936d..9043db1 100644 --- a/src/views/authorized.pug +++ b/src/views/authorized.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/controller_layout.pug b/src/views/controller_layout.pug index 04a6d71..87c08e3 100644 --- a/src/views/controller_layout.pug +++ b/src/views/controller_layout.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends head_layout @@ -18,13 +18,13 @@ block body_content | Key Networks .collapse.navbar-collapse(id='BarNav') ul.nav.navbar-nav - li(class=(page === 'controller_home'? 'active' : '')) + li(class=(nav.active === 'controller_home'? 'active' : '')) a(href='/controller') Home - li(class=(page === 'users'? 'active' : '')) + li(class=(nav.active === 'users'? 'active' : '')) a(href='/users') Users - li(class=(page === 'networks'? 'active' : '')) + li(class=(nav.active === 'networks'? 'active' : '')) a(href='/controller/networks') Networks - li(class=(page === 'add_network'? 'active' : '')) + li(class=(nav.active === 'add_network'? 'active' : '')) a(href='/controller/network/create') Add network ul.nav.navbar-nav.navbar-right li diff --git a/src/views/front_door.pug b/src/views/front_door.pug index 2ad7a27..e0f3a5b 100644 --- a/src/views/front_door.pug +++ b/src/views/front_door.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends login_layout diff --git a/src/views/head_layout.pug b/src/views/head_layout.pug index 136a39f..635ed4c 100644 --- a/src/views/head_layout.pug +++ b/src/views/head_layout.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. doctype html diff --git a/src/views/index.pug b/src/views/index.pug index ad9f7d2..02588ff 100644 --- a/src/views/index.pug +++ b/src/views/index.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends controller_layout diff --git a/src/views/ipAssignmentPools.pug b/src/views/ipAssignmentPools.pug index 7800b8a..da45c21 100644 --- a/src/views/ipAssignmentPools.pug +++ b/src/views/ipAssignmentPools.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/ipAssignments.pug b/src/views/ipAssignments.pug new file mode 100644 index 0000000..519ca02 --- /dev/null +++ b/src/views/ipAssignments.pug @@ -0,0 +1,70 @@ +//- + ztncui - ZeroTier network controller UI + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) + Licensed under GPLv3 - see LICENSE for details. + +extends network_layout + +block net_content + if errors + .row + .col-sm-12 + .alert.alert-warning + b Note errors listed below + + form(method='POST' action='') + + .row + .col-sm-6 + h4 Member name: + b= member.name + .col-sm-6.right + h4 ZeroTier address: + b= member.id + + .row + .col-sm-12 + table.table.table-responsive.table-striped.table-hover + tr + th(width='3%') + th IP address + + each ipAssignment, index in member.ipAssignments + tr + td(width='3%') + a.btn.btn-link(role='button' href='/controller/network/' + network.nwid + '/member/' + member.id + '/ipAssignments/' + index + '/delete') + i.glyphicon.glyphicon-trash + td + each digit in ipAssignment + = digit + + tr + td + button.btn.btn-link(type='submit') + i.glyphicon.glyphicon-plus + td + input#ipAddress.form-control(type='text' name='ipAddress' placeholder='IP address' value=(undefined===ipAssignment? '' : ipAssignment.ipAddress)) + + .row + .col-sm-12 + a(href='/controller/network/' + network.nwid + '/routes') + h3 Managed routes + table.table.table-responsive.table-striped.table-hover + tr + th + th Target + th Gateway + each route in network.routes + tr + td(width='3%') + a.btn.btn-link(role='button' href='/controller/network/' + network.nwid + '/routes/' + route.target + '/delete') + i.glyphicon.glyphicon-trash + td= route.target + td= route.via + + if errors + .row + .col-sm-12 + ul + for err in errors + li!= err.msg diff --git a/src/views/login.pug b/src/views/login.pug index ea501cd..378a885 100644 --- a/src/views/login.pug +++ b/src/views/login.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends login_layout @@ -26,7 +26,7 @@ block login_content .input-group span.input-group-addon i.glyphicon.glyphicon-user - input#username.form-control.input-lg(type='text' name='username' placeholder='Enter your username') + input#username.form-control(type='text' name='username' placeholder='Enter your username') .form-group.row .col-sm-2 @@ -35,11 +35,11 @@ block login_content .input-group span.input-group-addon i.glyphicon.glyphicon-lock - input#password.form-control.input-lg(type='password' name='password' placeholder='Enter your password') + input#password.form-control(type='password' name='password' placeholder='Enter your password') .form-group.row .col-sm-2 .col-sm-10 - button.btn.btn-primary.btn-lg(type='submit') Login + button.btn.btn-primary(type='submit') Login = ' ' - a.btn.btn-default.btn-lg(href='/' name='cancel' role='button') Cancel + a.btn.btn-default(href='/' name='cancel' role='button') Cancel diff --git a/src/views/login_layout.pug b/src/views/login_layout.pug index 362148f..9e4782c 100644 --- a/src/views/login_layout.pug +++ b/src/views/login_layout.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends head_layout diff --git a/src/views/member_delete.pug b/src/views/member_delete.pug index 96d0063..a1ce416 100644 --- a/src/views/member_delete.pug +++ b/src/views/member_delete.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/member_detail.pug b/src/views/member_detail.pug index 461bbf1..4e5656d 100644 --- a/src/views/member_detail.pug +++ b/src/views/member_detail.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/members.pug b/src/views/members.pug index 9dbde9f..d6d7519 100644 --- a/src/views/members.pug +++ b/src/views/members.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout @@ -46,9 +46,13 @@ block net_content input.checkbox(type='checkbox' name='authCheckBox' value=member.id checked=(member.authorized? true : false)) td each ipAssignment in member.ipAssignments - each digit in ipAssignment - = digit - = ' ' + a(href='/controller/network/' + network.nwid + '/member/' + member.id + '/ipAssignments') + each digit in ipAssignment + = digit + = ' ' + else + a(href='/controller/network/' + network.nwid + '/member/' + member.id + '/ipAssignments') + | IP assignment else .alert.alert-info diff --git a/src/views/name.pug b/src/views/name.pug index c089ea2..8ce23e4 100644 --- a/src/views/name.pug +++ b/src/views/name.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/network_create.pug b/src/views/network_create.pug index 2d2de35..c5fb323 100644 --- a/src/views/network_create.pug +++ b/src/views/network_create.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends controller_layout diff --git a/src/views/network_delete.pug b/src/views/network_delete.pug index 37582c9..5d8766d 100644 --- a/src/views/network_delete.pug +++ b/src/views/network_delete.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/network_detail.pug b/src/views/network_detail.pug index 11f2cdd..e42ed02 100644 --- a/src/views/network_detail.pug +++ b/src/views/network_detail.pug @@ -1,18 +1,14 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. -extends controller_layout +extends network_layout -block content +block net_content if error b #{error} else - h2 - a(href= network.nwid + '/name') #{network.name} - | (#{network.nwid}): - - if (members !== undefined) h3 Members each value, key in members diff --git a/src/views/network_easy.pug b/src/views/network_easy.pug index 7f22f36..ebe0151 100644 --- a/src/views/network_easy.pug +++ b/src/views/network_easy.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/network_layout.pug b/src/views/network_layout.pug index d2394d3..4ffb0aa 100644 --- a/src/views/network_layout.pug +++ b/src/views/network_layout.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends controller_layout @@ -10,10 +10,13 @@ block content b #{error} else .row - .col-sm-12 + .col-sm-10 h2 a(href='/controller/network/' + network.nwid) #{network.name} | (#{network.nwid}): h3= title - + + .col-sm-2 + h2.right + a.btn.btn-default(href=nav.whence role='button') Back block net_content diff --git a/src/views/networks.pug b/src/views/networks.pug index 353ba6e..e4cc173 100644 --- a/src/views/networks.pug +++ b/src/views/networks.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends controller_layout diff --git a/src/views/not_implemented.pug b/src/views/not_implemented.pug index 5196046..a4e0a2b 100644 --- a/src/views/not_implemented.pug +++ b/src/views/not_implemented.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/password.pug b/src/views/password.pug index 53710d7..1eb850c 100644 --- a/src/views/password.pug +++ b/src/views/password.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends users_layout @@ -20,7 +20,7 @@ block users_content .input-group span.input-group-addon i.glyphicon.glyphicon-user - input#username.form-control.input-lg(type='text' name='username' placeholder='Enter username' value=user.name readonly=readonly) + input#username.form-control(type='text' name='username' placeholder='Enter username' value=user.name readonly=readonly) .form-group.row .col-sm-2 @@ -29,7 +29,7 @@ block users_content .input-group span.input-group-addon i.glyphicon.glyphicon-lock - input#password1.form-control.input-lg(type='password' name='password1' placeholder='Enter new password' value=(undefined===user.password1? '' : user.password1)) + input#password1.form-control(type='password' name='password1' placeholder='Enter new password' value=(undefined===user.password1? '' : user.password1)) .form-group.row .col-sm-2 @@ -38,7 +38,7 @@ block users_content .input-group span.input-group-addon i.glyphicon.glyphicon-lock - input#password2.form-control.input-lg(type='password' name='password2' placeholder='Re-enter password' value=(undefined===user.password2? '' : user.password2)) + input#password2.form-control(type='password' name='password2' placeholder='Re-enter password' value=(undefined===user.password2? '' : user.password2)) .form-group.row .col-sm-2 @@ -49,9 +49,9 @@ block users_content .form-group.row .col-sm-2 .col-sm-10 - button.btn.btn-primary.btn-lg(type='submit') Set password + button.btn.btn-primary(type='submit') Set password = ' ' - a.btn.btn-default.btn-lg(href='/users' name='cancel' role='button') Cancel + a.btn.btn-default(href='/users' name='cancel' role='button') Cancel if errors .form-group.row diff --git a/src/views/routes.pug b/src/views/routes.pug index adb22f6..686909f 100644 --- a/src/views/routes.pug +++ b/src/views/routes.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout @@ -42,7 +42,7 @@ block net_content .col-sm-2 button.btn.btn-primary(type='submit') Submit = ' ' - a.btn.btn-default(href='/controller/network/' + network.nwid name='cancel' role='button') Cancel + a.btn.btn-default(href='/controller/networks' name='cancel' role='button') Cancel if errors .row diff --git a/src/views/user_delete.pug b/src/views/user_delete.pug index 56acef8..14c1116 100644 --- a/src/views/user_delete.pug +++ b/src/views/user_delete.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends users_layout diff --git a/src/views/users.pug b/src/views/users.pug index 868a439..5a6d247 100644 --- a/src/views/users.pug +++ b/src/views/users.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends users_layout diff --git a/src/views/users_layout.pug b/src/views/users_layout.pug index 3faf3c5..84bdbbd 100644 --- a/src/views/users_layout.pug +++ b/src/views/users_layout.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends head_layout @@ -18,11 +18,11 @@ block body_content | Key Networks .collapse.navbar-collapse(id='BarNav') ul.nav.navbar-nav - li(class=(page === 'home'? 'active' : '')) + li(class=(nav.active === 'home'? 'active' : '')) a(href='/controller') Home - li(class=(page === 'users'? 'active' : '')) + li(class=(nav.active === 'users'? 'active' : '')) a(href='/users') Users - li(class=(page === 'create_user'? 'active' : '')) + li(class=(nav.active === 'create_user'? 'active' : '')) a(href='/users/create') Create user ul.nav.navbar-nav.navbar-right li diff --git a/src/views/v4AssignMode.pug b/src/views/v4AssignMode.pug index 8aa336d..b00ff00 100644 --- a/src/views/v4AssignMode.pug +++ b/src/views/v4AssignMode.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout diff --git a/src/views/v6AssignMode.pug b/src/views/v6AssignMode.pug index d4cc5e4..a421d8d 100644 --- a/src/views/v6AssignMode.pug +++ b/src/views/v6AssignMode.pug @@ -1,6 +1,6 @@ //- ztncui - ZeroTier network controller UI - Copyright (C) 2017 Key Networks (https://key-networks.com) + Copyright (C) 2017-2018 Key Networks (https://key-networks.com) Licensed under GPLv3 - see LICENSE for details. extends network_layout