Skip to content

Commit 71dfd5d

Browse files
kolbiijc21
authored andcommitted
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
1 parent 133d66c commit 71dfd5d

File tree

12 files changed

+371
-17
lines changed

12 files changed

+371
-17
lines changed

src/backend/internal/nginx.js

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,35 @@ const internalNginx = {
130130
return '/data/nginx/' + host_type + '/' + host_id + '.conf';
131131
},
132132

133+
/**
134+
* Generates custom locations
135+
* @param {Object} host
136+
* @returns {Promise}
137+
*/
138+
renderLocations: (host) => {
139+
return new Promise((resolve, reject) => {
140+
let template;
141+
142+
try {
143+
template = fs.readFileSync(__dirname + '/../templates/_location.conf', {encoding: 'utf8'});
144+
} catch (err) {
145+
reject(new error.ConfigurationError(err.message));
146+
return;
147+
}
148+
149+
let renderer = new Liquid();
150+
let renderedLocations = '';
151+
152+
const locationRendering = async () => {
153+
for (let i = 0; i < host.locations.length; i++) {
154+
renderedLocations += await renderer.parseAndRender(template, host.locations[i]);
155+
}
156+
}
157+
158+
locationRendering().then(() => resolve(renderedLocations));
159+
});
160+
},
161+
133162
/**
134163
* @param {String} host_type
135164
* @param {Object} host
@@ -157,6 +186,9 @@ const internalNginx = {
157186
return;
158187
}
159188

189+
let locationsPromise;
190+
let origLocations;
191+
160192
// Manipulate the data a bit before sending it to the template
161193
if (host_type !== 'default') {
162194
host.use_default_location = true;
@@ -165,24 +197,38 @@ const internalNginx = {
165197
}
166198
}
167199

168-
renderEngine
169-
.parseAndRender(template, host)
170-
.then(config_text => {
171-
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
200+
if (host.locations) {
201+
origLocations = [].concat(host.locations);
202+
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
203+
host.locations = renderedLocations;
204+
});
205+
} else {
206+
locationsPromise = Promise.resolve();
207+
}
172208

173-
if (debug_mode) {
174-
logger.success('Wrote config:', filename, config_text);
175-
}
209+
locationsPromise.then(() => {
210+
renderEngine
211+
.parseAndRender(template, host)
212+
.then(config_text => {
213+
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
176214

177-
resolve(true);
178-
})
179-
.catch(err => {
180-
if (debug_mode) {
181-
logger.warn('Could not write ' + filename + ':', err.message);
182-
}
215+
if (debug_mode) {
216+
logger.success('Wrote config:', filename, config_text);
217+
}
183218

184-
reject(new error.ConfigurationError(err.message));
185-
});
219+
// Restore locations array
220+
host.locations = origLocations;
221+
222+
resolve(true);
223+
})
224+
.catch(err => {
225+
if (debug_mode) {
226+
logger.warn('Could not write ' + filename + ':', err.message);
227+
}
228+
229+
reject(new error.ConfigurationError(err.message));
230+
});
231+
});
186232
});
187233
},
188234

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const migrate_name = 'custom_locations';
4+
const logger = require('../logger').migrate;
5+
6+
/**
7+
* Migrate
8+
* Extends proxy_host table with locations field
9+
*
10+
* @see http://knexjs.org/#Schema
11+
*
12+
* @param {Object} knex
13+
* @param {Promise} Promise
14+
* @returns {Promise}
15+
*/
16+
exports.up = function (knex/*, Promise*/) {
17+
logger.info('[' + migrate_name + '] Migrating Up...');
18+
19+
return knex.schema.table('proxy_host', function (proxy_host) {
20+
proxy_host.json('locations');
21+
})
22+
.then(() => {
23+
logger.info('[' + migrate_name + '] proxy_host Table altered');
24+
})
25+
};
26+
27+
/**
28+
* Undo Migrate
29+
*
30+
* @param {Object} knex
31+
* @param {Promise} Promise
32+
* @returns {Promise}
33+
*/
34+
exports.down = function (knex, Promise) {
35+
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
36+
return Promise.resolve(true);
37+
};

src/backend/models/proxy_host.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ProxyHost extends Model {
4747
}
4848

4949
static get jsonAttributes () {
50-
return ['domain_names', 'meta'];
50+
return ['domain_names', 'meta', 'locations'];
5151
}
5252

5353
static get relationMappings () {

src/backend/schema/endpoints/proxy-hosts.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,41 @@
6969
},
7070
"meta": {
7171
"type": "object"
72+
},
73+
"locations": {
74+
"type": "array",
75+
"minItems": 0,
76+
"items": {
77+
"type": "object",
78+
"required": [
79+
"forward_scheme",
80+
"forward_host",
81+
"forward_port",
82+
"path"
83+
],
84+
"additionalProperties": false,
85+
"properties": {
86+
"id": {
87+
"type": ["integer", "null"]
88+
},
89+
"path": {
90+
"type": "string",
91+
"minLength": 1
92+
},
93+
"forward_scheme": {
94+
"$ref": "#/definitions/forward_scheme"
95+
},
96+
"forward_host": {
97+
"$ref": "#/definitions/forward_host"
98+
},
99+
"forward_port": {
100+
"$ref": "#/definitions/forward_port"
101+
},
102+
"advanced_config": {
103+
"type": "string"
104+
}
105+
}
106+
}
72107
}
73108
},
74109
"properties": {
@@ -128,6 +163,9 @@
128163
},
129164
"meta": {
130165
"$ref": "#/definitions/meta"
166+
},
167+
"locations": {
168+
"$ref": "#/definitions/locations"
131169
}
132170
},
133171
"links": [
@@ -215,6 +253,9 @@
215253
},
216254
"meta": {
217255
"$ref": "#/definitions/meta"
256+
},
257+
"locations": {
258+
"$ref": "#/definitions/locations"
218259
}
219260
}
220261
},
@@ -285,6 +326,9 @@
285326
},
286327
"meta": {
287328
"$ref": "#/definitions/meta"
329+
},
330+
"locations": {
331+
"$ref": "#/definitions/locations"
288332
}
289333
}
290334
},

