diff --git a/README.md b/README.md
index f707a8a..baf04f1 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ See [github.com/key-networks/ztncui-containerized](https://github.com/key-networ
Relative directory references below are relative to the cloned ztncui directory.
### Prerequisites
-* ztncui is a [node.js](https://nodejs.org) [Express](https://expressjs.com) application that requires [node.js](https://nodejs.org) v8 or higher.
+* ztncui is a [node.js](https://nodejs.org) [Express](https://expressjs.com) application that requires [node.js](https://nodejs.org) v14.
* ztncui uses argon2 for password hashing. Argon2 needs the following:
1. g++
@@ -29,7 +29,7 @@ sudo npm install -g node-gyp
* ztncui requires [ZeroTier One](https://www.zerotier.com/download.shtml) to be installed on the same machine. This will run as the network controller to establish ZeroTier networks.
-* ztncui has been developed on a Linux platform and expects the ZT home directory to be in `/var/lib/zerotier-one`. It should be easy to modify for other platforms - please feed back if this is required. (Edit: it should be easier to run on any platform now using a `.env` file - see below).
+* ztncui has been developed on a Linux platform and expects the ZT home directory to be in `/var/lib/zerotier-one`.
### Installing
##### 1. Clone the repository on a machine running ZeroTier One:
@@ -79,13 +79,20 @@ chmod 600 .env
The `.env` file should make it easier to run ztncui on a non-Linux platform.
-##### 4. Copy the default passwd file
+##### 4. Run in production mode
+To run the server in production mode, add the following to the `.env` file (see 3B above):
+```
+NODE_ENV=production
+```
+Without this, the template engine always re-compiles the pug file when rendering (taking ~200 ms!)
+
+##### 5. Copy the default passwd file
To prevent git from over-writing your password file every time you pull updates from the repository, the etc/passwd file has been added to .gitignore. So you need to copy the default file after the first time you do a git clone. All these things ideally need to be done with a package installer script:
```shell
cp -v etc/default.passwd etc/passwd
```
-##### 5. Start the app manually:
+##### 6. Start the app manually:
```shell
npm start
```
@@ -94,7 +101,7 @@ This will run the app on TCP port 3000 by default. If port 3000 is already in u
HTTP_PORT=3456
```
-##### 6. Start the app automatically
+##### 7. Start the app automatically
To start the app automatically, something like [PM2](http://pm2.keymetrics.io) can be used. Install it with:
```shell
sudo npm install -g pm2
@@ -117,27 +124,27 @@ Save the current PM2 process list so that ztncui will restart across reboots:
pm2 save
```
-##### 7. Test access on http://localhost:3000
+##### 8. Test access on http://localhost:3000
If the machine has a GUI and GUI web browser, then use it to access the app, otherwise use a text web browser like Lynx or a CLI web browser like curl:
```shell
curl http://localhost:3000
```
You should see the front page of the app (or the raw HTML with curl).
-##### 8. Remote access via HTTPS
+##### 9. Remote access via HTTPS
This app listens for HTTP requests on the looback interface (default port 3000). It can be reverse proxied by Nginx (which can proxy the HTTP as HTTPS), or accessed over an SSH tunnel as described below.
-The app can be made to listen on all interfaces for HTTP requests by setting HTTP_ALL_INTERFACES in the .env file, e.g.:
+The app can be made to listen on all interfaces for HTTP requests by setting HTTP_ALL_INTERFACES in the `.env` file, e.g.:
```
HTTP_ALL_INTERFACES=yes
```
Note that HTTP traffic is unencrypted, so this should only be done on a secure network, otherwise usernames and passwords will be exposed in plain text over the network.
-The app can be made to listen on all interfaces for HTTPS requests by specifying HTTPS_PORT in the .env file, e.g.:
+The app can be made to listen on all interfaces for HTTPS requests by specifying HTTPS_PORT in the `.env` file, e.g.:
```
HTTPS_PORT=3443
```
-The app can be made to listen on a specific interface for HTTPS requests by specifying HTTPS_HOST (the host name or IP address of the interface) in the .env file, e.g.:
+The app can be made to listen on a specific interface for HTTPS requests by specifying HTTPS_HOST (the host name or IP address of the interface) in the `.env` file, e.g.:
```
HTTPS_HOST=12.34.56.78
```
@@ -156,7 +163,9 @@ If HTTPS_HOST is not specified, but HTTPS_PORT is specified, then the app will l
###### TLS Certificate
For HTTPS you obviously need a TLS (SSL) certificate and private key pair. There are a few options:
-1. Generate a self-signed certificate as follows:
+1. By default, if there is no existing TLS certificate and private key pair, the RPM and DEB packages automatically generate a self-signed certificate / private key pair.
+
+2. If you are running directly from source, then generate a self-signed certificate as follows:
```shell
cd etc/tls
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privkey.pem -out fullchain.pem
@@ -165,11 +174,11 @@ For HTTPS you obviously need a TLS (SSL) certificate and private key pair. Ther
The advantage of this option is that it is quick and easy to generate the certificate / private key pair. The disadvantage is that your web browser will give you a warning that it cannot verify the certificate. You can override this warning and make a temporary exception.
-2. Buy a certificate:
+3. Buy a certificate:
You will need to store the private key as `etc/tls/privkey.pem` and the full certificate chain as `etc/tls/fullchain.pem`. They need to be in PEM format.
-3. Get a free certificate from Letsencrypt.org:
+4. Get a free certificate from Letsencrypt.org:
a. Install certbot by following the instructions at certbot.eff.org:
@@ -201,7 +210,7 @@ For HTTPS you obviously need a TLS (SSL) certificate and private key pair. Ther
Once you have a certificate at `etc/tls/fullchain.pem` and private key at `etc/tls/privkey.pem`, you should be able to access ztncui over HTTPS on the port specified by HTTPS_PORT.
-##### 9. Remote access via SSH
+##### 10. Remote access via SSH
###### SSH tunnel from Linux / Unix / macOS client
An SSH tunnel can be established with:
```shell
@@ -304,3 +313,6 @@ Problems with ztncui can be reported using the GitHub issue tracking system. Pl
## License
The ztncui code is open source code, licensed under the GNU GPLv3, and is free to use on those terms. If you are interested in commercial licensing, please contact us via the contact form at [key-networks.com](https://key-networks.com) .
+
+## Thanks
+@lideming for a rework and improvement of the network details page, adding DNS support, peer status/address/latency and other improvements.
diff --git a/build/build.sh b/build/build.sh
index 4dc24cb..03c5492 100755
--- a/build/build.sh
+++ b/build/build.sh
@@ -24,7 +24,7 @@ LICENSE='GPLv3'
BINDINGGYP='node_modules/argon2/binding.gyp'
-NODE_VER='v8'
+NODE_VER='v14'
if [ ! -f /usr/lib/gcc/x86_64-redhat-linux/10/libstdc++.a ]; then
echo "You must install libstdc++-static"
@@ -75,7 +75,7 @@ if [ $? -ne 0 ]; then
fi
popd
-pkg -c ./package.json -t node8-linux-x64 bin/www -o $BUILD_DIR/ztncui
+pkg -c ./package.json -t node14-linux-x64 bin/www -o $BUILD_DIR/ztncui
popd
diff --git a/src/app.js b/src/app.js
index 6145cba..5bbde10 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1,6 +1,6 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
*/
diff --git a/src/bin/www b/src/bin/www
index 56fe4da..837bedd 100755
--- a/src/bin/www
+++ b/src/bin/www
@@ -10,11 +10,6 @@ const http = require('http');
const https = require('https');
const fs = require('fs');
-const options = {
- cert: fs.readFileSync('etc/tls/fullchain.pem'),
- key: fs.readFileSync('etc/tls/privkey.pem')
-}
-
/**
* Get ports from environment and store in Express.
*/
@@ -50,6 +45,11 @@ if (http_all_interfaces) {
app.listen(http_port, 'localhost');
}
+const options = !https_port ? {} : {
+ cert: fs.readFileSync('etc/tls/fullchain.pem'),
+ key: fs.readFileSync('etc/tls/privkey.pem')
+};
+
const server = https.createServer(options, app);
if (https_port) {
diff --git a/src/controllers/auth.js b/src/controllers/auth.js
index a5099c2..8e11ecb 100644
--- a/src/controllers/auth.js
+++ b/src/controllers/auth.js
@@ -1,13 +1,13 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
*/
const argon2 = require('argon2');
const usersController = require('../controllers/usersController');
-hash_check = async function(user, password) {
+const hash_check = async function(user, password) {
let verified = false;
try {
var users = await usersController.get_users();
@@ -43,6 +43,6 @@ exports.restrict = function(req, res, next) {
next();
} else {
req.session.error = 'Access denied!';
- res.redirect('/login');
+ res.redirect('/login?redirect=' + encodeURIComponent(req.originalUrl));
}
}
diff --git a/src/controllers/networkController.js b/src/controllers/networkController.js
index 009802a..c13c299 100644
--- a/src/controllers/networkController.js
+++ b/src/controllers/networkController.js
@@ -1,6 +1,6 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
*/
@@ -12,6 +12,42 @@ const util = require('util');
storage.initSync({dir: 'etc/storage'});
+async function get_network_with_members(nwid) {
+ const [network, peers, members] = await Promise.all([
+ zt.network_detail(nwid),
+ zt.peers(),
+ zt.members(nwid)
+ .then(member_ids =>
+ Promise.all(
+ Object.keys(member_ids)
+ .map(id => Promise.all([
+ zt.member_detail(nwid, id),
+ storage.getItem(id)
+ ]))
+ )
+ ).then(results => results.map(([member, name]) => {
+ member.name = name || '';
+ return member;
+ }))
+ ]);
+ for (const member of members) {
+ member.peer = peers.find(x => x.address === member.address);
+ }
+ return {network, members};
+}
+
+async function get_network_member(nwid, memberid) {
+ const [network, member, peer, name] = await Promise.all([
+ zt.network_detail(nwid),
+ zt.member_detail(nwid, memberid),
+ zt.peer(memberid),
+ storage.getItem(memberid)
+ ]);
+ member.name = name || '';
+ member.peer = peer;
+ return {network, member};
+}
+
// ZT network controller home page
exports.index = async function(req, res) {
const navigate =
@@ -20,11 +56,11 @@ exports.index = async function(req, res) {
}
try {
- zt_address = await zt.get_zt_address();
- res.render('index', {title: 'ztncui', navigate: navigate, zt_address: zt_address});
+ const zt_status = await zt.get_zt_status();
+ res.render('index', {title: 'ztncui', navigate: navigate, zt_status});
} catch (err) {
res.render('index', {title: 'ztncui',
- navigate: navigate, error: 'ERROR resolving ZT address: ' + err});
+ navigate: navigate, error: 'ERROR getting ZT status: ' + err});
}
};
@@ -51,13 +87,15 @@ exports.network_detail = async function(req, res) {
whence: '/controller/networks'
}
- console.log('NAVIGATE = ' + navigate.toString());
- console.log(util.inspect(navigate, false, null, true /* enable colors */))
-
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', navigate: navigate, network: network, members: members});
+ const [
+ {network, members},
+ zt_address
+ ] = await Promise.all([
+ get_network_with_members(req.params.nwid),
+ zt.get_zt_address()
+ ]);
+ res.render('network_detail', {title: 'Network ' + network.name, navigate: navigate, network: network, members: members, zt_address: zt_address});
} catch (err) {
res.render('network_detail', {title: 'Detail for network', navigate: navigate, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err});
}
@@ -95,7 +133,7 @@ exports.network_create_post = async function(req, res) {
} else {
try {
const network = await zt.network_create(name);
- res.redirect('/controller/networks');
+ res.redirect('/controller/network/' + network.nwid);
} catch (err) {
res.render('network_detail', {title: 'Create Network - error', navigate: navigate, error: 'Error creating network ' + name.name});
}
@@ -177,21 +215,15 @@ exports.name = async function(req, res) {
let name = { name: req.body.name };
if (errors) {
- try {
- const network = await zt.network_detail(req.params.nwid);
- res.render('name', {title: 'Rename network', navigate: navigate, network: network, name: name, errors: errors});
- } catch (err) {
- res.render('name', {title: 'Rename network', navigate: navigate, error: 'Error resolving network detail for network ' + req.params.nwid + ': ' + err});
- }
+ console.error("network name validation errors", errors);
} else {
try {
const network = await zt.network_object(req.params.nwid, name);
- res.redirect('/controller/networks');
} catch ( err) {
- res.render('name', {title: 'Rename network', navigate: navigate, error: 'Error renaming network ' + req.params.nwid + ': ' + err});
+ console.error("Error renaming network " + req.params.nwid, err);
}
}
-
+ res.redirect('/controller/network/' + req.params.nwid);
};
// ipAssignmentPools POST
@@ -425,6 +457,35 @@ exports.v6AssignMode = async function (req, res) {
}
}
+// dns POST
+exports.dns = async function (req, res) {
+ const navigate = {
+ active: 'networks',
+ whence: ''
+ };
+
+ const dns = {
+ dns: {
+ domain: req.body.domain,
+ servers: req.body.servers
+ .split('\n')
+ .map(x => x.trim())
+ .filter(ip =>
+ new ipaddr.Address4(ip).isValid() ||
+ new ipaddr.Address6(ip).isValid()
+ )
+ }
+ };
+
+ try {
+ const network = await zt.network_object(req.params.nwid, dns);
+ navigate.whence = '/controller/network/' + network.nwid;
+ res.render('dns', {title: 'dns', navigate: navigate, network: network});
+ } catch (err) {
+ res.render('dns', {title: 'dns', navigate: navigate, error: 'Error updating dns for network ' + req.params.nwid + ': ' + err});
+ }
+}
+
// Display detail page for specific member
exports.member_detail = async function(req, res) {
const navigate =
@@ -434,15 +495,12 @@ exports.member_detail = async function(req, res) {
}
try {
- const network = await zt.network_detail(req.params.nwid);
- const member = await zt.member_detail(req.params.nwid, req.params.id);
- let name = await storage.getItem(member.id);
- if (!name) name = '';
- member.name = name;
- navigate.whence = '/controller/network/' + network.nwid + '/members';
+ const {network, member} = await get_network_member(req.params.nwid, req.params.id);
+ navigate.whence = '/controller/network/' + network.nwid + '#members';
res.render('member_detail', {title: 'Network member detail', navigate: navigate, network: network, member: member});
} catch (err) {
- res.render(req.params.object, {title: req.params.object, navigate: navigate, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err});
+ console.error(err);
+ res.render('error', {title: req.params.object, navigate: navigate, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err});
}
};
@@ -455,12 +513,8 @@ exports.member_object = async function(req, res) {
}
try {
- const network = await zt.network_detail(req.params.nwid);
- const member = await zt.member_detail(req.params.nwid, req.params.id);
- let name = await storage.getItem(member.id);
- if (!name) name = '';
- member.name = name;
- navigate.whence = '/controller/network/' + network.nwid + '/members';
+ const {network, member} = await get_network_member(req.params.nwid, req.params.id);
+ navigate.whence = '/controller/network/' + network.nwid + '#members';
res.render(req.params.object, {title: req.params.object, navigate: navigate, network: network, member: member}, function(err, html) {
if (err) {
if (err.message.indexOf('Failed to lookup view') !== -1 ) {
@@ -480,7 +534,7 @@ exports.easy_get = async function(req, res) {
const navigate =
{
active: 'networks',
- whence: '/controller/networks'
+ whence: '/controller/network/' + req.params.nwid
}
try {
@@ -561,7 +615,7 @@ exports.easy_post = async function(req, res) {
}
}
-// Easy members auth GET or POST
+// Easy members auth POST
exports.members = async function(req, res) {
const navigate =
{
@@ -630,26 +684,6 @@ exports.members = async function(req, res) {
}
}
}
-
- try {
- const network = await zt.network_detail(req.params.nwid);
- const member_ids = await zt.members(req.params.nwid);
- const members = [];
- for (id in member_ids) {
- let member = await zt.member_detail(req.params.nwid, id);
- let name = await storage.getItem(member.id);
- if (!name) name = '';
- member.name = name;
- members.push(member);
- }
-
- res.render('members', {title: 'Members of this network', navigate: navigate,
- network: network, members: members, errors: errors});
- } catch (err) {
- res.render('members', {title: 'Members of this network', navigate: navigate,
- error: 'Error resolving detail for network ' + req.params.nwid
- + ': ' + err});
- }
}
// Member delete GET or POST
@@ -673,10 +707,9 @@ exports.member_delete = async function(req, res) {
member = await zt.member_detail(req.params.nwid, req.params.id);
name = await storage.getItem(member.id);
}
- if (!name) name = '';
- member.name = name;
+ member.name = name || '';
- navigate.whence = '/controller/network/' + network.nwid + '/members';
+ navigate.whence = '/controller/network/' + network.nwid;
res.render('member_delete', {title: 'Delete member from ' + network.name,
navigate: navigate, network: network, member: member});
} catch (err) {
@@ -697,10 +730,8 @@ exports.delete_ip = async function(req, res) {
try {
const network = await zt.network_detail(req.params.nwid);
let member = await zt.member_detail(req.params.nwid, req.params.id);
- navigate.whence = '/controller/network/' + network.nwid + '/members';
- let name = await storage.getItem(member.id);
- if (!name) name = '';
- member.name = name;
+ navigate.whence = '/controller/network/' + network.nwid;
+ member.name = await storage.getItem(member.id) | '';
if (req.params.index) {
member = await zt.ipAssignmentDelete(network.nwid, member.id,
req.params.index);
@@ -758,15 +789,13 @@ exports.assign_ip = async function(req, res) {
try {
let member = await zt.member_detail(req.params.nwid, req.params.id);
- navigate.whence = '/controller/network/' + network.nwid + '/members';
+ navigate.whence = '/controller/network/' + network.nwid;
if (!errors) {
member = await zt.ipAssignmentAdd(network.nwid, member.id, ipAssignment);
}
- let name = await storage.getItem(member.id);
- if (!name) name = '';
- member.name = name;
+ member.name = await storage.getItem(member.id) | '';
res.render('ipAssignments', {title: 'ipAssignments', navigate: navigate,
ipAssignment: ipAssignment, network: network, member: member,
diff --git a/src/controllers/token.js b/src/controllers/token.js
index 0bbdbe1..4a5f8da 100644
--- a/src/controllers/token.js
+++ b/src/controllers/token.js
@@ -1,6 +1,6 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 780dc48..4ac89fb 100644
--- a/src/controllers/usersController.js
+++ b/src/controllers/usersController.js
@@ -1,6 +1,6 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
*/
@@ -17,7 +17,7 @@ const chmod = util.promisify(fs.chmod);
let _users = null;
-get_users = async function() {
+const get_users = async function() {
if (_users) {
return _users;
} else {
@@ -31,7 +31,7 @@ get_users = async function() {
}
exports.get_users = get_users;
-update_users = async function(users) {
+const update_users = async function(users) {
try {
await writeFile(passwd_file, JSON.stringify(users), 'utf8');
await chmod(passwd_file, 0600);
diff --git a/src/controllers/zt.js b/src/controllers/zt.js
index cf770ff..d01f415 100644
--- a/src/controllers/zt.js
+++ b/src/controllers/zt.js
@@ -1,6 +1,6 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
*/
@@ -8,9 +8,9 @@ const got = require('got');
const ipaddr = require('ip-address');
const token = require('./token');
-ZT_ADDR = process.env.ZT_ADDR || 'localhost:9993';
+const ZT_ADDR = process.env.ZT_ADDR || 'localhost:9993';
-init_options = async function() {
+const init_options = async function() {
let tok = null;
try {
@@ -29,16 +29,21 @@ init_options = async function() {
return options;
}
-get_zt_address = async function() {
+const get_zt_status = async function() {
const options = await init_options();
try {
const response = await got(ZT_ADDR + '/status', options);
- return response.body.address;
+ return response.body;
} catch(err) {
throw(err);
}
}
+exports.get_zt_status = get_zt_status;
+
+const get_zt_address = async function() {
+ return (await get_zt_status()).address;
+}
exports.get_zt_address = get_zt_address;
exports.network_list = async function() {
@@ -68,7 +73,7 @@ exports.network_list = async function() {
return networks;
}
-network_detail = async function(nwid) {
+const network_detail = async function(nwid) {
const options = await init_options();
try {
@@ -241,7 +246,7 @@ exports.members = async function(nwid) {
}
}
-member_detail = async function(nwid, id) {
+const member_detail = async function(nwid, id) {
const options = await init_options();
try {
@@ -303,3 +308,22 @@ exports.network_easy_setup = async function(nwid,
throw(err);
}
}
+
+exports.peers = async function() {
+ const options = await init_options();
+ const response = await got(ZT_ADDR + '/peer', options);
+ return response.body;
+}
+
+exports.peer = async function(id) {
+ const options = await init_options();
+ try {
+ const response = await got(ZT_ADDR + '/peer/' + id, options);
+ return response.body;
+ } catch (error) {
+ if (error instanceof got.HTTPError && error.statusCode == 404) {
+ return null;
+ }
+ throw error;
+ }
+}
diff --git a/src/package.json b/src/package.json
index bf03fd0..4af3b61 100644
--- a/src/package.json
+++ b/src/package.json
@@ -1,6 +1,6 @@
{
"name": "ztncui",
- "version": "0.6.6",
+ "version": "0.7.0",
"private": true,
"scripts": {
"start": "node ./bin/www",
@@ -19,7 +19,7 @@
"got": "^7.1.0",
"helmet": "^3.23.0",
"ip-address": "^5.8.9",
- "jquery": "^3.5.1",
+ "jquery": "~3.4.1",
"morgan": "~1.9.1",
"node-persist": "^2.1.0",
"pug": "^2.0.4",
@@ -32,7 +32,8 @@
"assets": [
"views/*",
"public/**/*",
- "etc/**/*"
+ "etc/**/*",
+ "node_modules/jquery/dist/jquery.min.js"
]
}
}
diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css
index a4c5c7b..8932fcc 100644
--- a/src/public/stylesheets/style.css
+++ b/src/public/stylesheets/style.css
@@ -1,5 +1,4 @@
html, body {
- height: 100%;
font: 16px "Lucida Grande", Helvetica, Arial, sans-serif;
}
diff --git a/src/routes/index.js b/src/routes/index.js
index b178ab1..cc255fb 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -1,6 +1,6 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
*/
@@ -10,8 +10,17 @@ const authenticate = auth.authenticate;
const restrict = auth.restrict;
const router = express.Router();
+/** Redirect logged user to controler page */
+function guest_only(req, res, next) {
+ if (req.session.user) {
+ res.redirect('/controller');
+ } else {
+ next();
+ }
+}
+
/* GET home page. */
-router.get('/', function(req, res, next) {
+router.get('/', guest_only, function(req, res, next) {
res.render('front_door', {title: 'ztncui'});
});
@@ -21,7 +30,7 @@ router.get('/logout', function(req, res) {
});
});
-router.get('/login', function(req, res) {
+router.get('/login', guest_only, function(req, res) {
let message = null;
if (req.session.error) {
if (req.session.error !== 'Access denied!') {
@@ -40,7 +49,7 @@ router.post('/login', async function(req, res) {
req.session.user = user;
req.session.success = 'Authenticated as ' + user.name;
if (user.pass_set) {
- res.redirect('/controller');
+ res.redirect(req.query.redirect || '/controller');
} else {
res.redirect('/users/' + user.name + '/password');
}
diff --git a/src/routes/users.js b/src/routes/users.js
index 11abd03..7fb18fe 100644
--- a/src/routes/users.js
+++ b/src/routes/users.js
@@ -1,6 +1,6 @@
/*
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 44603da..99ae096 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
*/
@@ -43,6 +43,9 @@ router.get('/network/:nwid/routes/:target_ip/:target_prefix/delete', restrict, n
// POST request for routes
router.post('/network/:nwid/routes', restrict, networkController.routes);
+// POST request for dns
+router.post('/network/:nwid/dns', restrict, networkController.dns);
+
// POST request for private
router.post('/network/:nwid/private', restrict, networkController.private);
diff --git a/src/views/controller_layout.pug b/src/views/controller_layout.pug
index 0bd051a..b0f72ee 100644
--- a/src/views/controller_layout.pug
+++ b/src/views/controller_layout.pug
@@ -1,38 +1,18 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends head_layout
-block body_content
- nav.navbar.navbar-inverse.navbar-fixed-top
- .container-fluid
- .navbar-header
- button.navbar-toggle(type='button' data-toggle='collapse' data-target='#BarNav')
- span.icon-bar
- span.icon-bar
- span.icon-bar
- a.navbar-brand(href='https://key-networks.com' target='_blank')
- img(src='/images/key-logo.svg' alt='Key Networks logo' height='25px' width='25px' style='display: inline')
- | Key Networks
- .collapse.navbar-collapse(id='BarNav')
- ul.nav.navbar-nav
- li(class=(navigate.active === 'controller_home'? 'active' : ''))
- a(href='/controller') Home
- li(class=(navigate.active === 'users'? 'active' : ''))
- a(href='/users') Users
- li(class=(navigate.active === 'networks'? 'active' : ''))
- a(href='/controller/networks') Networks
- li(class=(navigate.active === 'add_network'? 'active' : ''))
- a(href='/controller/network/create') Add network
- ul.nav.navbar-nav.navbar-right
- li
- a(href='/logout')
- span.glyphicon.glyphicon-log-out
- | Logout
+block nav_items
+ +nav_item('controller_home', 'Home', '/controller')
+ +nav_item('users', 'Users', '/users')
+ +nav_item('networks', 'Networks', '/controller/networks')
+ +nav_item('add_network', 'Add network', '/controller/network/create')
- .container(style='margin-top:50px')
+block body_content
+ .container(style='margin: 50px auto 20px')
.row
.col-sm-12
block content
diff --git a/src/views/dns.pug b/src/views/dns.pug
new file mode 100644
index 0000000..87b08a4
--- /dev/null
+++ b/src/views/dns.pug
@@ -0,0 +1,57 @@
+//-
+ ztncui - ZeroTier network controller UI
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
+ Licensed under GPLv3 - see LICENSE for details.
+
+extends network_layout
+
+block net_content
+ - const dns = network.dns || {};
+ if (!dns.domain && !(dns.servers && dns.servers.length > 0))
+ .row
+ .col-sm-12
+ b No DNS configuration on this network.
+ else
+ .row
+ .col-sm-2
+ b Domain:
+ .col-sm-10
+ p= dns.domain
+ .row
+ .col-sm-2
+ b Servers:
+ .col-sm-10
+ .row
+ each server in dns.servers
+ .col-sm-12= server
+
+ .row
+ .col-sm-12
+ h3 Change DNS configuration:
+
+ form(method='POST' action='')
+ .form-group.row
+ .col-sm-12
+ label(for='domain') Domain:
+ .col-sm-12
+ input#domain.form-control(type='text' name='domain' value=dns.domain)
+
+ .form-group.row
+ .col-sm-12
+ label(for='servers') Servers:
+ .col-sm-12
+ textarea#servers.form-control(type='text' name='servers' placeholder='(one IP address per line)')
+ = !dns.servers ? '' : dns.servers.join('\n')
+
+ .form-group.row
+ .col-sm-12
+ button.btn.btn-primary(type='submit') Submit
+ = ' '
+ a.btn.btn-default(href=('/controller/network/' + network.nwid) name='cancel' role='button') Cancel
+
+ if errors
+ .row
+ .col-sm-12
+ ul
+ for err in errors
+ li!= err.msg
diff --git a/src/views/front_door.pug b/src/views/front_door.pug
index e0f3a5b..eeebc37 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 635ed4c..c2be79c 100644
--- a/src/views/head_layout.pug
+++ b/src/views/head_layout.pug
@@ -1,8 +1,18 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
+mixin nav_item(name, displayName, href)
+ li(class=((navigate && navigate.active === name) ? 'active' : ''))
+ a(href=href) #{displayName}
+
+mixin json_value(value)
+ - if ((!!value ) && (value.constructor == Object || value.constructor == Array))
+ code(style='white-space: pre;')= JSON.stringify(value, null, 2)
+ - else
+ code= value
+
doctype html
html(lang='en')
head
@@ -11,7 +21,26 @@ html(lang='en')
meta(name='viewport', content='width=device-width, initial-scale=1')
link(rel='stylesheet', href='/bscss/bootstrap.min.css')
link(rel='stylesheet', href='/stylesheets/style.css')
- script(src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js')
+ script(src='/jqjs/jquery.min.js')
script(src='/bsjs/bootstrap.min.js')
body
+ nav.navbar.navbar-inverse.navbar-fixed-top
+ .container-fluid
+ .navbar-header
+ button.navbar-toggle(type='button' data-toggle='collapse' data-target='#BarNav')
+ span.icon-bar
+ span.icon-bar
+ span.icon-bar
+ a.navbar-brand(href='https://key-networks.com' target='_blank')
+ img(src='/images/key-logo.svg' alt='Key Networks logo' height='25px' width='25px' style='display: inline')
+ | Key Networks
+ .collapse.navbar-collapse(id='BarNav')
+ ul.nav.navbar-nav
+ block nav_items
+ ul.nav.navbar-nav.navbar-right
+ li
+ block nav_login
+ a(href='/logout')
+ span.glyphicon.glyphicon-log-out
+ | Logout
block body_content
diff --git a/src/views/index.pug b/src/views/index.pug
index e87c6de..6a77369 100644
--- a/src/views/index.pug
+++ b/src/views/index.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends controller_layout
@@ -10,14 +10,13 @@ block content
h2
a(href='https://zerotier.com' target='_blank') ZeroTier
- | network controller UI
-
- h3 Network controller details
+ | network controller UI by
+ a(href='https://key-networks.com' target='_blank') Key Networks
if error
b #{error}
else
- h4 This network controller has a ZeroTier address of #{zt_address}
-
+ h4 This network controller has a ZeroTier address of #{zt_status.address}
+ h4 ZeroTier version #{zt_status.version}
h4
a(href='/controller/networks') List all networks on this network controller
diff --git a/src/views/ipAssignmentPools.pug b/src/views/ipAssignmentPools.pug
index da45c21..7af58bf 100644
--- a/src/views/ipAssignmentPools.pug
+++ b/src/views/ipAssignmentPools.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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
index 519ca02..26c3ee1 100644
--- a/src/views/ipAssignments.pug
+++ b/src/views/ipAssignments.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout
diff --git a/src/views/login.pug b/src/views/login.pug
index 378a885..9a131ab 100644
--- a/src/views/login.pug
+++ b/src/views/login.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends login_layout
diff --git a/src/views/login_layout.pug b/src/views/login_layout.pug
index 9e4782c..a9fcb5c 100644
--- a/src/views/login_layout.pug
+++ b/src/views/login_layout.pug
@@ -1,28 +1,16 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends head_layout
-block body_content
- nav.navbar.navbar-inverse.navbar-fixed-top
- .container-fluid
- .navbar-header
- button.navbar-toggle(type='button' data-toggle='collapse' data-target='#BarNav')
- span.icon-bar
- span.icon-bar
- span.icon-bar
- a.navbar-brand(href='https://key-networks.com' target='_blank')
- img(src='/images/key-logo.svg' alt='Key Networks logo' height='25px' width='25px' style='display: inline')
- | Key Networks
- .collapse.navbar-collapse(id='BarNav')
- ul.nav.navbar-nav.navbar-right
- li
- a(href='/login')
- span.glyphicon.glyphicon-log-in
- | Login
+block nav_login
+ a(href='/login')
+ span.glyphicon.glyphicon-log-in
+ | Login
+block body_content
.container(style='margin-top:50px')
.row
.col-sm-12
diff --git a/src/views/member_delete.pug b/src/views/member_delete.pug
index ac187d1..e6001ea 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout
@@ -9,7 +9,7 @@ block net_content
if member.deleted
.alert.alert-success
strong #{member.name} (#{member.id}) was deleted
- a.btn.btn-default(href='../../members' name='networks' role='button') Members
+ a.btn.btn-default(href=('/controller/network/' + network.nwid + '#members') name='networks' role='button') Members
else
.alert.alert-info
@@ -21,7 +21,7 @@ block net_content
form(method='POST' action='')
button.btn.btn-primary(type='submit', name='delete') Delete #{member.name} (#{member.id})
= ' '
- a.btn.btn-default(href='/controller/network/' + network.nwid + '/members',
+ a.btn.btn-default(href='/controller/network/' + network.nwid + '#members',
name='cancel', role='button') Cancel
if errors
diff --git a/src/views/member_detail.pug b/src/views/member_detail.pug
index 4acfba2..8d1369a 100644
--- a/src/views/member_detail.pug
+++ b/src/views/member_detail.pug
@@ -1,32 +1,20 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout
block net_content
- h4 for member #{member.name} (#{member.address})
+ h4
+ | for member #{member.name} (#{member.address}) in network
each value, key in member
.row
.col-sm-2
- a(href= member.address + '/' + key) #{key}:
+ a(href=('/controller/network/' + member.nwid + '/member/' + member.address + '/' + key)) #{key}:
.col-sm-10
- - if ((!!value ) && (value.constructor == Object))
- p {
- each v2, k2 in value
- p #{k2}: #{v2},
- p }
- - else if ((!!value ) && (value.constructor == Array))
- p [
- each elem in value
- p {
- each v2, k2 in elem
- p #{k2}: #{v2},
- p }
- p ]
- - else
- | #{value}
+ +json_value(value)
- a.btn.btn-default(href='../members' name='networks' role='button') Members
+ a.btn.btn-default(href=('/controller/network/' + member.nwid + "#members") name='networks' role='button' style='margin-top: 10px;')
+ | Members
diff --git a/src/views/members.pug b/src/views/members.pug
deleted file mode 100644
index 0a56c20..0000000
--- a/src/views/members.pug
+++ /dev/null
@@ -1,76 +0,0 @@
-//-
- 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
- script.
- $(function() {
- $('.authCheck').on('click', function() {
- $.post('', {'id': this.value, 'auth': this.checked});
- });
- });
-
- $(function() {
- $('.bridgeCheck').on('click', function() {
- $.post('', {'id': this.value, 'activeBridge': this.checked});
- });
- });
-
- $(function() {
- $('.text').on('change', function() {
- $.post('', {'id': this.name, 'name': this.value});
- });
- });
-
- form(method='POST' action='')
- table.table.table-responsive.table-striped.table-hover
- tr
- td(width='3%')
- = ''
- td(width='20%')
- | Member name
- td(width='10%')
- | Member ID
- td(width='10%')
- | Authorized
- td(width='10%')
- | Active bridge
- td(width='47%')
- | IP assignment
- each member in members
- tr
- - let url = '/controller/network/' + network.nwid + '/member/' + member.id
- td
- a(href=url + '/delete')
- i.glyphicon.glyphicon-trash
- td
- input.form-control.text(type='text' name=member.id value=member.name)
- td
- a(href=url) #{member.id}
- td
- input.authCheck(type='checkbox' value=member.id checked=(member.authorized? true : false))
- td
- input.bridgeCheck(type='checkbox' value=member.id checked=(member.activeBridge? true : false))
- td
- each ipAssignment in member.ipAssignments
- 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
- strong There are no members on this network - invite users to join #{network.nwid}
-
- a.btn.btn-default(href='' name='refresh' role='button') Refresh
-
- if errors
- ul
- for err in errors
- li!= err.msg
diff --git a/src/views/name.pug b/src/views/name.pug
deleted file mode 100644
index 8ce23e4..0000000
--- a/src/views/name.pug
+++ /dev/null
@@ -1,28 +0,0 @@
-//-
- 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
- form(method='POST' action='')
-
- .form-group.row
- .col-sm-2
- label(for='name') Network name:
- .col-sm-10
- input#name.form-control(type='text' name='name' placeholder='New network name' value=(undefined===network.name? '' : network.name))
-
- .form-group.row
- .col-sm-12
- button.btn.btn-primary(type='submit') Submit
- = ' '
- a.btn.btn-default(href='/controller/networks' name='cancel' role='button') Cancel
-
- if errors
- .row
- .col-sm-12
- ul
- for err in errors
- li!= err.msg
diff --git a/src/views/network_create.pug b/src/views/network_create.pug
index c5fb323..2246548 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 5d8766d..00bba12 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 7a4bc5b..2517a32 100644
--- a/src/views/network_detail.pug
+++ b/src/views/network_detail.pug
@@ -1,44 +1,159 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout
+//- Don't display that title
+block title
+
+//- Replace the network title with the editable one
+block network_title
+ h2
+ | Network
+ a#change-name(href='#')
+ span#name= network.name
+ i.glyphicon.glyphicon-pencil(style='font-size: 20px;')
+ input#name-input.form-control(type='text' style='width: 200px; display: none;')
+ | (#{network.nwid}):
+ script.
+ $(function() {
+ var nwurl = '/controller/network/#{network.nwid}';
+ var name = !{JSON.stringify(network.name)};
+
+ function toggleNameEditor(show) {
+ $('#change-name').css('display', !show ? '' : 'none');
+ $('#name-input').css('display', show ? 'inline-block' : 'none');
+ }
+
+ function submit() {
+ var newName = $('#name-input').val();
+ if (newName != name) {
+ name = newName;
+ $.post(nwurl + '/name', {'name': name})
+ .done(function () {
+ $('#name').text(newName);
+ });
+ }
+ toggleNameEditor(false);
+ }
+
+ $('#change-name').on('click', function() {
+ toggleNameEditor(true);
+ $('#name-input').val(name);
+ $('#name-input').focus();
+ });
+ $('#name-input').on('focusout', submit);
+ $('#name-input').keypress(function (e) {
+ if (e.which == 13) submit();
+ });
+ });
+
block net_content
- if error
- b #{error}
- else
- - if (members !== undefined)
- h3 Members
- each value, key in members
- .row
- .col-sm-2
- a(href= network.nwid + '/member/' + key) #{key}
- .col-sm-10
- | revision: #{value}
+ - const nwurl = '/controller/network/' + network.nwid;
- h3= title
- each value, key in network
- .row
- .col-sm-2
- a(href= network.nwid + '/' + key) #{key}:
- .col-sm-10
- - if ((!!value ) && (value.constructor == Object))
- p {
- each v2, k2 in value
- p #{k2}: #{v2},
- p }
- - else if ((!!value ) && (value.constructor == Array))
- p [
- each elem in value
- p {
- - if (!!elem)
- each v2, k2 in elem
- p #{k2}: #{v2},
- p },
- p ]
- - else
- | #{value}
+ a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/private') role='button')
+ = network.private ? "Private" : "Public"
+ a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/easy') role='button') Easy setup
+ a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/routes') role='button') Routes
+ a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/ipAssignmentPools') role='button') Assignment Pools
+ a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/v4AssignMode') role='button') IPv4 Assign Mode
+ a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/v6AssignMode') role='button') IPv6 Assign Mode
+ a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/dns') role='button') DNS
- a.btn.btn-default(href='/controller/networks' name='networks' role='button') Networks
+ if (members !== undefined)
+ script.
+ $(function() {
+ const url = "#{nwurl}/members";
+ $('.authCheck').on('click', function() {
+ $.post(url, {'id': this.value, 'auth': this.checked});
+ });
+ $('.bridgeCheck').on('click', function() {
+ $.post(url, {'id': this.value, 'activeBridge': this.checked});
+ });
+ $('.text').on('change', function() {
+ $.post(url, {'id': this.name, 'name': this.value});
+ });
+ });
+ h3#members Members (#{members.length})
+ form(method='POST' action='')
+ table.table.table-responsive.table-striped.table-hover
+ tr
+ td(width='3%')
+ = ''
+ td(width='20%')
+ | Member name
+ td(width='10%')
+ | Member ID
+ td(width='10%')
+ | Authorized
+ td(width='10%')
+ | Active bridge
+ td(width='17%')
+ | IP assignment
+ td(width='17%')
+ | Peer status
+ td(width='13%')
+ | Peer address / latency
+ each member in members
+ - const peer = member.peer;
+ tr
+ - const url = nwurl + '/member/' + member.id
+ td
+ a(href=url + '/delete')
+ i.glyphicon.glyphicon-trash
+ td
+ input.form-control.text(type='text' name=member.id value=member.name)
+ td
+ a(href=url) #{member.id}
+ td
+ input.authCheck(type='checkbox' value=member.id checked=(member.authorized? true : false))
+ td
+ input.bridgeCheck(type='checkbox' value=member.id checked=(member.activeBridge? true : false))
+ td
+ each ipAssignment in member.ipAssignments
+ a(href=nwurl + '/member/' + member.id + '/ipAssignments')
+ each digit in ipAssignment
+ = digit
+ = ' '
+ else
+ a(href=nwurl + '/member/' + member.id + '/ipAssignments')
+ | IP assignment
+ td
+ if (peer && peer.latency != -1 && peer.versionMajor != -1)
+ if (peer.latency != -1)
+ span(style='color: green;')
+ | ONLINE (v#{peer.version})
+ else
+ span(style='color: orange;')
+ | RELAY (v#{peer.version})
+ else if (member.id == zt_address)
+ span(style='color: green;') CONTROLLER
+ else
+ span(style='color: red;') OFFLINE
+ td
+ if (peer)
+ each path in peer.paths
+ - const [ip, port] = path.address.split('/');
+ = ip
+ span(style='color: gray;') /#{port}
+ = ' '
+ if (peer.latency != -1)
+ br
+ | (#{peer.latency} ms)
+ else
+ .alert.alert-info
+ strong There are no members on this network - invite users to join #{network.nwid}
+
+ a.btn.btn-default(href='' name='refresh' role='button') Refresh
+
+ h3#detail Detail for network
+ each value, key in network
+ .row(style='margin: 5px 0;')
+ .col-sm-2
+ a(href= network.nwid + '/' + key) #{key}:
+ .col-sm-10
+ +json_value(value)
+
+ a.btn.btn-default(href='/controller/networks' name='networks' role='button' style='margin-top: 10px;') Networks
diff --git a/src/views/network_easy.pug b/src/views/network_easy.pug
index ebe0151..209428e 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout
@@ -80,7 +80,7 @@ block net_content
.form-group(style='padding-top: 10px')
button.btn.btn-primary(type='submit') Submit
= ' '
- a.btn.btn-default(href='/controller/networks' name='cancel' role='button') Cancel
+ a.btn.btn-default(href=('/controller/network/' + network.nwid) name='cancel' role='button') Cancel
if errors
ul
diff --git a/src/views/network_layout.pug b/src/views/network_layout.pug
index 60f32a5..2004c85 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends controller_layout
@@ -11,10 +11,14 @@ block content
else
.row
.col-sm-10
- h2
- a(href='/controller/network/' + network.nwid) #{network.name}
- | (#{network.nwid}):
- h3= title
+ block network_title
+ h2
+ | Network
+ a(href='/controller/network/' + network.nwid) #{network.name}
+ | (#{network.nwid}):
+ block title
+ if title
+ h3= title
.col-sm-2
h2.right
diff --git a/src/views/networks.pug b/src/views/networks.pug
index e4cc173..6a9e5d4 100644
--- a/src/views/networks.pug
+++ b/src/views/networks.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends controller_layout
@@ -11,36 +11,36 @@ block content
if error
b #{error}
else
-
table.table.table-responsive.table-striped.table-hover
tr
th(width='3%')
= ''
th(width='20%')
| Network name
- th(width='10%')
+ th(width='20%')
| Network ID
- th(width='7%')
+ th(width='8%')
= ''
- th(width='10%')
+ th(width='12%')
= ''
- th(width='50%')
+ th(width='37%')
= ''
each network in networks
+ - const nwurl = '/controller/network/' + network.nwid;
tr
td
- a(href='/controller/network/' + network.nwid + '/delete')
+ a(href=nwurl + '/delete')
i.glyphicon.glyphicon-trash
td
- a(href='/controller/network/' + network.nwid + '/name') #{network.name}
+ a(href=nwurl) #{network.name}
td
= network.nwid
td
- a(href='/controller/network/' + network.nwid) detail
+ a(href=nwurl) detail
td
- a(href='/controller/network/' + network.nwid + '/easy') easy setup
+ a(href=nwurl + '/easy') easy setup
td
- a(href='/controller/network/' + network.nwid + '/members') members
+ a(href=nwurl + "#members") members
else
.alert.alert-info
diff --git a/src/views/not_implemented.pug b/src/views/not_implemented.pug
index e6997c7..6023c63 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 1eb850c..09b9369 100644
--- a/src/views/password.pug
+++ b/src/views/password.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends users_layout
diff --git a/src/views/private.pug b/src/views/private.pug
index 0b87bfb..e490209 100644
--- a/src/views/private.pug
+++ b/src/views/private.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout
diff --git a/src/views/routes.pug b/src/views/routes.pug
index 686909f..d80c5e4 100644
--- a/src/views/routes.pug
+++ b/src/views/routes.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout
@@ -27,22 +27,22 @@ block net_content
form(method='POST' action='/controller/network/' + network.nwid + '/routes')
.form-group.row
- .col-sm-2
+ .col-sm-12
label(for='target') Target:
.col-sm-12
input#target.form-control(type='text' name='target' placeholder='e.g. 10.11.12.0/24' value=(undefined===route? '' : route.target))
.form-group.row
- .col-sm-2
+ .col-sm-12
label(for='via') Gateway:
.col-sm-12
input#via.form-control(type='text' name='via' placeholder='e.g. 172.16.2.1 or leave blank if the target is the ZT network' value=(undefined===route? '' : route.via))
.form-group.row
- .col-sm-2
+ .col-sm-12
button.btn.btn-primary(type='submit') Submit
= ' '
- a.btn.btn-default(href='/controller/networks' name='cancel' role='button') Cancel
+ a.btn.btn-default(href=('/controller/network/' + network.nwid) name='cancel' role='button') Cancel
if errors
.row
diff --git a/src/views/user_delete.pug b/src/views/user_delete.pug
index 14c1116..d9d74b6 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-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 5a6d247..5cef684 100644
--- a/src/views/users.pug
+++ b/src/views/users.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 e2508ba..bf5e73d 100644
--- a/src/views/users_layout.pug
+++ b/src/views/users_layout.pug
@@ -1,35 +1,17 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends head_layout
-block body_content
- nav.navbar.navbar-inverse.navbar-fixed-top
- .container-fluid
- .navbar-header
- button.navbar-toggle(type='button' data-toggle='collapse' data-target='#BarNav')
- span.icon-bar
- span.icon-bar
- span.icon-bar
- a.navbar-brand(href='https://key-networks.com' target='_blank')
- img(src='/images/key-logo.svg' alt='Key Networks logo' height='25px' width='25px' style='display: inline')
- | Key Networks
- .collapse.navbar-collapse(id='BarNav')
- ul.nav.navbar-nav
- li(class=(navigate.active === 'home'? 'active' : ''))
- a(href='/controller') Home
- li(class=(navigate.active === 'users'? 'active' : ''))
- a(href='/users') Users
- li(class=(navigate.active === 'create_user'? 'active' : ''))
- a(href='/users/create') Create user
- ul.nav.navbar-nav.navbar-right
- li
- a(href='/logout')
- span.glyphicon.glyphicon-log-out
- | Logout
+block nav_items
+ +nav_item('controller_home', 'Home', '/controller')
+ +nav_item('users', 'Users', '/users')
+ +nav_item('networks', 'Networks', '/controller/networks')
+ +nav_item('create_user', 'Create user', '/users/create')
+block body_content
.container(style='margin-top:50px')
.row
.col-sm-12
diff --git a/src/views/v4AssignMode.pug b/src/views/v4AssignMode.pug
index 41d2ee6..2aabf03 100644
--- a/src/views/v4AssignMode.pug
+++ b/src/views/v4AssignMode.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 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 490e782..4833ac6 100644
--- a/src/views/v6AssignMode.pug
+++ b/src/views/v6AssignMode.pug
@@ -1,6 +1,6 @@
//-
ztncui - ZeroTier network controller UI
- Copyright (C) 2017-2018 Key Networks (https://key-networks.com)
+ Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
Licensed under GPLv3 - see LICENSE for details.
extends network_layout