mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-06-18 18:16:26 +00:00
Initial commit
This commit is contained in:
5
manager/src/frontend/js/app/access/empty.ejs
Normal file
5
manager/src/frontend/js/app/access/empty.ejs
Normal file
@ -0,0 +1,5 @@
|
||||
<td colspan="10" class="text-center">
|
||||
<br><br>
|
||||
<p>It looks like there are no access lists configured.</p>
|
||||
<p><button type="button" class="btn btn-sm btn-success">Create your first Access List</button></p>
|
||||
</td>
|
23
manager/src/frontend/js/app/access/empty.js
Normal file
23
manager/src/frontend/js/app/access/empty.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./empty.ejs');
|
||||
const AccessModel = require('../../models/access');
|
||||
const Controller = require('../controller');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
create: 'button'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.create': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showAccessListForm(new AccessModel.Model);
|
||||
}
|
||||
}
|
||||
});
|
11
manager/src/frontend/js/app/access/main.ejs
Normal file
11
manager/src/frontend/js/app/access/main.ejs
Normal file
@ -0,0 +1,11 @@
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<th>Access List Name</th>
|
||||
<th>User Count</th>
|
||||
<th>Host Count</th>
|
||||
<th class="text-right"><button type="button" class="btn btn-xs btn-info">Create Access List</button></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
||||
</table>
|
62
manager/src/frontend/js/app/access/main.js
Normal file
62
manager/src/frontend/js/app/access/main.js
Normal file
@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const Api = require('../api');
|
||||
const template = require('./main.ejs');
|
||||
const Controller = require('../controller');
|
||||
const RowView = require('./row');
|
||||
const AccessListModel = require('../../models/access');
|
||||
const EmptyView = require('./empty');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
childView: RowView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
id: 'access',
|
||||
|
||||
regions: {
|
||||
list_region: {
|
||||
el: 'tbody',
|
||||
replaceElement: true
|
||||
}
|
||||
},
|
||||
|
||||
ui: {
|
||||
'create': 'th button'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.create': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showAccessListForm(new AccessListModel.Model);
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
Api.Access.getAll()
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
view.showChildView('list_region', new TableBody({
|
||||
collection: new AccessListModel.Collection(response)
|
||||
}));
|
||||
} else {
|
||||
view.showChildView('list_region', new EmptyView());
|
||||
}
|
||||
|
||||
view.trigger('loaded');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
Controller.showError(err, 'Could not fetch Access Lists');
|
||||
view.trigger('loaded');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
7
manager/src/frontend/js/app/access/row.ejs
Normal file
7
manager/src/frontend/js/app/access/row.ejs
Normal file
@ -0,0 +1,7 @@
|
||||
<td><%- name %></td>
|
||||
<td><%- items.length %></td>
|
||||
<td><%- hosts.length %></td>
|
||||
<td class="text-right">
|
||||
<button type="button" class="btn btn-default btn-xs edit" title="Edit"><i class="fa fa-pencil" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn btn-default btn-xs delete" title="Delete"><i class="fa fa-times" aria-hidden="true"></i></button>
|
||||
</td>
|
32
manager/src/frontend/js/app/access/row.js
Normal file
32
manager/src/frontend/js/app/access/row.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./row.ejs');
|
||||
const Controller = require('../controller');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
edit: 'button.edit',
|
||||
delete: 'button.delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showAccessListForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showDeleteAccessList(this.model);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
24
manager/src/frontend/js/app/access_list/delete.ejs
Normal file
24
manager/src/frontend/js/app/access_list/delete.ejs
Normal file
@ -0,0 +1,24 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="form-horizontal">
|
||||
<div class="modal-header text-left">
|
||||
<h4 class="modal-title">Delete Access List</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure? You cannot undo this.</p>
|
||||
<% if (hosts && hosts.length) { %>
|
||||
<p>The following Hosts are using this Access List and will become publicly available upon deletion:</p>
|
||||
<ul>
|
||||
<% _.map(hosts, function (host) { %>
|
||||
<li><%- host.hostname %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger delete">Yes I'm Sure</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
35
manager/src/frontend/js/app/access_list/delete.js
Normal file
35
manager/src/frontend/js/app/access_list/delete.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./delete.ejs');
|
||||
const Controller = require('../controller');
|
||||
const Api = require('../api');
|
||||
const App = require('../main');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
buttons: 'form button',
|
||||
delete: 'button.delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
|
||||
Api.Access.delete(this.model.get('_id'))
|
||||
.then((/*result*/) => {
|
||||
App.UI.closeModal();
|
||||
Controller.showAccess();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
30
manager/src/frontend/js/app/access_list/form.ejs
Normal file
30
manager/src/frontend/js/app/access_list/form.ejs
Normal file
@ -0,0 +1,30 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form>
|
||||
<div class="modal-header text-left">
|
||||
<h4 class="modal-title"><% if (typeof _id !== 'undefined') { %>Edit<% } else { %>Create<% } %> Access List</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="list_name">List Name</label>
|
||||
<input type="text" class="form-control" placeholder="Cool People" name="name" id="list_name" value="<%- name %>" required>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<strong>Username</strong>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<strong>Password</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="items"><!-- items --></div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-success save">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
95
manager/src/frontend/js/app/access_list/form.js
Normal file
95
manager/src/frontend/js/app/access_list/form.js
Normal file
@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const _ = require('lodash');
|
||||
const template = require('./form.ejs');
|
||||
const Controller = require('../controller');
|
||||
const Api = require('../api');
|
||||
const App = require('../main');
|
||||
const ItemView = require('./item');
|
||||
const AccessItemModel = require('../../models/access_item');
|
||||
|
||||
require('jquery-serializejson');
|
||||
|
||||
const ItemsView = Mn.CollectionView.extend({
|
||||
childView: ItemView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
id: 'access-list-form',
|
||||
|
||||
ui: {
|
||||
items_region: '.items',
|
||||
form: 'form',
|
||||
buttons: 'form button'
|
||||
},
|
||||
|
||||
regions: {
|
||||
items_region: '@ui.items_region'
|
||||
},
|
||||
|
||||
events: {
|
||||
'submit @ui.form': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
let form_data = this.ui.form.serializeJSON();
|
||||
let items_data = [];
|
||||
|
||||
_.map(form_data.username, (val, idx) => {
|
||||
if (val.trim().length) {
|
||||
items_data.push({
|
||||
username: val.trim(),
|
||||
password: form_data.password[idx]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!items_data.length) {
|
||||
alert('You must specify at least 1 Username and Password combination');
|
||||
return;
|
||||
}
|
||||
|
||||
let data = {
|
||||
name: form_data.name,
|
||||
items: items_data
|
||||
};
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
let method = Api.Access.create;
|
||||
|
||||
if (this.model.get('_id')) {
|
||||
// edit
|
||||
method = Api.Access.update;
|
||||
data._id = this.model.get('_id');
|
||||
}
|
||||
|
||||
method(data)
|
||||
.then((/*result*/) => {
|
||||
App.UI.closeModal();
|
||||
Controller.showAccess();
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let items = this.model.get('items');
|
||||
|
||||
// Add empty items to the end of the list. This is cheating but hey I don't have the time to do it right
|
||||
let items_to_add = 5 - items.length;
|
||||
if (items_to_add) {
|
||||
for (let i = 0; i < items_to_add; i++) {
|
||||
items.push({});
|
||||
}
|
||||
}
|
||||
|
||||
this.showChildView('items_region', new ItemsView({
|
||||
collection: new AccessItemModel.Collection(items)
|
||||
}));
|
||||
}
|
||||
});
|
8
manager/src/frontend/js/app/access_list/item.ejs
Normal file
8
manager/src/frontend/js/app/access_list/item.ejs
Normal file
@ -0,0 +1,8 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" placeholder="" name="username[]" value="<%- typeof username !== 'undefined' ? username : '' %>">
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<input type="password" class="form-control" placeholder="<%- typeof hint !== 'undefined' ? hint : '' %>" name="password[]" value="">
|
||||
</div>
|
||||
</div>
|
9
manager/src/frontend/js/app/access_list/item.js
Normal file
9
manager/src/frontend/js/app/access_list/item.js
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./item.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template
|
||||
});
|
168
manager/src/frontend/js/app/api.js
Normal file
168
manager/src/frontend/js/app/api.js
Normal file
@ -0,0 +1,168 @@
|
||||
'use strict';
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
/**
|
||||
* @param {String} message
|
||||
* @param {*} debug
|
||||
* @param {Number} code
|
||||
* @constructor
|
||||
*/
|
||||
const ApiError = function (message, debug, code) {
|
||||
let temp = Error.call(this, message);
|
||||
temp.name = this.name = 'ApiError';
|
||||
|
||||
this.stack = temp.stack;
|
||||
this.message = temp.message;
|
||||
this.debug = debug;
|
||||
this.code = code;
|
||||
};
|
||||
|
||||
ApiError.prototype = Object.create(Error.prototype, {
|
||||
constructor: {
|
||||
value: ApiError,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} verb
|
||||
* @param {String} path
|
||||
* @param {Object} [data]
|
||||
* @param {Object} [options]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function fetch (verb, path, data, options) {
|
||||
options = options || {};
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
let api_url = '/api/';
|
||||
let url = api_url + path;
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
data: typeof data === 'object' && data !== null ? JSON.stringify(data) : data,
|
||||
type: verb,
|
||||
dataType: 'json',
|
||||
contentType: 'application/json; charset=UTF-8',
|
||||
crossDomain: true,
|
||||
timeout: (options.timeout ? options.timeout : 30000),
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
|
||||
success: function (data, textStatus, response) {
|
||||
resolve(response);
|
||||
},
|
||||
|
||||
error: function (xhr, status, error_thrown) {
|
||||
let code = 400;
|
||||
|
||||
if (typeof xhr.responseJSON !== 'undefined' && typeof xhr.responseJSON.error !== 'undefined' && typeof xhr.responseJSON.error.message !== 'undefined') {
|
||||
error_thrown = xhr.responseJSON.error.message;
|
||||
code = xhr.responseJSON.error.code || 500;
|
||||
}
|
||||
|
||||
reject(new ApiError(error_thrown, xhr.responseText, code));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
status: function () {
|
||||
return fetch('get', '');
|
||||
},
|
||||
|
||||
Hosts: {
|
||||
|
||||
/**
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function () {
|
||||
return fetch('get', 'hosts');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'hosts', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {String} data._id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let _id = data._id;
|
||||
delete data._id;
|
||||
return fetch('put', 'hosts/' + _id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} _id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (_id) {
|
||||
return fetch('delete', 'hosts/' + _id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} _id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
reconfigure: function (_id) {
|
||||
return fetch('post', 'hosts/' + _id + '/reconfigure');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} _id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
renew: function (_id) {
|
||||
return fetch('post', 'hosts/' + _id + '/renew');
|
||||
}
|
||||
},
|
||||
|
||||
Access: {
|
||||
|
||||
/**
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function () {
|
||||
return fetch('get', 'access');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
create: function (data) {
|
||||
return fetch('post', 'access', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @param {String} data._id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
update: function (data) {
|
||||
let _id = data._id;
|
||||
delete data._id;
|
||||
return fetch('put', 'access/' + _id, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} _id
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (_id) {
|
||||
return fetch('delete', 'access/' + _id);
|
||||
}
|
||||
}
|
||||
};
|
5
manager/src/frontend/js/app/cache.js
Normal file
5
manager/src/frontend/js/app/cache.js
Normal file
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
let cache = {};
|
||||
|
||||
module.exports = cache;
|
148
manager/src/frontend/js/app/controller.js
Normal file
148
manager/src/frontend/js/app/controller.js
Normal file
@ -0,0 +1,148 @@
|
||||
'use strict';
|
||||
|
||||
import Backbone from 'backbone';
|
||||
|
||||
const Cache = require('./cache');
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* @param {String} route
|
||||
* @param {Object} [options]
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
navigate: function (route, options) {
|
||||
options = options || {};
|
||||
Backbone.history.navigate(route.toString(), options);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Dashboard
|
||||
*/
|
||||
showDashboard: function () {
|
||||
require(['./main', './dashboard/main'], (App, View) => {
|
||||
this.navigate('/');
|
||||
App.UI.showMainLoading();
|
||||
|
||||
let view = new View();
|
||||
|
||||
view.on('loaded', function () {
|
||||
App.UI.hideMainLoading();
|
||||
});
|
||||
|
||||
App.UI.showChildView('main_region', view);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Access
|
||||
*/
|
||||
showAccess: function () {
|
||||
require(['./main', './access/main'], (App, View) => {
|
||||
this.navigate('/access');
|
||||
App.UI.showMainLoading();
|
||||
|
||||
let view = new View();
|
||||
|
||||
view.on('loaded', function () {
|
||||
App.UI.hideMainLoading();
|
||||
});
|
||||
|
||||
App.UI.showChildView('main_region', view);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show Host Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showHostForm: function (model) {
|
||||
require(['./main', './host/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show Delete Host Confirmation
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showDeleteHost: function (model) {
|
||||
require(['./main', './host/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show Reconfigure Host
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showReconfigureHost: function (model) {
|
||||
require(['./main', './host/reconfigure'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show Renew Host
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showRenewHost: function (model) {
|
||||
require(['./main', './host/renew'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show Advanced Host
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showAdvancedHost: function (model) {
|
||||
require(['./main', './host/advanced'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Show Access List Form
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showAccessListForm: function (model) {
|
||||
require(['./main', './access_list/form'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show Delete Access List Confirmation
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showDeleteAccessList: function (model) {
|
||||
require(['./main', './access_list/delete'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Error
|
||||
*
|
||||
* @param {Error} err
|
||||
* @param {String} nice_msg
|
||||
*/
|
||||
showError: function (err, nice_msg) {
|
||||
require(['./main', './error/main'], (App, View) => {
|
||||
App.UI.showChildView('main_region', new View({
|
||||
err: err,
|
||||
nice_msg: nice_msg
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
5
manager/src/frontend/js/app/dashboard/empty.ejs
Normal file
5
manager/src/frontend/js/app/dashboard/empty.ejs
Normal file
@ -0,0 +1,5 @@
|
||||
<td colspan="10" class="text-center">
|
||||
<br><br>
|
||||
<p>It looks like there are no hosts configured.</p>
|
||||
<p><button type="button" class="btn btn-sm btn-success">Create your first Host</button></p>
|
||||
</td>
|
23
manager/src/frontend/js/app/dashboard/empty.js
Normal file
23
manager/src/frontend/js/app/dashboard/empty.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./empty.ejs');
|
||||
const HostModel = require('../../models/host');
|
||||
const Controller = require('../controller');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
create: 'button'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.create': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showHostForm(new HostModel.Model);
|
||||
}
|
||||
}
|
||||
});
|
12
manager/src/frontend/js/app/dashboard/main.ejs
Normal file
12
manager/src/frontend/js/app/dashboard/main.ejs
Normal file
@ -0,0 +1,12 @@
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<th>Hostname</th>
|
||||
<th>Forward</th>
|
||||
<th>SSL</th>
|
||||
<th>Access List</th>
|
||||
<th class="text-right"><button type="button" class="btn btn-xs btn-info">Create Host</button></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- items -->
|
||||
</tbody>
|
||||
</table>
|
63
manager/src/frontend/js/app/dashboard/main.js
Normal file
63
manager/src/frontend/js/app/dashboard/main.js
Normal file
@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const Api = require('../api');
|
||||
const template = require('./main.ejs');
|
||||
const Controller = require('../controller');
|
||||
const RowView = require('./row');
|
||||
const HostModel = require('../../models/host');
|
||||
const EmptyView = require('./empty');
|
||||
|
||||
const TableBody = Mn.CollectionView.extend({
|
||||
tagName: 'tbody',
|
||||
childView: RowView
|
||||
});
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
id: 'dashboard',
|
||||
|
||||
regions: {
|
||||
list_region: {
|
||||
el: 'tbody',
|
||||
replaceElement: true
|
||||
}
|
||||
},
|
||||
|
||||
ui: {
|
||||
'create': 'th button'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.create': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showHostForm(new HostModel.Model);
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
Api.Hosts.getAll()
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
if (response && response.length) {
|
||||
|
||||
view.showChildView('list_region', new TableBody({
|
||||
collection: new HostModel.Collection(response)
|
||||
}));
|
||||
} else {
|
||||
view.showChildView('list_region', new EmptyView());
|
||||
}
|
||||
|
||||
view.trigger('loaded');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
Controller.showError(err, 'Could not fetch Hosts');
|
||||
view.trigger('loaded');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
27
manager/src/frontend/js/app/dashboard/row.ejs
Normal file
27
manager/src/frontend/js/app/dashboard/row.ejs
Normal file
@ -0,0 +1,27 @@
|
||||
<td><a href="<%- ssl ? 'https' : 'http' %>://<%- hostname %>" target="_blank"><%- hostname %></a></td>
|
||||
<td><span class="monospace"><%- forward_server %>:<%- forward_port %></span></td>
|
||||
<td>
|
||||
<% if (ssl && force_ssl) { %>
|
||||
Forced
|
||||
<% } else if (ssl) { %>
|
||||
Enabled
|
||||
<% } else { %>
|
||||
No
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<% if (access_list) { %>
|
||||
<a href="#" class="access_list"><%- access_list.name %></a>
|
||||
<% } else { %>
|
||||
<em>None</em>
|
||||
<% } %>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<% if (ssl) { %>
|
||||
<button type="button" class="btn btn-default btn-xs renew" title="Renew SSL"><i class="fa fa-shield" aria-hidden="true"></i></button>
|
||||
<% } %>
|
||||
<button type="button" class="btn btn-default btn-xs reconfigure" title="Reconfigure Nginx"><i class="fa fa-refresh" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn btn-default btn-xs advanced" title="Advanced Configuration"><i class="fa fa-code" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn btn-warning btn-xs edit" title="Edit"><i class="fa fa-pencil" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn btn-danger btn-xs delete" title="Delete"><i class="fa fa-times" aria-hidden="true"></i></button>
|
||||
</td>
|
57
manager/src/frontend/js/app/dashboard/row.js
Normal file
57
manager/src/frontend/js/app/dashboard/row.js
Normal file
@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./row.ejs');
|
||||
const Controller = require('../controller');
|
||||
const AccessListModel = require('../../models/access');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
tagName: 'tr',
|
||||
|
||||
ui: {
|
||||
edit: 'button.edit',
|
||||
delete: 'button.delete',
|
||||
access_list: 'a.access_list',
|
||||
reconfigure: 'button.reconfigure',
|
||||
renew: 'button.renew',
|
||||
advanced: 'button.advanced'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.edit': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showHostForm(this.model);
|
||||
},
|
||||
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showDeleteHost(this.model);
|
||||
},
|
||||
|
||||
'click @ui.access_list': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showAccessListForm(new AccessListModel.Model(this.model.get('access_list')));
|
||||
},
|
||||
|
||||
'click @ui.reconfigure': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showReconfigureHost(this.model);
|
||||
},
|
||||
|
||||
'click @ui.renew': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showRenewHost(this.model);
|
||||
},
|
||||
|
||||
'click @ui.advanced': function (e) {
|
||||
e.preventDefault();
|
||||
Controller.showAdvancedHost(this.model);
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
3
manager/src/frontend/js/app/error/main.ejs
Normal file
3
manager/src/frontend/js/app/error/main.ejs
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<strong><%- getNiceMessage() %></strong> – <%- getErrorMessage() %>
|
||||
</div>
|
33
manager/src/frontend/js/app/error/main.js
Normal file
33
manager/src/frontend/js/app/error/main.js
Normal file
@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./main.ejs');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
id: 'error',
|
||||
template: template,
|
||||
|
||||
options: {
|
||||
err: null,
|
||||
nice_msg: 'Unknown error'
|
||||
},
|
||||
|
||||
templateContext: function () {
|
||||
let view = this;
|
||||
return {
|
||||
getNiceMessage: function () {
|
||||
return view.options.nice_msg;
|
||||
},
|
||||
|
||||
getErrorMessage: function () {
|
||||
return view.options.err ? view.options.err.message : '';
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
console.error(this.options.err);
|
||||
}
|
||||
});
|
||||
|
23
manager/src/frontend/js/app/host/advanced.ejs
Normal file
23
manager/src/frontend/js/app/host/advanced.ejs
Normal file
@ -0,0 +1,23 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="form">
|
||||
<div class="modal-header text-left">
|
||||
<h4 class="modal-title">Advanced Configuration for <%- hostname %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This section is for advanced users only! If you don't know Nginx configuration backwards, you should abort now.
|
||||
You might typically use this for configuring additional location sections, ip restrictions or websocket proxying.</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="advanced-input">Additional Nginx Configuration (inside server block)</label>
|
||||
<textarea class="form-control" rows="10" name="advanced" id="advanced-input"><%- advanced %></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger save">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
39
manager/src/frontend/js/app/host/advanced.js
Normal file
39
manager/src/frontend/js/app/host/advanced.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./advanced.ejs');
|
||||
const Controller = require('../controller');
|
||||
const Api = require('../api');
|
||||
const App = require('../main');
|
||||
|
||||
require('jquery-serializejson');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
buttons: 'form button'
|
||||
},
|
||||
|
||||
events: {
|
||||
'submit @ui.form': function (e) {
|
||||
e.preventDefault();
|
||||
let data = this.ui.form.serializeJSON();
|
||||
data._id = this.model.get('_id');
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
|
||||
Api.Hosts.update(data)
|
||||
.then((/*result*/) => {
|
||||
App.UI.closeModal();
|
||||
Controller.showDashboard();
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
16
manager/src/frontend/js/app/host/delete.ejs
Normal file
16
manager/src/frontend/js/app/host/delete.ejs
Normal file
@ -0,0 +1,16 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="form-horizontal">
|
||||
<div class="modal-header text-left">
|
||||
<h4 class="modal-title">Delete Host</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure? You cannot undo this.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger delete">Yes I'm Sure</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
35
manager/src/frontend/js/app/host/delete.js
Normal file
35
manager/src/frontend/js/app/host/delete.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./delete.ejs');
|
||||
const Controller = require('../controller');
|
||||
const Api = require('../api');
|
||||
const App = require('../main');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
buttons: 'form button',
|
||||
delete: 'button.delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.delete': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
|
||||
Api.Hosts.delete(this.model.get('_id'))
|
||||
.then((/*result*/) => {
|
||||
App.UI.closeModal();
|
||||
Controller.showDashboard();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
84
manager/src/frontend/js/app/host/form.ejs
Normal file
84
manager/src/frontend/js/app/host/form.ejs
Normal file
@ -0,0 +1,84 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="form-horizontal">
|
||||
<div class="modal-header text-left">
|
||||
<h4 class="modal-title"><% if (typeof _id !== 'undefined') { %>Edit<% } else { %>Create<% } %> Host</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Hostname</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control" placeholder="myhost.example.com" name="hostname" value="<%- hostname %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Forwarding IP</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control" placeholder="192.168.0.1" name="forward_server" value="<%- forward_server %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Forwarding Port</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" minimum="1" maximum="65535" class="form-control" placeholder="" name="forward_port" value="<%- forward_port %>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Access List</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="form-control" name="access_list_id">
|
||||
<option value="">Loading...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-4 col-sm-8">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="asset_caching" value="true"<%- asset_caching ? ' checked' : '' %>> Enable Asset Caching
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="block_exploits" value="true"<%- block_exploits ? ' checked' : '' %>> Block Common Exploits
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="ssl" value="true"<%- ssl ? ' checked' : '' %>> Enable SSL with Letsencrypt
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ssl_options"<%= ssl ? '' : ' style="display: none;"' %>>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Letsencrypt Email</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="email" class="form-control" placeholder="" name="letsencrypt_email" value="<%- letsencrypt_email %>"<%- ssl ? ' required' : '' %>>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-4 col-sm-8">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="accept_tos" value="true"<%- accept_tos ? ' checked' : '' %><%- ssl ? ' required' : '' %>> I accept the <a href="https://letsencrypt.org/repository/" target="_blank">Letsencrypt Terms of Service</a>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="force_ssl" value="true"<%- force_ssl ? ' checked' : '' %>> Redirect HTTP to HTTPS
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-success save">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
106
manager/src/frontend/js/app/host/form.js
Normal file
106
manager/src/frontend/js/app/host/form.js
Normal file
@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const _ = require('lodash');
|
||||
const template = require('./form.ejs');
|
||||
const Controller = require('../controller');
|
||||
const Api = require('../api');
|
||||
const App = require('../main');
|
||||
|
||||
require('jquery-serializejson');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
form: 'form',
|
||||
buttons: 'form button',
|
||||
ssl_options: '.ssl_options',
|
||||
ssl: 'input[name="ssl"]',
|
||||
letsencrypt_email: 'input[name="letsencrypt_email"]',
|
||||
accept_tos: 'input[name="accept_tos"]',
|
||||
access_list_id: 'select[name="access_list_id"]'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change @ui.ssl': function (e) {
|
||||
let inputs = this.ui.letsencrypt_email.add(this.ui.accept_tos);
|
||||
if (this.ui.ssl.prop('checked')) {
|
||||
this.ui.ssl_options.show();
|
||||
inputs.prop('required', true);
|
||||
} else {
|
||||
this.ui.ssl_options.hide();
|
||||
inputs.prop('required', false);
|
||||
}
|
||||
},
|
||||
|
||||
'submit @ui.form': function (e) {
|
||||
e.preventDefault();
|
||||
let data = _.extend({}, this.ui.form.serializeJSON());
|
||||
|
||||
// Change text true's to bools
|
||||
_.map(data, function (val, key) {
|
||||
if (val === 'true') {
|
||||
data[key] = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Port is integer
|
||||
data.forward_port = parseInt(data.forward_port, 10);
|
||||
|
||||
// accept_tos is not required for backend
|
||||
delete data.accept_tos;
|
||||
delete data.access_list;
|
||||
|
||||
if (!data.ssl) {
|
||||
delete data.letsencrypt_email;
|
||||
delete data.force_ssl;
|
||||
}
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
let method = Api.Hosts.create;
|
||||
|
||||
if (this.model.get('_id')) {
|
||||
// edit
|
||||
method = Api.Hosts.update;
|
||||
data._id = this.model.get('_id');
|
||||
}
|
||||
|
||||
method(data)
|
||||
.then((/*result*/) => {
|
||||
App.UI.closeModal();
|
||||
Controller.showDashboard();
|
||||
})
|
||||
.catch((err) => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
Api.Access.getAll()
|
||||
.then(response => {
|
||||
if (!view.isDestroyed()) {
|
||||
view.ui.access_list_id.empty().append($('<option>').val('').text('None (Publicly Accessible)'));
|
||||
|
||||
if (response && response.length) {
|
||||
_.map(response, access => {
|
||||
view.ui.access_list_id.append($('<option>').val(access._id).text(access.name));
|
||||
});
|
||||
}
|
||||
|
||||
if (this.model.get('access_list_id')) {
|
||||
view.ui.access_list_id.val(this.model.get('access_list_id'));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
alert("Error loading Access Lists!\n\n" + err.message);
|
||||
App.UI.closeModal();
|
||||
});
|
||||
}
|
||||
});
|
17
manager/src/frontend/js/app/host/reconfigure.ejs
Normal file
17
manager/src/frontend/js/app/host/reconfigure.ejs
Normal file
@ -0,0 +1,17 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="form-horizontal">
|
||||
<div class="modal-header text-left">
|
||||
<h4 class="modal-title">Reconfigure Host</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This will simply re-create the Nginx config based on it's settings. You shouldn't need to do this under normal circumstances
|
||||
but if your host isn't working as expected, this may fix it.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-success reconfigure">Reconfigure</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
33
manager/src/frontend/js/app/host/reconfigure.js
Normal file
33
manager/src/frontend/js/app/host/reconfigure.js
Normal file
@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./reconfigure.ejs');
|
||||
const Api = require('../api');
|
||||
const App = require('../main');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
buttons: 'form button',
|
||||
reconfigure: 'button.reconfigure'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.reconfigure': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
|
||||
Api.Hosts.reconfigure(this.model.get('_id'))
|
||||
.then((/*result*/) => {
|
||||
App.UI.closeModal();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
17
manager/src/frontend/js/app/host/renew.ejs
Normal file
17
manager/src/frontend/js/app/host/renew.ejs
Normal file
@ -0,0 +1,17 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="form-horizontal">
|
||||
<div class="modal-header text-left">
|
||||
<h4 class="modal-title">Renew SSL Certificates</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This will renew the SSL Certificates for the host. This normally happens automatically however if you notice
|
||||
SSL working incorrectly, this may fix it.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-success renew">Renew SSL</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
33
manager/src/frontend/js/app/host/renew.js
Normal file
33
manager/src/frontend/js/app/host/renew.js
Normal file
@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./renew.ejs');
|
||||
const Api = require('../api');
|
||||
const App = require('../main');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
buttons: 'form button',
|
||||
renew: 'button.renew'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.renew': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
||||
|
||||
Api.Hosts.renew(this.model.get('_id'))
|
||||
.then((/*result*/) => {
|
||||
App.UI.closeModal();
|
||||
})
|
||||
.catch(err => {
|
||||
alert(err.message);
|
||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
120
manager/src/frontend/js/app/main.js
Normal file
120
manager/src/frontend/js/app/main.js
Normal file
@ -0,0 +1,120 @@
|
||||
'use strict';
|
||||
|
||||
import $ from 'jquery';
|
||||
import _ from 'underscore';
|
||||
import Backbone from 'backbone';
|
||||
|
||||
const Mn = require('../lib/marionette');
|
||||
const Cache = require('./cache');
|
||||
const Controller = require('./controller');
|
||||
const Router = require('./router');
|
||||
const UI = require('./ui/main');
|
||||
const Api = require('./api');
|
||||
|
||||
const App = Mn.Application.extend({
|
||||
|
||||
region: '#app',
|
||||
Cache: Cache,
|
||||
Api: Api,
|
||||
UI: null,
|
||||
Controller: Controller,
|
||||
version: null,
|
||||
|
||||
onStart: function (app, options) {
|
||||
console.log('Welcome to Nginx Proxy Manager');
|
||||
|
||||
let myapp = this;
|
||||
|
||||
Api.status()
|
||||
.then(result => {
|
||||
this.version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
||||
})
|
||||
.then(Api.Bootstrap)
|
||||
.then(() => {
|
||||
this.bootstrapTimer();
|
||||
|
||||
this.UI = new UI();
|
||||
this.UI.on('render', () => {
|
||||
// If successful, start the history and routing
|
||||
new Router(options);
|
||||
Backbone.history.start({});
|
||||
|
||||
// Remove loading class
|
||||
$('#app').removeClass('loading');
|
||||
});
|
||||
|
||||
this.getRegion().show(this.UI);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.info('Not logged in: ', err.message);
|
||||
myapp.trigger('after:start');
|
||||
myapp.UI = new UI();
|
||||
myapp.UI.on('render', () => {
|
||||
// Remove loading class
|
||||
myapp.UI.reset();
|
||||
Controller.showLogin();
|
||||
});
|
||||
myapp.getRegion().show(myapp.UI);
|
||||
});
|
||||
},
|
||||
|
||||
History: {
|
||||
replace: function (data) {
|
||||
window.history.replaceState(_.extend(window.history.state || {}, data), document.title);
|
||||
},
|
||||
|
||||
get: function (attr) {
|
||||
return window.history.state ? window.history.state[attr] : undefined;
|
||||
}
|
||||
},
|
||||
|
||||
Error: function (code, message, debug) {
|
||||
let temp = Error.call(this, message);
|
||||
temp.name = this.name = 'AppError';
|
||||
this.stack = temp.stack;
|
||||
this.message = temp.message;
|
||||
this.code = code;
|
||||
this.debug = debug;
|
||||
},
|
||||
|
||||
showError: function () {
|
||||
let ErrorView = Mn.View.extend({
|
||||
tagName: 'section',
|
||||
id: 'error',
|
||||
template: _.template('Error loading stuff. Please reload the app.')
|
||||
});
|
||||
|
||||
this.getRegion().show(new ErrorView());
|
||||
},
|
||||
|
||||
/**
|
||||
* Bootstraps the user from time to time
|
||||
*/
|
||||
bootstrapTimer: function () {
|
||||
setTimeout(() => {
|
||||
Api.status()
|
||||
.then(result => {
|
||||
let version = [result.version.major, result.version.minor, result.version.revision].join('.');
|
||||
if (version !== this.version) {
|
||||
document.location.reload();
|
||||
}
|
||||
})
|
||||
.then(Api.Bootstrap)
|
||||
.then(() => {
|
||||
this.bootstrapTimer();
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.message !== 'timeout' && err.code && err.code !== 400) {
|
||||
console.log(err);
|
||||
console.error(err.message);
|
||||
document.location.reload();
|
||||
} else {
|
||||
this.bootstrapTimer();
|
||||
}
|
||||
});
|
||||
}, 30 * 1000);
|
||||
}
|
||||
});
|
||||
|
||||
const app = new App();
|
||||
module.exports = app;
|
15
manager/src/frontend/js/app/router.js
Normal file
15
manager/src/frontend/js/app/router.js
Normal file
@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const Mn = require('../lib/marionette');
|
||||
const Controller = require('./controller');
|
||||
|
||||
module.exports = Mn.AppRouter.extend({
|
||||
appRoutes: {
|
||||
access: 'showAccess',
|
||||
'*default': 'showDashboard'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.controller = Controller;
|
||||
}
|
||||
});
|
19
manager/src/frontend/js/app/ui/header/main.ejs
Normal file
19
manager/src/frontend/js/app/ui/header/main.ejs
Normal file
@ -0,0 +1,19 @@
|
||||
<nav class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#" title="Nginx Proxy Manager"><img src="/images/favicon/android-chrome-192x192.png" alt="Nginx Proxy Manager"></a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="#">Hosts</a></li>
|
||||
<li><a href="#access">Access Lists</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
37
manager/src/frontend/js/app/ui/header/main.js
Normal file
37
manager/src/frontend/js/app/ui/header/main.js
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./main.ejs');
|
||||
const Controller = require('../../controller');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
logo: '.navbar-brand',
|
||||
links: 'a[href^="#"]'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.links': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
let href = e.target.href.replace(/[^#]*#/g, '');
|
||||
|
||||
switch (href) {
|
||||
case 'dashboard':
|
||||
Controller.showDashboard();
|
||||
break;
|
||||
|
||||
case 'access':
|
||||
Controller.showAccess();
|
||||
break;
|
||||
|
||||
default:
|
||||
Controller.showDashboard();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
2
manager/src/frontend/js/app/ui/main.ejs
Normal file
2
manager/src/frontend/js/app/ui/main.ejs
Normal file
@ -0,0 +1,2 @@
|
||||
<section id="header"></section>
|
||||
<section id="main" class="container"></section>
|
101
manager/src/frontend/js/app/ui/main.js
Normal file
101
manager/src/frontend/js/app/ui/main.js
Normal file
@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
|
||||
import Mn from 'backbone.marionette';
|
||||
|
||||
const template = require('./main.ejs');
|
||||
const HeaderView = require('./header/main');
|
||||
const Cache = require('../cache');
|
||||
|
||||
require('bootstrap');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
template: template,
|
||||
|
||||
modal: null,
|
||||
|
||||
ui: {
|
||||
header_region: '#header',
|
||||
main_region: '#main',
|
||||
modal_region: '#modal-dialog',
|
||||
main_loader_region: '#main-loader'
|
||||
},
|
||||
|
||||
regions: {
|
||||
header_region: '@ui.header_region',
|
||||
main_region: '@ui.main_region',
|
||||
modal_region: '@ui.modal_region',
|
||||
main_loader_region: '@ui.main_loader_region'
|
||||
},
|
||||
|
||||
showMainLoading: function () {
|
||||
this.ui.main_loader_region.show();
|
||||
},
|
||||
|
||||
hideMainLoading: function () {
|
||||
this.ui.main_loader_region.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param view
|
||||
* @param [show_callback]
|
||||
* @param [shown_callback]
|
||||
*/
|
||||
showModalDialog: function (view, show_callback, shown_callback) {
|
||||
this.showChildView('modal_region', view);
|
||||
this.modal.modal('show');
|
||||
|
||||
let ui = this;
|
||||
|
||||
this.modal.on('hidden.bs.modal', function (/*e*/) {
|
||||
if (show_callback) {
|
||||
ui.modal.off('show.bs.modal', show_callback);
|
||||
}
|
||||
|
||||
if (shown_callback) {
|
||||
ui.modal.off('shown.bs.modal', shown_callback);
|
||||
}
|
||||
|
||||
ui.modal.off('hidden.bs.modal');
|
||||
view.destroy();
|
||||
});
|
||||
|
||||
if (show_callback) {
|
||||
this.modal.on('show.bs.modal', show_callback);
|
||||
}
|
||||
|
||||
if (shown_callback) {
|
||||
this.modal.on('shown.bs.modal', shown_callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param [hidden_callback]
|
||||
*/
|
||||
closeModal: function (hidden_callback) {
|
||||
this.modal.modal('hide');
|
||||
|
||||
if (hidden_callback) {
|
||||
this.modal.on('hidden.bs.modal', hidden_callback);
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.showChildView('header_region', new HeaderView({
|
||||
model: Cache.User
|
||||
}));
|
||||
|
||||
if (this.modal === null) {
|
||||
this.modal = $('#modal-dialog');
|
||||
this.modal.modal({
|
||||
show: false
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
this.getRegion('header_region').reset();
|
||||
this.getRegion('modal_region').reset();
|
||||
}
|
||||
});
|
Reference in New Issue
Block a user