Remove old manager code

This commit is contained in:
Jamie Curnow
2018-06-18 15:42:44 +10:00
parent 184a1cc6f8
commit 458f9daf4e
124 changed files with 0 additions and 9513 deletions

View File

@@ -1,5 +0,0 @@
<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>

View File

@@ -1,23 +0,0 @@
'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);
}
}
});

View File

@@ -1,11 +0,0 @@
<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>

View File

@@ -1,62 +0,0 @@
'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');
});
}
});

View File

@@ -1,7 +0,0 @@
<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>

View File

@@ -1,32 +0,0 @@
'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);
}
});

View File

@@ -1,24 +0,0 @@
<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>

View File

@@ -1,35 +0,0 @@
'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');
});
}
}
});

View File

@@ -1,30 +0,0 @@
<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>

View File

@@ -1,95 +0,0 @@
'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)
}));
}
});

View File

@@ -1,8 +0,0 @@
<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>

View File

@@ -1,9 +0,0 @@
'use strict';
import Mn from 'backbone.marionette';
const template = require('./item.ejs');
module.exports = Mn.View.extend({
template: template
});

View File

@@ -1,160 +0,0 @@
'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');
}
},
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);
}
}
};

View File

@@ -1,5 +0,0 @@
'use strict';
let cache = {};
module.exports = cache;

View File

@@ -1,170 +0,0 @@
'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 Proxy Host Form
*
* @param model
*/
showProxyHostForm: function (model) {
require(['./main', './host/proxy_form'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
},
/**
* Show Redirection Host Form
*
* @param model
*/
showRedirectionHostForm: function (model) {
require(['./main', './host/redirection_form'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
},
/**
* Show 404 Host Form
*
* @param model
*/
show404HostForm: function (model) {
require(['./main', './host/404_form'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
},
/**
* Show Stream Host Form
*
* @param model
*/
showStreamHostForm: function (model) {
require(['./main', './host/stream_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 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
}));
});
}
};

View File

@@ -1,9 +0,0 @@
<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 proxy">Create Proxy Host</button>
<button type="button" class="btn btn-sm btn-success redirection">Create Redirection Host</button>
<button type="button" class="btn btn-sm btn-success 404">Create 404 Host</button>
</p>
</td>

View File

@@ -1,35 +0,0 @@
'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: {
proxy: 'button.proxy',
redirection: 'button.redirection',
'404': 'button.404'
},
events: {
'click @ui.proxy': function (e) {
e.preventDefault();
Controller.showProxyHostForm(new HostModel.Model);
},
'click @ui.redirection': function (e) {
e.preventDefault();
Controller.showRedirectionHostForm(new HostModel.Model);
},
'click @ui.404': function (e) {
e.preventDefault();
Controller.show404HostForm(new HostModel.Model);
}
}
});

View File

@@ -1,24 +0,0 @@
<table class="table table-condensed table-striped">
<thead>
<th>Source</th>
<th>Destination</th>
<th>SSL</th>
<th>Access List</th>
<th class="text-right">
<div class="btn-group">
<button type="button" class="btn btn-xs btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Create Host <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" class="new-proxy">Proxy Host</a></li>
<li><a href="#" class="new-redirection">Redirection Host</a></li>
<li><a href="#" class="new-404">404 Host</a></li>
<li><a href="#" class="new-stream">Stream Host</a></li>
</ul>
</div>
</th>
</thead>
<tbody>
<!-- items -->
</tbody>
</table>

View File

@@ -1,81 +0,0 @@
'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: {
new_proxy: 'th .new-proxy',
new_redirection: 'th .new-redirection',
new_404: 'th .new-404',
new_stream: 'th .new-stream'
},
events: {
'click @ui.new_proxy': function (e) {
e.preventDefault();
Controller.showProxyHostForm(new HostModel.Model);
},
'click @ui.new_redirection': function (e) {
e.preventDefault();
Controller.showRedirectionHostForm(new HostModel.Model);
},
'click @ui.new_404': function (e) {
e.preventDefault();
Controller.show404HostForm(new HostModel.Model);
},
'click @ui.new_stream': function (e) {
e.preventDefault();
Controller.showStreamHostForm(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');
});
}
});

View File

@@ -1,49 +0,0 @@
<td>
<% if (type === 'stream') { %>
<%- incoming_port %>
<%- protocols.join(', ').toUpperCase() %>
<% } else { %>
<a href="<%- ssl ? 'https' : 'http' %>://<%- hostname %>" target="_blank"><%- hostname %></a>
<% } %>
</td>
<td>
<span class="monospace">
<% if (type === 'proxy' || type === 'stream') { %>
<%- forward_server %>:<%- forward_port %>
<% } else if (type === 'redirection') { %>
<%- forward_host %>
<% } else if (type === '404') { %>
404
<% } %>
</span>
</td>
<td>
<% if (type === 'stream') { %>
-
<% } else { %>
<% if (ssl && force_ssl) { %>
Forced
<% } else if (ssl) { %>
Enabled
<% } else { %>
No
<% } %>
<% } %>
</td>
<td>
<% if (type === 'stream') { %>
-
<% } else { %>
<% if (access_list) { %>
<a href="#" class="access_list"><%- access_list.name %></a>
<% } else { %>
<em>None</em>
<% } %>
<% } %>
</td>
<td class="text-right">
<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"<%- type === 'stream' ? ' disabled' : '' %>><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>

View File

