Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
*.swp
Release/
Staging/

node_modules/
39 changes: 30 additions & 9 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,27 @@ const zt_controller = require('./routes/zt_controller');

const app = express();

// Base path support for reverse proxy subpaths (e.g., Nginx location)
// Example: BASE_PATH=/ztncui -> app is served under http(s)://host/ztncui
const rawBasePath = process.env.BASE_PATH || '';
const basePath = (function normalizeBasePath(p) {
if (!p) return '';
if (!p.startsWith('/')) p = '/' + p;
// trim trailing slash except root
if (p.length > 1 && p.endsWith('/')) p = p.slice(0, -1);
return p;
})(rawBasePath);
app.locals.basePath = basePath;

const session_secret = Math.random().toString(36).substring(2,12);

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(helmet());
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
// Mount favicon and static assets under base path
app.use(basePath, favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
Expand All @@ -40,15 +53,23 @@ app.use(session({
}));
app.use(expressValidator());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/fonts', express.static(path.join(__dirname, 'node_modules/bootstrap/fonts')));
app.use('/bscss', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/css')));
app.use('/jqjs', express.static(path.join(__dirname, 'node_modules/jquery/dist')));
app.use('/bsjs', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/js')));
// Inject basePath into templates
app.use(function(req, res, next) {
res.locals.basePath = basePath;
next();
});

// Static mounts under base path
app.use(basePath, express.static(path.join(__dirname, 'public')));
app.use(basePath + '/fonts', express.static(path.join(__dirname, 'node_modules/bootstrap/fonts')));
app.use(basePath + '/bscss', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/css')));
app.use(basePath + '/jqjs', express.static(path.join(__dirname, 'node_modules/jquery/dist')));
app.use(basePath + '/bsjs', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/js')));

app.use('/', index);
app.use('/users', users);
app.use('/controller', zt_controller);
// Route mounts under base path
app.use(basePath + '/', index);
app.use(basePath + '/users', users);
app.use(basePath + '/controller', zt_controller);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
Expand Down
3 changes: 2 additions & 1 deletion src/controllers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ exports.restrict = function(req, res, next) {
if (req.session.user) {
next();
} else {
const basePath = req.app && req.app.locals ? (req.app.locals.basePath || '') : '';
req.session.error = 'Access denied!';
res.redirect('/login?redirect=' + encodeURIComponent(req.originalUrl));
res.redirect(basePath + '/login?redirect=' + encodeURIComponent(req.originalUrl));
}
}
91 changes: 64 additions & 27 deletions src/controllers/networkController.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ const util = require('util');

storage.initSync({dir: 'etc/storage'});

// Unified helper to get basePath consistently across the controller
function getBasePath(req) {
return (req.app && req.app.locals ? (req.app.locals.basePath || '') : '');
}

async function get_network_with_members(nwid) {
const [network, peers, members] = await Promise.all([
zt.network_detail(nwid),
Expand Down Expand Up @@ -92,10 +97,11 @@ exports.network_list = async function(req, res) {

// Display detail page for specific network
exports.network_detail = async function(req, res) {
const basePath = getBasePath(req);
const navigate =
{
active: 'networks',
whence: '/controller/networks'
whence: basePath + '/controller/networks'
}

try {
Expand Down Expand Up @@ -144,7 +150,8 @@ exports.network_create_post = async function(req, res) {
} else {
try {
const network = await zt.network_create(name);
res.redirect('/controller/network/' + network.nwid);
const basePath = getBasePath(req);
res.redirect(basePath + '/controller/network/' + network.nwid);
} catch (err) {
res.render('network_detail', {title: 'Create Network - error', navigate: navigate, error: 'Error creating network ' + name.name});
}
Expand All @@ -153,10 +160,11 @@ exports.network_create_post = async function(req, res) {

// Display Network delete form on GET
exports.network_delete_get = async function(req, res) {
const basePath = getBasePath(req);
const navigate =
{
active: 'networks',
whence: '/controller/networks'
whence: basePath + '/controller/networks'
}

try {
Expand All @@ -170,10 +178,11 @@ exports.network_delete_get = async function(req, res) {

// Handle Network delete on POST
exports.network_delete_post = async function(req, res) {
const basePath = getBasePath(req);
const navigate =
{
active: 'networks',
whence: '/controller/networks'
whence: basePath + '/controller/networks'
}

try {
Expand All @@ -194,7 +203,8 @@ exports.network_object = async function(req, res) {

try {
const network = await zt.network_detail(req.params.nwid);
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render(req.params.object, {title: req.params.object, navigate: navigate, network: network}, function(err, html) {
if (err) {
if (err.message.indexOf('Failed to lookup view') !== -1 ) {
Expand All @@ -211,10 +221,11 @@ exports.network_object = async function(req, res) {

// Handle Network rename form on POST
exports.name = async function(req, res) {
const basePath = getBasePath(req);
const navigate =
{
active: 'networks',
whence: '/controller/networks'
whence: basePath + '/controller/networks'
}

req.checkBody('name', 'Network name required').notEmpty();
Expand All @@ -234,7 +245,7 @@ exports.name = async function(req, res) {
console.error("Error renaming network " + req.params.nwid, err);
}
}
res.redirect('/controller/network/' + req.params.nwid);
res.redirect(basePath + '/controller/network/' + req.params.nwid);
};

// ipAssignmentPools POST
Expand Down Expand Up @@ -265,15 +276,17 @@ exports.ipAssignmentPools = async function(req, res) {
if (errors) {
try {
const network = await zt.network_detail(req.params.nwid);
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, ipAssignmentPool: ipAssignmentPool, network: network, errors: errors});
} catch (err) {
res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, error: 'Error resolving network detail for network ' + req.params.nwid + ': ' + err});
}
} else {
try {
const network = await zt.ipAssignmentPools(req.params.nwid, ipAssignmentPool, 'add');
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, ipAssignmentPool: ipAssignmentPool, network: network});
} catch (err) {
res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, error: 'Error applying IP Assignment Pools for network ' + req.params.nwid + ': ' + err});
Expand Down Expand Up @@ -332,15 +345,17 @@ exports.routes = async function (req, res) {
if (errors) {
try {
const network = await zt.network_detail(req.params.nwid);
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('routes', {title: 'routes', navigate: navigate, route: route, network: network, errors: errors});
} catch (err) {
res.render('routes', {title: 'routes', navigate: navigate, error: 'Error resolving network detail'});
}
} else {
try {
const network = await zt.routes(req.params.nwid, route, 'add');
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('routes', {title: 'routes', navigate: navigate, route: route, network: network});
} catch (err) {
res.render('routes', {title: 'routes', navigate: navigate, error: 'Error adding route for network ' + req.params.nwid + ': ' + err});
Expand All @@ -366,7 +381,8 @@ exports.route_delete = async function (req, res) {

try {
const network = await zt.routes(req.params.nwid, route, 'delete');
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('routes', {title: 'routes', navigate: navigate, route: route, network: network});
} catch (err) {
res.render('routes', {title: 'routes', navigate: navigate, error: 'Error deleting route for network ' + req.params.nwid + ': ' + err});
Expand All @@ -390,7 +406,8 @@ exports.ipAssignmentPool_delete = async function (req, res) {

try {
const network = await zt.ipAssignmentPools(req.params.nwid, ipAssignmentPool, 'delete');
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, ipAssignmentPool: ipAssignmentPool, network: network});
} catch (err) {
res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, error: 'Error deleting IP Assignment Pool for network ' + req.params.nwid + ': ' + err});
Expand All @@ -412,7 +429,8 @@ exports.private = async function (req, res) {

try {
const network = await zt.network_object(req.params.nwid, private);
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('private', {title: 'private', navigate: navigate, network: network});
} catch (err) {
res.render('private', {title: 'private', navigate: navigate, error: 'Error applying private for network ' + req.params.nwid + ': ' + err});
Expand All @@ -434,7 +452,8 @@ exports.v4AssignMode = async function (req, res) {

try {
const network = await zt.network_object(req.params.nwid, v4AssignMode);
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('v4AssignMode', {title: 'v4AssignMode', navigate: navigate, network: network});
} catch (err) {
res.render('v4AssignMode', {title: 'v4AssignMode', navigate: navigate, error: 'Error applying v4AssignMode for network ' + req.params.nwid + ': ' + err});
Expand All @@ -461,7 +480,8 @@ exports.v6AssignMode = async function (req, res) {

try {
const network = await zt.network_object(req.params.nwid, v6AssignMode);
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('v6AssignMode', {title: 'v6AssignMode', navigate: navigate, network: network});
} catch (err) {
res.render('v6AssignMode', {title: 'v6AssignMode', navigate: navigate, error: 'Error applying v6AssignMode for network ' + req.params.nwid + ': ' + err});
Expand Down Expand Up @@ -490,7 +510,8 @@ exports.dns = async function (req, res) {

try {
const network = await zt.network_object(req.params.nwid, dns);
navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/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});
Expand All @@ -507,7 +528,8 @@ exports.member_detail = async function(req, res) {

try {
const {network, member} = await get_network_member(req.params.nwid, req.params.id);
navigate.whence = '/controller/network/' + network.nwid + '#members';
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid + '#members';
res.render('member_detail', {title: 'Network member detail', navigate: navigate, network: network, member: member});
} catch (err) {
console.error(err);
Expand All @@ -525,7 +547,8 @@ exports.member_object = async function(req, res) {

try {
const {network, member} = await get_network_member(req.params.nwid, req.params.id);
navigate.whence = '/controller/network/' + network.nwid + '#members';
const basePath = getBasePath(req);
navigate.whence = basePath + '/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 ) {
Expand All @@ -542,10 +565,11 @@ exports.member_object = async function(req, res) {

// Easy network setup GET
exports.easy_get = async function(req, res) {
const basePath = getBasePath(req);
const navigate =
{
active: 'networks',
whence: '/controller/network/' + req.params.nwid
whence: basePath + '/controller/network/' + req.params.nwid
}

try {
Expand All @@ -558,10 +582,11 @@ exports.easy_get = async function(req, res) {

// Easy network setup POST
exports.easy_post = async function(req, res) {
const basePath = getBasePath(req);
const navigate =
{
active: 'networks',
whence: '/controller/networks'
whence: basePath + '/controller/networks'
}

req.checkBody('networkCIDR', 'Network address is required').notEmpty();
Expand Down Expand Up @@ -631,7 +656,7 @@ exports.members = async function(req, res) {
const navigate =
{
active: 'networks',
whence: '/controller/networks'
whence: getBasePath(req) + '/controller/networks'
}

let errors = null;
Expand Down Expand Up @@ -694,8 +719,17 @@ exports.members = async function(req, res) {
}
}
}

// Respond based on validation result
if (errors) {
return res.status(400).json({ errors });
}

// No content needed for AJAX updates
return res.sendStatus(204);
} else { // GET
res.redirect("/controller/network/" + req.params.nwid + "#members");
const basePath = getBasePath(req);
res.redirect(basePath + "/controller/network/" + req.params.nwid + "#members");
}
}

Expand All @@ -722,7 +756,8 @@ exports.member_delete = async function(req, res) {
}
member.name = name || '';

navigate.whence = '/controller/network/' + network.nwid;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;
res.render('member_delete', {title: 'Delete member from ' + network.name,
navigate: navigate, network: network, member: member});
} catch (err) {
Expand All @@ -743,12 +778,13 @@ 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;
const basePath = getBasePath(req);
navigate.whence = basePath + '/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);
res.redirect('/controller/network/' + network.nwid + '/member/' +
res.redirect(basePath + '/controller/network/' + network.nwid + '/member/' +
member.id + '/ipAssignments');
}
res.render('ipAssignments', {title: 'ipAssignments ' + network.name,
Expand Down Expand Up @@ -802,7 +838,8 @@ 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;
const basePath = getBasePath(req);
navigate.whence = basePath + '/controller/network/' + network.nwid;

if (!errors) {
member = await zt.ipAssignmentAdd(network.nwid, member.id, ipAssignment);
Expand Down
3 changes: 2 additions & 1 deletion src/controllers/usersController.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ exports.user_create_post = async function(req, res) {
active: 'create_user',
}

res.redirect(307, '/users/' + req.body.username + '/password');
const basePath = (res && res.locals && res.locals.basePath) ? res.locals.basePath : '';
res.redirect(307, basePath + '/users/' + req.body.username + '/password');
}

exports.user_delete = async function(req, res) {
Expand Down
Loading