diff --git a/manager/package.json b/manager/package.json index 4f3bedc3..d6acfa0b 100644 --- a/manager/package.json +++ b/manager/package.json @@ -1,6 +1,6 @@ { "name": "nginx-proxy-manager", - "version": "1.0.1", + "version": "1.1.0", "description": "Nginx proxt with built in Web based management", "main": "src/backend/index.js", "dependencies": { diff --git a/manager/src/backend/internal/host.js b/manager/src/backend/internal/host.js index 1cdb667d..de3a24a8 100644 --- a/manager/src/backend/internal/host.js +++ b/manager/src/backend/internal/host.js @@ -44,19 +44,31 @@ const internalHost = { */ create: payload => { return new Promise((resolve, reject) => { - // Enforce lowercase hostnames - payload.hostname = payload.hostname.toLowerCase(); + let existing_host = false; - // 1. Check that the hostname doesn't already exist - let existing_host = db.hosts.findOne({hostname: payload.hostname}); + if (payload.type === 'stream') { + // Check that the incoming port doesn't already exist + existing_host = db.hosts.findOne({incoming_port: payload.incoming_port}); + + if (payload.incoming_port === 80 || payload.incoming_port === 81 || payload.incoming_port === 443) { + reject(new error.ConfigurationError('Port ' + payload.incoming_port + ' is reserved')); + return; + } + + } else { + payload.hostname = payload.hostname.toLowerCase(); + + // Check that the hostname doesn't already exist + existing_host = db.hosts.findOne({hostname: payload.hostname}); + } if (existing_host) { reject(new error.ValidationError('Hostname already exists')); } else { - // 2. Add host to db + // Add host to db let host = db.hosts.save(payload); - // 3. Fire the config generation for this host + // Fire the config generation for this host internalHost.configure(host, true) .then((/*result*/) => { resolve(host); @@ -98,10 +110,16 @@ const internalHost = { } // Check that the hostname doesn't already exist - let other_host = db.hosts.findOne({hostname: payload.hostname}); + let other_host = false; + + if (typeof payload.incoming_port !== 'undefined') { + other_host = db.hosts.findOne({incoming_port: payload.incoming_port}); + } else { + other_host = db.hosts.findOne({hostname: payload.hostname}); + } if (other_host && other_host._id !== id) { - reject(new error.ValidationError('Hostname already exists')); + reject(new error.ValidationError((other_host.type === 'stream' ? 'Source Stream Port' : 'Hostname') + ' already exists')); } else { // 2. Update host db.hosts.update({_id: id}, payload, {multi: false, upsert: false}); @@ -126,17 +144,22 @@ const internalHost = { return data; }) .then(data => { - if ( - (data.original.ssl && !data.updated.ssl) || // ssl was enabled and is now disabled - (data.original.ssl && data.original.hostname !== data.updated.hostname) // hostname was changed for a previously ssl-enabled host - ) { - // SSL was turned off or hostname for ssl has changed so we should remove certs for the original - return internalSsl.deleteCerts(data.original) - .then(() => { - db.hosts.update({_id: data.updated._id}, {ssl_expires: 0}, {multi: false, upsert: false}); - data.updated.ssl_expires = 0; - return data; - }); + if (data.updated.type !== 'stream') { + if ( + (data.original.ssl && !data.updated.ssl) || // ssl was enabled and is now disabled + (data.original.ssl && data.original.hostname !== data.updated.hostname) // hostname was changed for a previously ssl-enabled host + ) { + // SSL was turned off or hostname for ssl has changed so we should remove certs for the original + return internalSsl.deleteCerts(data.original) + .then(() => { + db.hosts.update({_id: data.updated._id}, {ssl_expires: 0}, { + multi: false, + upsert: false + }); + data.updated.ssl_expires = 0; + return data; + }); + } } return data; diff --git a/manager/src/backend/internal/nginx.js b/manager/src/backend/internal/nginx.js index 1aa40688..c05d50d8 100644 --- a/manager/src/backend/internal/nginx.js +++ b/manager/src/backend/internal/nginx.js @@ -32,6 +32,10 @@ const internalNginx = { * @returns {String} */ getConfigName: host => { + if (host.type === 'stream') { + return '/config/nginx/stream/' + host.incoming_port + '.conf'; + } + return '/config/nginx/' + host.hostname + '.conf'; }, diff --git a/manager/src/backend/schema/endpoints/hosts.json b/manager/src/backend/schema/endpoints/hosts.json index 6e0ed9f9..1949e4f6 100644 --- a/manager/src/backend/schema/endpoints/hosts.json +++ b/manager/src/backend/schema/endpoints/hosts.json @@ -12,7 +12,7 @@ }, "type": { "type": "string", - "pattern": "^(proxy|redirection|404)$" + "pattern": "^(proxy|redirection|404|stream)$" }, "hostname": { "$ref": "../definitions.json#/definitions/hostname" @@ -59,6 +59,17 @@ "access_list": { "type": "object", "readonly": true + }, + "incoming_port": { + "type": "integer", + "minumum": 1, + "maxiumum": 65535 + }, + "protocols": { + "type": "array", + "items": { + "type": "string" + } } }, "links": [ @@ -86,8 +97,7 @@ "schema": { "type": "object", "required": [ - "type", - "hostname" + "type" ], "properties": { "type": { @@ -125,6 +135,12 @@ }, "access_list_id": { "$ref": "#/definitions/access_list_id" + }, + "incoming_port": { + "$ref": "#/definitions/incoming_port" + }, + "protocols": { + "$ref": "#/definitions/protocols" } } }, @@ -181,6 +197,12 @@ }, "access_list_id": { "$ref": "#/definitions/access_list_id" + }, + "incoming_port": { + "$ref": "#/definitions/incoming_port" + }, + "protocols": { + "$ref": "#/definitions/protocols" } } }, @@ -247,6 +269,12 @@ }, "advanced": { "$ref": "#/definitions/advanced" + }, + "incoming_port": { + "$ref": "#/definitions/incoming_port" + }, + "protocols": { + "$ref": "#/definitions/protocols" } } } diff --git a/manager/src/backend/templates/stream.conf.ejs b/manager/src/backend/templates/stream.conf.ejs new file mode 100644 index 00000000..49994a26 --- /dev/null +++ b/manager/src/backend/templates/stream.conf.ejs @@ -0,0 +1,11 @@ +# <%- incoming_port %> - <%- protocols.join(',').toUpperCase() %> +<% +protocols.forEach(function (protocol) { +%> +server { + listen <%- incoming_port %> <%- protocol === 'tcp' ? '' : protocol %>; + proxy_pass <%- forward_server %>:<%- forward_port %>; +} +<% +}); +%> diff --git a/manager/src/frontend/js/app/controller.js b/manager/src/frontend/js/app/controller.js index 789fd951..5893f5c9 100644 --- a/manager/src/frontend/js/app/controller.js +++ b/manager/src/frontend/js/app/controller.js @@ -86,6 +86,17 @@ module.exports = { }); }, + /** + * 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 * diff --git a/manager/src/frontend/js/app/dashboard/main.ejs b/manager/src/frontend/js/app/dashboard/main.ejs index 6759348b..6df4f241 100644 --- a/manager/src/frontend/js/app/dashboard/main.ejs +++ b/manager/src/frontend/js/app/dashboard/main.ejs @@ -1,6 +1,6 @@
Hostname | +Source | Destination | SSL | Access List | @@ -13,6 +13,7 @@<%- hostname %> | ++ <% if (type === 'stream') { %> + <%- incoming_port %> + <%- protocols.join(', ').toUpperCase() %> + <% } else { %> + <%- hostname %> + <% } %> + | - <% if (type === 'proxy') { %> + <% if (type === 'proxy' || type === 'stream') { %> <%- forward_server %>:<%- forward_port %> <% } else if (type === 'redirection') { %> <%- forward_host %> @@ -11,19 +18,27 @@ | - <% if (ssl && force_ssl) { %> - Forced - <% } else if (ssl) { %> - Enabled + <% if (type === 'stream') { %> + - <% } else { %> - No + <% if (ssl && force_ssl) { %> + Forced + <% } else if (ssl) { %> + Enabled + <% } else { %> + No + <% } %> <% } %> | - <% if (access_list) { %> - <%- access_list.name %> + <% if (type === 'stream') { %> + - <% } else { %> - None + <% if (access_list) { %> + <%- access_list.name %> + <% } else { %> + None + <% } %> <% } %> | @@ -31,7 +46,7 @@ <% } %> - + | diff --git a/manager/src/frontend/js/app/dashboard/row.js b/manager/src/frontend/js/app/dashboard/row.js index 23eab1d0..0c403f99 100644 --- a/manager/src/frontend/js/app/dashboard/row.js +++ b/manager/src/frontend/js/app/dashboard/row.js @@ -32,6 +32,9 @@ module.exports = Mn.View.extend({ case '404': Controller.show404HostForm(this.model); break; + case 'stream': + Controller.showStreamHostForm(this.model); + break; } }, diff --git a/manager/src/frontend/js/app/host/stream_form.ejs b/manager/src/frontend/js/app/host/stream_form.ejs new file mode 100644 index 00000000..9a52b3c2 --- /dev/null +++ b/manager/src/frontend/js/app/host/stream_form.ejs @@ -0,0 +1,55 @@ +
---|