src/backend/templates/_location.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
location {{ path }} {
2+
proxy_set_header Host $host;
3+
proxy_set_header X-Forwarded-Scheme $scheme;
4+
proxy_set_header X-Forwarded-Proto $scheme;
5+
proxy_set_header X-Forwarded-For $remote_addr;
6+
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }};
7+
{{ advanced_config }}
8+
}

src/backend/templates/proxy_host.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ server {
1616

1717
{{ advanced_config }}
1818

19+
{{ locations }}
20+
1921
{% if use_default_location %}
22+
2023
location / {
2124
{%- if access_list_id > 0 -%}
2225
# Access List

src/frontend/js/app/nginx/proxy/form.ejs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,22 @@
77
<form>
88
<ul class="nav nav-tabs" role="tablist">
99
<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>
10+
<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>
1011
<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>
1112
<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>
1213
</ul>
1314
<div class="tab-content">
15+
16+
<!-- Locations -->
17+
<div class="tab-pane" id="locations">
18+
<div class="row">
19+
<div class="col-sm-12">
20+
<button type="button" class="btn btn-secondary add_location"><%- i18n('locations', 'new_location') %></button>
21+
<div class="locations_container mt-3"></div>
22+
</div>
23+
</div>
24+
</div>
25+
1426
<!-- Details -->
1527
<div role="tabpanel" class="tab-pane active" id="details">
1628
<div class="row">

src/frontend/js/app/nginx/proxy/form.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,32 @@
33
const Mn = require('backbone.marionette');
44
const App = require('../../main');
55
const ProxyHostModel = require('../../../models/proxy-host');
6+
const ProxyLocationModel = require('../../../models/proxy-host-location');
67
const template = require('./form.ejs');
78
const certListItemTemplate = require('../certificates-list-item.ejs');
89
const accessListItemTemplate = require('./access-list-item.ejs');
10+
const CustomLocation = require('./location');
911
const Helpers = require('../../../lib/helpers');
1012

13+
1114
require('jquery-serializejson');
1215
require('selectize');
1316

1417
module.exports = Mn.View.extend({
1518
template: template,
1619
className: 'modal-dialog',
1720

21+
locationsCollection: new ProxyLocationModel.Collection(),
22+
1823
ui: {
1924
form: 'form',
2025
domain_names: 'input[name="domain_names"]',
2126
forward_host: 'input[name="forward_host"]',
2227
buttons: '.modal-footer button',
2328
cancel: 'button.cancel',
2429
save: 'button.save',
30+
add_location_btn: 'button.add_location',
31+
locations_container:'.locations_container',
2532
certificate_select: 'select[name="certificate_id"]',
2633
access_list_select: 'select[name="access_list_id"]',
2734
ssl_forced: 'input[name="ssl_forced"]',
@@ -32,6 +39,10 @@ module.exports = Mn.View.extend({
3239
letsencrypt: '.letsencrypt'
3340
},
3441

42+
regions: {
43+
locations_regions: '@ui.locations_container'
44+
},
45+
3546
events: {
3647
'change @ui.certificate_select': function () {
3748
let id = this.ui.certificate_select.val();
@@ -82,6 +93,13 @@ module.exports = Mn.View.extend({
8293
}
8394
},
8495

96+
'click @ui.add_location_btn': function (e) {
97+
e.preventDefault();
98+
99+
const model = new ProxyLocationModel.Model();
100+
this.locationsCollection.add(model);
101+
},
102+
85103
'click @ui.save': function (e) {
86104
e.preventDefault();
87105

@@ -93,6 +111,16 @@ module.exports = Mn.View.extend({
93111
let view = this;
94112
let data = this.ui.form.serializeJSON();
95113

114+
// Add locations
115+
data.locations = [];
116+
this.locationsCollection.models.forEach((location) => {
117+
data.locations.push(location.toJSON());
118+
});
119+
120+
// Serialize collects path from custom locations
121+
// This field must be removed from root object
122+
delete data.path;
123+
96124
// Manipulate
97125
data.forward_port = parseInt(data.forward_port, 10);
98126
data.block_exploits = !!data.block_exploits;
@@ -246,5 +274,20 @@ module.exports = Mn.View.extend({
246274
if (typeof options.model === 'undefined' || !options.model) {
247275
this.model = new ProxyHostModel.Model();
248276
}
277+
278+
this.locationsCollection = new ProxyLocationModel.Collection();
279+
280+
// Custom locations
281+
this.showChildView('locations_regions', new CustomLocation.LocationCollectionView({
282+
collection: this.locationsCollection
283+
}));
284+
285+
// Check wether there are any location defined
286+
if (options.model && Array.isArray(options.model.attributes.locations)) {
287+
options.model.attributes.locations.forEach((location) => {
288+
let m = new ProxyLocationModel.Model(location);
289+
this.locationsCollection.add(m);
290+
});
291+
}
249292
}
250293
});

0 commit comments

Comments
 (0)