Feature/custom locations (#74)

* New feature: custom locations

* Custom locations: exteding config generator

* Custom locations: refactoring

* Fixing proxy_host table on small screens

* Custom locations: translations

* Custom locations bugfix

* Custom locations bugfix

* PR #74 fixes
This commit is contained in:
kolbii
2019-03-04 23:21:02 +01:00
committed by jc21
parent 133d66c2fe
commit 71dfd5d8f8
12 changed files with 373 additions and 19 deletions

View File

@ -7,10 +7,22 @@
<form>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
<li role="presentation" class="nav-item"><a href="#locations" aria-controls="tab4" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-layers"></i> <%- i18n('all-hosts', 'locations') %></a></li>
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
</ul>
<div class="tab-content">
<!-- Locations -->
<div class="tab-pane" id="locations">
<div class="row">
<div class="col-sm-12">
<button type="button" class="btn btn-secondary add_location"><%- i18n('locations', 'new_location') %></button>
<div class="locations_container mt-3"></div>
</div>
</div>
</div>
<!-- Details -->
<div role="tabpanel" class="tab-pane active" id="details">
<div class="row">

View File

@ -3,11 +3,14 @@
const Mn = require('backbone.marionette');
const App = require('../../main');
const ProxyHostModel = require('../../../models/proxy-host');
const ProxyLocationModel = require('../../../models/proxy-host-location');
const template = require('./form.ejs');
const certListItemTemplate = require('../certificates-list-item.ejs');
const accessListItemTemplate = require('./access-list-item.ejs');
const CustomLocation = require('./location');
const Helpers = require('../../../lib/helpers');
require('jquery-serializejson');
require('selectize');
@ -15,6 +18,8 @@ module.exports = Mn.View.extend({
template: template,
className: 'modal-dialog',
locationsCollection: new ProxyLocationModel.Collection(),
ui: {
form: 'form',
domain_names: 'input[name="domain_names"]',
@ -22,6 +27,8 @@ module.exports = Mn.View.extend({
buttons: '.modal-footer button',
cancel: 'button.cancel',
save: 'button.save',
add_location_btn: 'button.add_location',
locations_container:'.locations_container',
certificate_select: 'select[name="certificate_id"]',
access_list_select: 'select[name="access_list_id"]',
ssl_forced: 'input[name="ssl_forced"]',
@ -32,6 +39,10 @@ module.exports = Mn.View.extend({
letsencrypt: '.letsencrypt'
},
regions: {
locations_regions: '@ui.locations_container'
},
events: {
'change @ui.certificate_select': function () {
let id = this.ui.certificate_select.val();
@ -82,6 +93,13 @@ module.exports = Mn.View.extend({
}
},
'click @ui.add_location_btn': function (e) {
e.preventDefault();
const model = new ProxyLocationModel.Model();
this.locationsCollection.add(model);
},
'click @ui.save': function (e) {
e.preventDefault();
@ -93,6 +111,16 @@ module.exports = Mn.View.extend({
let view = this;
let data = this.ui.form.serializeJSON();
// Add locations
data.locations = [];
this.locationsCollection.models.forEach((location) => {
data.locations.push(location.toJSON());
});
// Serialize collects path from custom locations
// This field must be removed from root object
delete data.path;
// Manipulate
data.forward_port = parseInt(data.forward_port, 10);
data.block_exploits = !!data.block_exploits;
@ -246,5 +274,20 @@ module.exports = Mn.View.extend({
if (typeof options.model === 'undefined' || !options.model) {
this.model = new ProxyHostModel.Model();
}
this.locationsCollection = new ProxyLocationModel.Collection();
// Custom locations
this.showChildView('locations_regions', new CustomLocation.LocationCollectionView({
collection: this.locationsCollection
}));
// Check wether there are any location defined
if (options.model && Array.isArray(options.model.attributes.locations)) {
options.model.attributes.locations.forEach((location) => {
let m = new ProxyLocationModel.Model(location);
this.locationsCollection.add(m);
});
}
}
});

View File

@ -0,0 +1,63 @@
<div class="location-block card">
<div class="card-body">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="form-label"><%- i18n('locations', 'location_label') %> <span class="form-required">*</span></label>
<div class="row gutter-xs">
<div class="col">
<div class="input-group">
<span class="input-group-prepend">
<span class="input-group-text">location</span>
</span>
<input type="text" name="path" class="form-control model" value="<%- path %>" placeholder="<%- i18n('locations', 'path') %>" required>
</div>
</div>
<div class="col-auto">
<div class="selectgroup">
<label class="selectgroup-item">
<input type="checkbox" class="selectgroup-input">
<span class="selectgroup-button">
<i class="fe fe-settings"></i>
</span>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-3 col-md-3">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'forward-scheme') %><span class="form-required">*</span></label>
<select name="forward_scheme" class="form-control custom-select model" placeholder="http">
<option value="http" <%- forward_scheme === 'http' ? 'selected' : '' %>>http</option>
<option value="https" <%- forward_scheme === 'https' ? 'selected' : '' %>>https</option>
</select>
</div>
</div>
<div class="col-sm-5 col-md-5">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'forward-host') %><span class="form-required">*</span></label>
<input type="text" name="forward_host" class="form-control text-monospace model" placeholder="" value="<%- forward_host %>" autocomplete="off" maxlength="50" required>
</div>
</div>
<div class="col-sm-4 col-md-4">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'forward-port') %> <span class="form-required">*</span></label>
<input name="forward_port" type="number" class="form-control text-monospace model" placeholder="80" value="<%- forward_port %>" required>
</div>
</div>
</div>
<div class="row config">
<div class="col-md-12">
<div class="form-group">
<textarea name="advanced_config" rows="8" class="form-control text-monospace model" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
</div>
</div>
</div>
<a href="#" class="card-link location-delete">
<i class="fa fa-trash"></i> <%- i18n('locations', 'delete') %>
</a>
</div>
</div>

View File

@ -0,0 +1,54 @@
const locationItemTemplate = require('./location-item.ejs');
const Mn = require('backbone.marionette');
const App = require('../../main');
const LocationView = Mn.View.extend({
template: locationItemTemplate,
className: 'location_block',
ui: {
toggle: 'input[type="checkbox"]',
config: '.config',
delete: '.location-delete'
},
events: {
'change @ui.toggle': function(el) {
if (el.target.checked) {
this.ui.config.show();
} else {
this.ui.config.hide();
}
},
'change .model': function (e) {
const map = {};
map[e.target.name] = e.target.value;
this.model.set(map);
},
'click @ui.delete': function () {
this.model.destroy();
}
},
onRender: function() {
$(this.ui.config).hide();
},
templateContext: function() {
return {
i18n: App.i18n
}
}
});
const LocationCollectionView = Mn.CollectionView.extend({
className: 'locations_container',
childView: LocationView
});
module.exports = {
LocationCollectionView,
LocationView
}