Browse Source

Issue #2: Unable to write ipAssignments

Key Networks 7 years ago
parent
commit
09f1e841b3

+ 2 - 0
.gitignore

@@ -1 +1,3 @@
 *.swp
+Release/
+Staging/

+ 5 - 4
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'

+ 1 - 1
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.
 */
 

+ 1 - 1
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.
 */
 

+ 270 - 74
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});
+  }
+}

+ 1 - 1
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.
 */
 

+ 36 - 18
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 });
     }
   }
 }

+ 39 - 2
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();

+ 1 - 1
src/package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "ztncui",
-  "version": "0.3.2",
+  "version": "0.4.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 1 - 1
src/package.json

@@ -1,6 +1,6 @@
 {
   "name": "ztncui",
-  "version": "0.3.2",
+  "version": "0.4.0",
   "private": true,
   "scripts": {
     "start": "node ./bin/www",

+ 14 - 0
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;

+ 1 - 1
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.
 */
 

+ 1 - 1
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.
 */
 

+ 7 - 1
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

+ 1 - 1
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

+ 5 - 5
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

+ 1 - 1
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

+ 1 - 1
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

+ 1 - 1
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

+ 1 - 1
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

+ 70 - 0
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

+ 5 - 5
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

+ 1 - 1
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

+ 1 - 1
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

+ 1 - 1
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

+ 8 - 4
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

+ 1 - 1
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

+ 1 - 1
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

+ 1 - 1
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

+ 3 - 7
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

+ 1 - 1
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

+ 6 - 3
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

+ 1 - 1
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

+ 1 - 1
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

+ 6 - 6
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

+ 2 - 2
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

+ 1 - 1
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

+ 1 - 1
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

+ 4 - 4
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

+ 1 - 1
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

+ 1 - 1
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