diff --git a/backend/migrations/20240629165112_stream_load_balance.js b/backend/migrations/20240629165112_stream_load_balance.js new file mode 100644 index 00000000..da5db668 --- /dev/null +++ b/backend/migrations/20240629165112_stream_load_balance.js @@ -0,0 +1,42 @@ +const migrate_name = 'stream_load_balance'; +const logger = require('../logger').migrate; + +/** + * Migrate + * + * @see http://knexjs.org/#Schema + * + * @param {Object} knex + * @param {Promise} Promise + * @returns {Promise} + */ +exports.up = function (knex) { + logger.info('[' + migrate_name + '] Migrating Up...'); + + return knex.schema + .table('stream', (table) => { + table.renameColumn('forwarding_host', 'forwarding_hosts'); + }) + .then(function () { + logger.info('[' + migrate_name + '] stream Table altered'); + }); +}; + +/** + * Undo Migrate + * + * @param {Object} knex + * @param {Promise} Promise + * @returns {Promise} + */ +exports.down = function (knex) { + logger.info('[' + migrate_name + '] Migrating Down...'); + + return knex.schema + .table('stream', (table) => { + table.renameColumn('forwarding_hosts', 'forwarding_host'); + }) + .then(function () { + logger.info('[' + migrate_name + '] stream Table altered'); + }); +}; diff --git a/backend/models/stream.js b/backend/models/stream.js index 7d84d2c3..00f2a67e 100644 --- a/backend/models/stream.js +++ b/backend/models/stream.js @@ -9,33 +9,43 @@ const now = require('./now_helper'); Model.knex(db); class Stream extends Model { - $beforeInsert () { + $beforeInsert() { this.created_on = now(); this.modified_on = now(); + // Default for forwarding_hosts + if (typeof this.forwarding_hosts === 'undefined') { + this.forwarding_hosts = []; + } + // Default for meta if (typeof this.meta === 'undefined') { this.meta = {}; } } - $beforeUpdate () { + $beforeUpdate() { this.modified_on = now(); + + // Sort domain_names + if (typeof this.forwarding_hosts !== 'undefined') { + this.forwarding_hosts.sort(); + } } - static get name () { + static get name() { return 'Stream'; } - static get tableName () { + static get tableName() { return 'stream'; } - static get jsonAttributes () { - return ['meta']; + static get jsonAttributes() { + return ['forwarding_hosts', 'meta']; } - static get relationMappings () { + static get relationMappings() { return { owner: { relation: Model.HasOneRelation, diff --git a/backend/schema/endpoints/streams.json b/backend/schema/endpoints/streams.json index 159c8036..da3de83e 100644 --- a/backend/schema/endpoints/streams.json +++ b/backend/schema/endpoints/streams.json @@ -20,7 +20,7 @@ "minimum": 1, "maximum": 65535 }, - "forwarding_host": { + "host": { "anyOf": [ { "$ref": "../definitions.json#/definitions/domain_name" @@ -35,6 +35,22 @@ } ] }, + "forwarding_hosts": { + "anyOf": [ + { + "$ref": "#/definitions/host" + }, + { + "type": "array", + "minItems": 1, + "maxItems": 15, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/host" + } + } + ] + }, "forwarding_port": { "type": "integer", "minimum": 1, @@ -66,8 +82,8 @@ "incoming_port": { "$ref": "#/definitions/incoming_port" }, - "forwarding_host": { - "$ref": "#/definitions/forwarding_host" + "forwarding_hosts": { + "$ref": "#/definitions/forwarding_hosts" }, "forwarding_port": { "$ref": "#/definitions/forwarding_port" @@ -118,15 +134,15 @@ "additionalProperties": false, "required": [ "incoming_port", - "forwarding_host", + "forwarding_hosts", "forwarding_port" ], "properties": { "incoming_port": { "$ref": "#/definitions/incoming_port" }, - "forwarding_host": { - "$ref": "#/definitions/forwarding_host" + "forwarding_hosts": { + "$ref": "#/definitions/forwarding_hosts" }, "forwarding_port": { "$ref": "#/definitions/forwarding_port" @@ -165,8 +181,8 @@ "incoming_port": { "$ref": "#/definitions/incoming_port" }, - "forwarding_host": { - "$ref": "#/definitions/forwarding_host" + "forwarding_hosts": { + "$ref": "#/definitions/forwarding_hosts" }, "forwarding_port": { "$ref": "#/definitions/forwarding_port" @@ -231,4 +247,4 @@ } } ] -} +} \ No newline at end of file diff --git a/backend/templates/stream.conf b/backend/templates/stream.conf index 76159a64..6c328c14 100644 --- a/backend/templates/stream.conf +++ b/backend/templates/stream.conf @@ -3,6 +3,21 @@ # ------------------------------------------------------------ {% if enabled %} + +upstream stream_{{ incoming_port }}_tcp { + {% if forwarding_hosts.length > 1 -%} + least_conn; + {%- endif -%} + + {% for forwarding_host in forwarding_hosts %} + {% if forloop.first == true and forloop.last == true -%} + server {{ forwarding_host }}:{{ forwarding_port }}; + {%- else -%} + server {{ forwarding_host}}:{{ forwarding_port}} max_fails=3; + {%- endif %} + {%- endfor %} +} + {% if tcp_forwarding == 1 or tcp_forwarding == true -%} server { listen {{ incoming_port }}; @@ -12,7 +27,7 @@ server { #listen [::]:{{ incoming_port }}; {% endif %} - proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; + proxy_pass stream_{{ incoming_port }}_tcp; # Custom include /data/nginx/custom/server_stream[.]conf; @@ -20,18 +35,26 @@ server { } {% endif %} {% if udp_forwarding == 1 or udp_forwarding == true %} + +upstream stream_{{ incoming_port }}_udp { + {% for forwarding_host in forwarding_hosts %} + server {{ forwarding_host }}:{{ forwarding_port }}; + {%- endfor %} +} + server { listen {{ incoming_port }} udp; {% if ipv6 -%} listen [::]:{{ incoming_port }} udp; {% else -%} - #listen [::]:{{ incoming_port }} udp; + #listen [::]:{{ incoming_port }} udp; {% endif %} - proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; + + proxy_pass stream_{{ incoming_port }}_udp; # Custom include /data/nginx/custom/server_stream[.]conf; include /data/nginx/custom/server_stream_udp[.]conf; } {% endif %} -{% endif %} \ No newline at end of file +{% endif %} diff --git a/frontend/js/app/nginx/stream/form.ejs b/frontend/js/app/nginx/stream/form.ejs index 1fc4f134..7db7ac8d 100644 --- a/frontend/js/app/nginx/stream/form.ejs +++ b/frontend/js/app/nginx/stream/form.ejs @@ -2,7 +2,7 @@