mirror of
https://github.com/key-networks/ztncui.git
synced 2024-08-31 04:28:00 +00:00
Some changes, including DNS support (#57)
* remove CDN * Add a link to "members" in the network detail page * Show object values as JSON * remove TLS options * Minor style improvements * Show object values as JSON (member_detail) * Add missing 'const' * Navbar height fix * Merge jQuery ready functions * Change brand * Merge network pages (name, members, detail) into single page and... * Show ZT version on controller index page * Show count of members * Use <code> tag to display JSON data * Fix error in some "error" pages caused by missing "navigate" when rendering nav items, use pug mixin to render nav items. * Adjust column widths of network list * Refactor: move duplicated nav code to `head_layout` * Remove some debug logging code * Get network members detail parallelly * Add missing frontend script for members * Revert "Change brand" * Remove "members" and "name" pages which are merged into "detail" * Add DNS support * Trivial changes (table width etc.) * Don't try to read TLS cert files when not using HTTPS * Validate DNS IP * Downgrade jquery to 3.4.1 to fix nav bar collapse * Revert "Navbar height fix" This reverts commit 8edaa9aa81b3bf56e3d3ed52cb2fd33c9cb3c69d, which break the nav item height on mobile. * Add missing margin for some buttons * Display current DNS configuration above inputs * Change network rename UI/UX * Includes 'jquery.min.js' in pkg * Improve JSON value rendering * Get peer status of network members * Display members with peer status * Show controller itself as "CONTROLLER" * Display peer address * Improve login redirection * pr57: Doc updates; version bump * pr57: Year update Co-authored-by: Key Networks <34238649+key-networks@users.noreply.github.com>
This commit is contained in:
parent
ce4b0e6d79
commit
1295dd1d07
40
README.md
40
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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
src/bin/www
10
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) {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
html, body {
|
||||
height: 100%;
|
||||
font: 16px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
57
src/views/dns.pug
Normal file
57
src/views/dns.pug
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 <b>#{zt_address}</b>
|
||||
|
||||
h4 This network controller has a ZeroTier address of <b>#{zt_status.address}</b>
|
||||
h4 ZeroTier version <b>#{zt_status.version}</b>
|
||||
h4
|
||||
a(href='/controller/networks') List all networks on this network controller
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user