@@ -1,64 +0,0 @@
'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',
advanced: 'button.advanced'
},
events: {
'click @ui.edit': function (e) {
e.preventDefault();
switch (this.model.get('type')) {
case 'proxy':
Controller.showProxyHostForm(this.model);
break;
case 'redirection':
Controller.showRedirectionHostForm(this.model);
break;
case '404':
Controller.show404HostForm(this.model);
break;
case 'stream':
Controller.showStreamHostForm(this.model);
break;
}
},
'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.advanced': function (e) {
e.preventDefault();
Controller.showAdvancedHost(this.model);
}
},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
}
});

View File

@@ -1,3 +0,0 @@
<div class="alert alert-danger" role="alert">
<strong><%- getNiceMessage() %></strong> &ndash; <%- getErrorMessage() %>
</div>

View File

@@ -1,33 +0,0 @@
'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);
}
});

View File

@@ -1,50 +0,0 @@
<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<% } %> 404 Host</h4>
</div>
<div class="modal-body">
<p>A 404 host will simply return a 404 not found page for any hits to any path on the domain.</p>
<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">
<div class="col-sm-offset-4 col-sm-8">
<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"<%- ssl && typeof _id !== 'undefined' ? ' checked' : '' %><%- ssl ? ' required' : '' %>> I accept the <a href="https://letsencrypt.org/repository/" target="_blank">Letsencrypt Terms of Service</a>
</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>

View File

@@ -1,78 +0,0 @@
'use strict';
import Mn from 'backbone.marionette';
const _ = require('lodash');
const template = require('./404_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"]'
},
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;
}
});
// This is a 404 host
data.type = '404';
// accept_tos is not required for backend
delete data.accept_tos;
if (!data.ssl) {
delete data.letsencrypt_email;
}
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');
});
}
}
});

View File

@@ -1,23 +0,0 @@
<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>

View File

@@ -1,39 +0,0 @@
'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');
});
}
}
});

View File

@@ -1,16 +0,0 @@
<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>

View File

@@ -1,35 +0,0 @@
'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');
});
}
}
});

View File

@@ -1,84 +0,0 @@
<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<% } %> Proxy 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"<%- ssl && typeof _id !== 'undefined' ? ' 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>

View File

@@ -1,108 +0,0 @@
'use strict';
import Mn from 'backbone.marionette';
const _ = require('lodash');
const template = require('./proxy_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;
}
});
data.type = 'proxy';
// 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();
});
}
});

View File

@@ -1,17 +0,0 @@
<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>

View File

@@ -1,33 +0,0 @@
'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');
});
}
}
});

View File

@@ -1,62 +0,0 @@
<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<% } %> Redirection Host</h4>
</div>
<div class="modal-body">
<p>A redirection host will forward browser requests on this hostname to the new hostname while keeping the same path.</p>
<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 Hostname</label>
<div class="col-sm-8">
<input type="text" class="form-control" placeholder="mynewhost.example.com" name="forward_host" value="<%- forward_host %>" required>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<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"<%- ssl && typeof _id !== 'undefined' ? ' checked' : '' %><%- ssl ? ' required' : '' %>> I accept the <a href="https://letsencrypt.org/repository/" target="_blank">Letsencrypt Terms of Service</a>
</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>

View File

@@ -1,78 +0,0 @@
'use strict';
import Mn from 'backbone.marionette';
const _ = require('lodash');
const template = require('./redirection_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"]'
},
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;
}
});
data.type = 'redirection';
// accept_tos is not required for backend
delete data.accept_tos;
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');
});
}
}
});

View File

@@ -1,55 +0,0 @@
<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<% } %> Stream Host</h4>
</div>
<div class="modal-body">
<div class="alert alert-warning" role="alert">
A Stream Host will forward a TCP/UDP connection directly to a another server on your network. <strong>There is no authentication.</strong>
Note you will also have to open the incoming port in your docker configuration for this to work.
<br>
<br>
You will not be able to use port <strong>80</strong>, <strong>81</strong> or <strong>443</strong> or any other previously configured Stream Host incoming port.
</div>
<div class="form-group">
<label class="col-sm-4 control-label">Incoming Port</label>
<div class="col-sm-8">
<input type="number" minimum="1" maximum="65535" class="form-control" placeholder="" name="incoming_port" value="<%- incoming_port ? incoming_port : '' %>" 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="<%- typeof _id === 'undefined' ? '' : forward_port %>" required>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<div class="checkbox">
<label>
<input type="checkbox" name="protocols[]" value="tcp"<%- typeof _id === 'undefined' || hasStreamProtocol('tcp') ? ' checked' : '' %>> TCP Forwarding
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="protocols[]" value="udp"<%- hasStreamProtocol('udp') ? ' checked' : '' %>> UDP Forwarding
</label>
</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>

View File

@@ -1,63 +0,0 @@
'use strict';
import Mn from 'backbone.marionette';
const _ = require('lodash');
const template = require('./stream_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'
},
events: {
'submit @ui.form': function (e) {
e.preventDefault();
let data = _.extend({}, this.ui.form.serializeJSON());
data.type = 'stream';
// Ports are integers
data.incoming_port = parseInt(data.incoming_port, 10);
data.forward_port = parseInt(data.forward_port, 10);
if (typeof data.protocols === 'undefined' || !data.protocols.length) {
alert('You must select one or more Protocols');
return;
}
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');
});
}
},
templateContext: {
hasStreamProtocol: function (protocol) {
return this.protocols.indexOf(protocol) !== -1;
}
}
});

View File

@@ -1,120 +0,0 @@
'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;

View File

@@ -1,15 +0,0 @@
'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;
}
});

View File

@@ -1,19 +0,0 @@
<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>

View File

@@ -1,37 +0,0 @@
'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;
}
}
}
});

View File

@@ -1,2 +0,0 @@
<section id="header"></section>
<section id="main" class="container"></section>

View File

@@ -1,101 +0,0 @@
'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();
}
});