mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-11-04 09:25:15 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v2.12.4
			...
			33cf8ba1c9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					33cf8ba1c9 | ||
| 
						 | 
					04079e7c10 | 
							
								
								
									
										42
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							@@ -128,7 +128,7 @@ pipeline {
 | 
			
		||||
					sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
 | 
			
		||||
				}
 | 
			
		||||
				unstable {
 | 
			
		||||
					dir(path: 'test/results') {
 | 
			
		||||
					dir(path: 'testing/results') {
 | 
			
		||||
						archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
@@ -161,45 +161,7 @@ pipeline {
 | 
			
		||||
					sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
 | 
			
		||||
				}
 | 
			
		||||
				unstable {
 | 
			
		||||
					dir(path: 'test/results') {
 | 
			
		||||
						archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		stage('Test Postgres') {
 | 
			
		||||
			environment {
 | 
			
		||||
				COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_postgres"
 | 
			
		||||
				COMPOSE_FILE         = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.postgres.yml'
 | 
			
		||||
			}
 | 
			
		||||
			when {
 | 
			
		||||
				not {
 | 
			
		||||
					equals expected: 'UNSTABLE', actual: currentBuild.result
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			steps {
 | 
			
		||||
				sh 'rm -rf ./test/results/junit/*'
 | 
			
		||||
				sh './scripts/ci/fulltest-cypress'
 | 
			
		||||
			}
 | 
			
		||||
			post {
 | 
			
		||||
				always {
 | 
			
		||||
					// Dumps to analyze later
 | 
			
		||||
					sh 'mkdir -p debug/postgres'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/postgres/docker_fullstack.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q stepca) > debug/postgres/docker_stepca.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q pdns) > debug/postgres/docker_pdns.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/postgres/docker_pdns-db.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/postgres/docker_dnsrouter.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q db-postgres) > debug/postgres/docker_db-postgres.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q authentik) > debug/postgres/docker_authentik.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q authentik-redis) > debug/postgres/docker_authentik-redis.log 2>&1'
 | 
			
		||||
					sh 'docker logs $(docker-compose ps --all -q authentik-ldap) > debug/postgres/docker_authentik-ldap.log 2>&1'
 | 
			
		||||
 | 
			
		||||
					junit 'test/results/junit/*'
 | 
			
		||||
					sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
 | 
			
		||||
				}
 | 
			
		||||
				unstable {
 | 
			
		||||
					dir(path: 'test/results') {
 | 
			
		||||
					dir(path: 'testing/results') {
 | 
			
		||||
						archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
	<img src="https://nginxproxymanager.com/github.png">
 | 
			
		||||
	<br><br>
 | 
			
		||||
	<img src="https://img.shields.io/badge/version-2.12.4-green.svg?style=for-the-badge">
 | 
			
		||||
	<img src="https://img.shields.io/badge/version-2.12.1-green.svg?style=for-the-badge">
 | 
			
		||||
	<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
 | 
			
		||||
		<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
 | 
			
		||||
	</a>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
const schema = require('./schema');
 | 
			
		||||
const logger = require('./logger').global;
 | 
			
		||||
 | 
			
		||||
const IP_RANGES_FETCH_ENABLED = process.env.IP_RANGES_FETCH_ENABLED !== 'false';
 | 
			
		||||
 | 
			
		||||
async function appStart () {
 | 
			
		||||
	const migrate             = require('./migrate');
 | 
			
		||||
	const setup               = require('./setup');
 | 
			
		||||
@@ -15,16 +13,7 @@ async function appStart () {
 | 
			
		||||
	return migrate.latest()
 | 
			
		||||
		.then(setup)
 | 
			
		||||
		.then(schema.getCompiledSchema)
 | 
			
		||||
		.then(() => {
 | 
			
		||||
			if (IP_RANGES_FETCH_ENABLED) {
 | 
			
		||||
				logger.info('IP Ranges fetch is enabled');
 | 
			
		||||
				return internalIpRanges.fetch().catch((err) => {
 | 
			
		||||
					logger.error('IP Ranges fetch failed, continuing anyway:', err.message);
 | 
			
		||||
				});
 | 
			
		||||
			} else {
 | 
			
		||||
				logger.info('IP Ranges fetch is disabled by environment variable');
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		.then(internalIpRanges.fetch)
 | 
			
		||||
		.then(() => {
 | 
			
		||||
			internalCertificate.initTimer();
 | 
			
		||||
			internalIpRanges.initTimer();
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,7 @@ const internalAccessList = {
 | 
			
		||||
 | 
			
		||||
				return internalAccessList.build(row)
 | 
			
		||||
					.then(() => {
 | 
			
		||||
						if (parseInt(row.proxy_host_count, 10)) {
 | 
			
		||||
						if (row.proxy_host_count) {
 | 
			
		||||
							return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
 | 
			
		||||
						}
 | 
			
		||||
					})
 | 
			
		||||
@@ -223,7 +223,7 @@ const internalAccessList = {
 | 
			
		||||
			.then((row) => {
 | 
			
		||||
				return internalAccessList.build(row)
 | 
			
		||||
					.then(() => {
 | 
			
		||||
						if (parseInt(row.proxy_host_count, 10)) {
 | 
			
		||||
						if (row.proxy_host_count) {
 | 
			
		||||
							return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
 | 
			
		||||
						}
 | 
			
		||||
					}).then(internalNginx.reload)
 | 
			
		||||
@@ -252,13 +252,9 @@ const internalAccessList = {
 | 
			
		||||
				let query = accessListModel
 | 
			
		||||
					.query()
 | 
			
		||||
					.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
 | 
			
		||||
					.leftJoin('proxy_host', function() {
 | 
			
		||||
						this.on('proxy_host.access_list_id', '=', 'access_list.id')
 | 
			
		||||
							.andOn('proxy_host.is_deleted', '=', 0);
 | 
			
		||||
					})
 | 
			
		||||
					.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
 | 
			
		||||
					.where('access_list.is_deleted', 0)
 | 
			
		||||
					.andWhere('access_list.id', data.id)
 | 
			
		||||
					.groupBy('access_list.id')
 | 
			
		||||
					.allowGraph('[owner,items,clients,proxy_hosts.[certificate,access_list.[clients,items]]]')
 | 
			
		||||
					.first();
 | 
			
		||||
 | 
			
		||||
@@ -377,10 +373,7 @@ const internalAccessList = {
 | 
			
		||||
				let query = accessListModel
 | 
			
		||||
					.query()
 | 
			
		||||
					.select('access_list.*', accessListModel.raw('COUNT(proxy_host.id) as proxy_host_count'))
 | 
			
		||||
					.leftJoin('proxy_host', function() {
 | 
			
		||||
						this.on('proxy_host.access_list_id', '=', 'access_list.id')
 | 
			
		||||
							.andOn('proxy_host.is_deleted', '=', 0);
 | 
			
		||||
					})
 | 
			
		||||
					.joinRaw('LEFT JOIN `proxy_host` ON `proxy_host`.`access_list_id` = `access_list`.`id` AND `proxy_host`.`is_deleted` = 0')
 | 
			
		||||
					.where('access_list.is_deleted', 0)
 | 
			
		||||
					.groupBy('access_list.id')
 | 
			
		||||
					.allowGraph('[owner,items,clients]')
 | 
			
		||||
@@ -508,13 +501,8 @@ const internalAccessList = {
 | 
			
		||||
								if (typeof item.password !== 'undefined' && item.password.length) {
 | 
			
		||||
									logger.info('Adding: ' + item.username);
 | 
			
		||||
 | 
			
		||||
									utils.execFile('openssl', ['passwd', '-apr1', item.password])
 | 
			
		||||
										.then((res) => {
 | 
			
		||||
											try {
 | 
			
		||||
												fs.appendFileSync(htpasswd_file, item.username + ':' + res + '\n', {encoding: 'utf8'});
 | 
			
		||||
											} catch (err) {
 | 
			
		||||
												reject(err);
 | 
			
		||||
											}
 | 
			
		||||
									utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
 | 
			
		||||
										.then((/*result*/) => {
 | 
			
		||||
											next();
 | 
			
		||||
										})
 | 
			
		||||
										.catch((err) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
const error            = require('../lib/error');
 | 
			
		||||
const auditLogModel    = require('../models/audit-log');
 | 
			
		||||
const {castJsonIfNeed} = require('../lib/helpers');
 | 
			
		||||
const error         = require('../lib/error');
 | 
			
		||||
const auditLogModel = require('../models/audit-log');
 | 
			
		||||
 | 
			
		||||
const internalAuditLog = {
 | 
			
		||||
 | 
			
		||||
@@ -23,9 +22,9 @@ const internalAuditLog = {
 | 
			
		||||
					.allowGraph('[user]');
 | 
			
		||||
 | 
			
		||||
				// Query is used for searching
 | 
			
		||||
				if (typeof search_query === 'string' && search_query.length > 0) {
 | 
			
		||||
				if (typeof search_query === 'string') {
 | 
			
		||||
					query.where(function () {
 | 
			
		||||
						this.where(castJsonIfNeed('meta'), 'like', '%' + search_query + '%');
 | 
			
		||||
						this.where('meta', 'like', '%' + search_query + '%');
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -313,9 +313,6 @@ const internalCertificate = {
 | 
			
		||||
					.where('is_deleted', 0)
 | 
			
		||||
					.andWhere('id', data.id)
 | 
			
		||||
					.allowGraph('[owner]')
 | 
			
		||||
					.allowGraph('[proxy_hosts]')
 | 
			
		||||
					.allowGraph('[redirection_hosts]')
 | 
			
		||||
					.allowGraph('[dead_hosts]')
 | 
			
		||||
					.first();
 | 
			
		||||
 | 
			
		||||
				if (access_data.permission_visibility !== 'all') {
 | 
			
		||||
@@ -467,9 +464,6 @@ const internalCertificate = {
 | 
			
		||||
					.where('is_deleted', 0)
 | 
			
		||||
					.groupBy('id')
 | 
			
		||||
					.allowGraph('[owner]')
 | 
			
		||||
					.allowGraph('[proxy_hosts]')
 | 
			
		||||
					.allowGraph('[redirection_hosts]')
 | 
			
		||||
					.allowGraph('[dead_hosts]')
 | 
			
		||||
					.orderBy('nice_name', 'ASC');
 | 
			
		||||
 | 
			
		||||
				if (access_data.permission_visibility !== 'all') {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ const internalHost        = require('./host');
 | 
			
		||||
const internalNginx       = require('./nginx');
 | 
			
		||||
const internalAuditLog    = require('./audit-log');
 | 
			
		||||
const internalCertificate = require('./certificate');
 | 
			
		||||
const {castJsonIfNeed}    = require('../lib/helpers');
 | 
			
		||||
 | 
			
		||||
function omissions () {
 | 
			
		||||
	return ['is_deleted'];
 | 
			
		||||
@@ -410,16 +409,16 @@ const internalDeadHost = {
 | 
			
		||||
					.where('is_deleted', 0)
 | 
			
		||||
					.groupBy('id')
 | 
			
		||||
					.allowGraph('[owner,certificate]')
 | 
			
		||||
					.orderBy(castJsonIfNeed('domain_names'), 'ASC');
 | 
			
		||||
					.orderBy('domain_names', 'ASC');
 | 
			
		||||
 | 
			
		||||
				if (access_data.permission_visibility !== 'all') {
 | 
			
		||||
					query.andWhere('owner_user_id', access.token.getUserId(1));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Query is used for searching
 | 
			
		||||
				if (typeof search_query === 'string' && search_query.length > 0) {
 | 
			
		||||
				if (typeof search_query === 'string') {
 | 
			
		||||
					query.where(function () {
 | 
			
		||||
						this.where(castJsonIfNeed('domain_names'), 'like', '%' + search_query + '%');
 | 
			
		||||
						this.where('domain_names', 'like', '%' + search_query + '%');
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ const _                    = require('lodash');
 | 
			
		||||
const proxyHostModel       = require('../models/proxy_host');
 | 
			
		||||
const redirectionHostModel = require('../models/redirection_host');
 | 
			
		||||
const deadHostModel        = require('../models/dead_host');
 | 
			
		||||
const {castJsonIfNeed}     = require('../lib/helpers');
 | 
			
		||||
 | 
			
		||||
const internalHost = {
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +17,7 @@ const internalHost = {
 | 
			
		||||
	cleanSslHstsData: function (data, existing_data) {
 | 
			
		||||
		existing_data = existing_data === undefined ? {} : existing_data;
 | 
			
		||||
 | 
			
		||||
		const combined_data = _.assign({}, existing_data, data);
 | 
			
		||||
		let combined_data = _.assign({}, existing_data, data);
 | 
			
		||||
 | 
			
		||||
		if (!combined_data.certificate_id) {
 | 
			
		||||
			combined_data.ssl_forced    = false;
 | 
			
		||||
@@ -74,7 +73,7 @@ const internalHost = {
 | 
			
		||||
	 * @returns {Promise}
 | 
			
		||||
	 */
 | 
			
		||||
	getHostsWithDomains: function (domain_names) {
 | 
			
		||||
		const promises = [
 | 
			
		||||
		let promises = [
 | 
			
		||||
			proxyHostModel
 | 
			
		||||
				.query()
 | 
			
		||||
				.where('is_deleted', 0),
 | 
			
		||||
@@ -126,19 +125,19 @@ const internalHost = {
 | 
			
		||||
	 * @returns {Promise}
 | 
			
		||||
	 */
 | 
			
		||||
	isHostnameTaken: function (hostname, ignore_type, ignore_id) {
 | 
			
		||||
		const promises = [
 | 
			
		||||
		let promises = [
 | 
			
		||||
			proxyHostModel
 | 
			
		||||
				.query()
 | 
			
		||||
				.where('is_deleted', 0)
 | 
			
		||||
				.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
 | 
			
		||||
				.andWhere('domain_names', 'like', '%' + hostname + '%'),
 | 
			
		||||
			redirectionHostModel
 | 
			
		||||
				.query()
 | 
			
		||||
				.where('is_deleted', 0)
 | 
			
		||||
				.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%'),
 | 
			
		||||
				.andWhere('domain_names', 'like', '%' + hostname + '%'),
 | 
			
		||||
			deadHostModel
 | 
			
		||||
				.query()
 | 
			
		||||
				.where('is_deleted', 0)
 | 
			
		||||
				.andWhere(castJsonIfNeed('domain_names'), 'like', '%' + hostname + '%')
 | 
			
		||||
				.andWhere('domain_names', 'like', '%' + hostname + '%')
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		return Promise.all(promises)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ const internalHost        = require('./host');
 | 
			
		||||
const internalNginx       = require('./nginx');
 | 
			
		||||
const internalAuditLog    = require('./audit-log');
 | 
			
		||||
const internalCertificate = require('./certificate');
 | 
			
		||||
const {castJsonIfNeed}    = require('../lib/helpers');
 | 
			
		||||
 | 
			
		||||
function omissions () {
 | 
			
		||||
	return ['is_deleted', 'owner.is_deleted'];
 | 
			
		||||
@@ -417,16 +416,16 @@ const internalProxyHost = {
 | 
			
		||||
					.where('is_deleted', 0)
 | 
			
		||||
					.groupBy('id')
 | 
			
		||||
					.allowGraph('[owner,access_list,certificate]')
 | 
			
		||||
					.orderBy(castJsonIfNeed('domain_names'), 'ASC');
 | 
			
		||||
					.orderBy('domain_names', 'ASC');
 | 
			
		||||
 | 
			
		||||
				if (access_data.permission_visibility !== 'all') {
 | 
			
		||||
					query.andWhere('owner_user_id', access.token.getUserId(1));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Query is used for searching
 | 
			
		||||
				if (typeof search_query === 'string' && search_query.length > 0) {
 | 
			
		||||
				if (typeof search_query === 'string') {
 | 
			
		||||
					query.where(function () {
 | 
			
		||||
						this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
 | 
			
		||||
						this.where('domain_names', 'like', '%' + search_query + '%');
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ const internalHost         = require('./host');
 | 
			
		||||
const internalNginx        = require('./nginx');
 | 
			
		||||
const internalAuditLog     = require('./audit-log');
 | 
			
		||||
const internalCertificate  = require('./certificate');
 | 
			
		||||
const {castJsonIfNeed}     = require('../lib/helpers');
 | 
			
		||||
 | 
			
		||||
function omissions () {
 | 
			
		||||
	return ['is_deleted'];
 | 
			
		||||
@@ -410,16 +409,16 @@ const internalRedirectionHost = {
 | 
			
		||||
					.where('is_deleted', 0)
 | 
			
		||||
					.groupBy('id')
 | 
			
		||||
					.allowGraph('[owner,certificate]')
 | 
			
		||||
					.orderBy(castJsonIfNeed('domain_names'), 'ASC');
 | 
			
		||||
					.orderBy('domain_names', 'ASC');
 | 
			
		||||
 | 
			
		||||
				if (access_data.permission_visibility !== 'all') {
 | 
			
		||||
					query.andWhere('owner_user_id', access.token.getUserId(1));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Query is used for searching
 | 
			
		||||
				if (typeof search_query === 'string' && search_query.length > 0) {
 | 
			
		||||
				if (typeof search_query === 'string') {
 | 
			
		||||
					query.where(function () {
 | 
			
		||||
						this.where(castJsonIfNeed('domain_names'), 'like', `%${search_query}%`);
 | 
			
		||||
						this.where('domain_names', 'like', '%' + search_query + '%');
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,12 @@
 | 
			
		||||
const _                   = require('lodash');
 | 
			
		||||
const error               = require('../lib/error');
 | 
			
		||||
const utils               = require('../lib/utils');
 | 
			
		||||
const streamModel         = require('../models/stream');
 | 
			
		||||
const internalNginx       = require('./nginx');
 | 
			
		||||
const internalAuditLog    = require('./audit-log');
 | 
			
		||||
const internalCertificate = require('./certificate');
 | 
			
		||||
const internalHost        = require('./host');
 | 
			
		||||
const {castJsonIfNeed}    = require('../lib/helpers');
 | 
			
		||||
const _                = require('lodash');
 | 
			
		||||
const error            = require('../lib/error');
 | 
			
		||||
const utils            = require('../lib/utils');
 | 
			
		||||
const streamModel      = require('../models/stream');
 | 
			
		||||
const internalNginx    = require('./nginx');
 | 
			
		||||
const internalAuditLog = require('./audit-log');
 | 
			
		||||
 | 
			
		||||
function omissions () {
 | 
			
		||||
	return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
 | 
			
		||||
	return ['is_deleted'];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const internalStream = {
 | 
			
		||||
@@ -20,12 +17,6 @@ const internalStream = {
 | 
			
		||||
	 * @returns {Promise}
 | 
			
		||||
	 */
 | 
			
		||||
	create: (access, data) => {
 | 
			
		||||
		const create_certificate = data.certificate_id === 'new';
 | 
			
		||||
 | 
			
		||||
		if (create_certificate) {
 | 
			
		||||
			delete data.certificate_id;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return access.can('streams:create', data)
 | 
			
		||||
			.then((/*access_data*/) => {
 | 
			
		||||
				// TODO: At this point the existing ports should have been checked
 | 
			
		||||
@@ -35,44 +26,16 @@ const internalStream = {
 | 
			
		||||
					data.meta = {};
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// streams aren't routed by domain name so don't store domain names in the DB
 | 
			
		||||
				let data_no_domains = structuredClone(data);
 | 
			
		||||
				delete data_no_domains.domain_names;
 | 
			
		||||
 | 
			
		||||
				return streamModel
 | 
			
		||||
					.query()
 | 
			
		||||
					.insertAndFetch(data_no_domains)
 | 
			
		||||
					.insertAndFetch(data)
 | 
			
		||||
					.then(utils.omitRow(omissions()));
 | 
			
		||||
			})
 | 
			
		||||
			.then((row) => {
 | 
			
		||||
				if (create_certificate) {
 | 
			
		||||
					return internalCertificate.createQuickCertificate(access, data)
 | 
			
		||||
						.then((cert) => {
 | 
			
		||||
							// update host with cert id
 | 
			
		||||
							return internalStream.update(access, {
 | 
			
		||||
								id:             row.id,
 | 
			
		||||
								certificate_id: cert.id
 | 
			
		||||
							});
 | 
			
		||||
						})
 | 
			
		||||
						.then(() => {
 | 
			
		||||
							return row;
 | 
			
		||||
						});
 | 
			
		||||
				} else {
 | 
			
		||||
					return row;
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
			.then((row) => {
 | 
			
		||||
				// re-fetch with cert
 | 
			
		||||
				return internalStream.get(access, {
 | 
			
		||||
					id:     row.id,
 | 
			
		||||
					expand: ['certificate', 'owner']
 | 
			
		||||
				});
 | 
			
		||||
			})
 | 
			
		||||
			.then((row) => {
 | 
			
		||||
				// Configure nginx
 | 
			
		||||
				return internalNginx.configure(streamModel, 'stream', row)
 | 
			
		||||
					.then(() => {
 | 
			
		||||
						return row;
 | 
			
		||||
						return internalStream.get(access, {id: row.id, expand: ['owner']});
 | 
			
		||||
					});
 | 
			
		||||
			})
 | 
			
		||||
			.then((row) => {
 | 
			
		||||
@@ -96,12 +59,6 @@ const internalStream = {
 | 
			
		||||
	 * @return {Promise}
 | 
			
		||||
	 */
 | 
			
		||||
	update: (access, data) => {
 | 
			
		||||
		const create_certificate = data.certificate_id === 'new';
 | 
			
		||||
 | 
			
		||||
		if (create_certificate) {
 | 
			
		||||
			delete data.certificate_id;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return access.can('streams:update', data.id)
 | 
			
		||||
			.then((/*access_data*/) => {
 | 
			
		||||
				// TODO: at this point the existing streams should have been checked
 | 
			
		||||
@@ -113,32 +70,16 @@ const internalStream = {
 | 
			
		||||
					throw new error.InternalValidationError('Stream could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (create_certificate) {
 | 
			
		||||
					return internalCertificate.createQuickCertificate(access, {
 | 
			
		||||
						domain_names: data.domain_names || row.domain_names,
 | 
			
		||||
						meta:         _.assign({}, row.meta, data.meta)
 | 
			
		||||
					})
 | 
			
		||||
						.then((cert) => {
 | 
			
		||||
							// update host with cert id
 | 
			
		||||
							data.certificate_id = cert.id;
 | 
			
		||||
						})
 | 
			
		||||
						.then(() => {
 | 
			
		||||
							return row;
 | 
			
		||||
						});
 | 
			
		||||
				} else {
 | 
			
		||||
					return row;
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
			.then((row) => {
 | 
			
		||||
				// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
 | 
			
		||||
				data = _.assign({}, {
 | 
			
		||||
					domain_names: row.domain_names
 | 
			
		||||
				}, data);
 | 
			
		||||
 | 
			
		||||
				return streamModel
 | 
			
		||||
					.query()
 | 
			
		||||
					.patchAndFetchById(row.id, data)
 | 
			
		||||
					.then(utils.omitRow(omissions()))
 | 
			
		||||
					.then((saved_row) => {
 | 
			
		||||
						return internalNginx.configure(streamModel, 'stream', saved_row)
 | 
			
		||||
							.then(() => {
 | 
			
		||||
								return internalStream.get(access, {id: row.id, expand: ['owner']});
 | 
			
		||||
							});
 | 
			
		||||
					})
 | 
			
		||||
					.then((saved_row) => {
 | 
			
		||||
						// Add to audit log
 | 
			
		||||
						return internalAuditLog.add(access, {
 | 
			
		||||
@@ -151,17 +92,6 @@ const internalStream = {
 | 
			
		||||
								return saved_row;
 | 
			
		||||
							});
 | 
			
		||||
					});
 | 
			
		||||
			})
 | 
			
		||||
			.then(() => {
 | 
			
		||||
				return internalStream.get(access, {id: data.id, expand: ['owner', 'certificate']})
 | 
			
		||||
					.then((row) => {
 | 
			
		||||
						return internalNginx.configure(streamModel, 'stream', row)
 | 
			
		||||
							.then((new_meta) => {
 | 
			
		||||
								row.meta = new_meta;
 | 
			
		||||
								row      = internalHost.cleanRowCertificateMeta(row);
 | 
			
		||||
								return _.omit(row, omissions());
 | 
			
		||||
							});
 | 
			
		||||
					});
 | 
			
		||||
			});
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +114,7 @@ const internalStream = {
 | 
			
		||||
					.query()
 | 
			
		||||
					.where('is_deleted', 0)
 | 
			
		||||
					.andWhere('id', data.id)
 | 
			
		||||
					.allowGraph('[owner,certificate]')
 | 
			
		||||
					.allowGraph('[owner]')
 | 
			
		||||
					.first();
 | 
			
		||||
 | 
			
		||||
				if (access_data.permission_visibility !== 'all') {
 | 
			
		||||
@@ -201,7 +131,6 @@ const internalStream = {
 | 
			
		||||
				if (!row || !row.id) {
 | 
			
		||||
					throw new error.ItemNotFoundError(data.id);
 | 
			
		||||
				}
 | 
			
		||||
				row = internalHost.cleanRowCertificateMeta(row);
 | 
			
		||||
				// Custom omissions
 | 
			
		||||
				if (typeof data.omit !== 'undefined' && data.omit !== null) {
 | 
			
		||||
					row = _.omit(row, data.omit);
 | 
			
		||||
@@ -267,14 +196,14 @@ const internalStream = {
 | 
			
		||||
			.then(() => {
 | 
			
		||||
				return internalStream.get(access, {
 | 
			
		||||
					id:     data.id,
 | 
			
		||||
					expand: ['certificate', 'owner']
 | 
			
		||||
					expand: ['owner']
 | 
			
		||||
				});
 | 
			
		||||
			})
 | 
			
		||||
			.then((row) => {
 | 
			
		||||
				if (!row || !row.id) {
 | 
			
		||||
					throw new error.ItemNotFoundError(data.id);
 | 
			
		||||
				} else if (row.enabled) {
 | 
			
		||||
					throw new error.ValidationError('Stream is already enabled');
 | 
			
		||||
					throw new error.ValidationError('Host is already enabled');
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				row.enabled = 1;
 | 
			
		||||
@@ -320,7 +249,7 @@ const internalStream = {
 | 
			
		||||
				if (!row || !row.id) {
 | 
			
		||||
					throw new error.ItemNotFoundError(data.id);
 | 
			
		||||
				} else if (!row.enabled) {
 | 
			
		||||
					throw new error.ValidationError('Stream is already disabled');
 | 
			
		||||
					throw new error.ValidationError('Host is already disabled');
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				row.enabled = 0;
 | 
			
		||||
@@ -364,11 +293,11 @@ const internalStream = {
 | 
			
		||||
	getAll: (access, expand, search_query) => {
 | 
			
		||||
		return access.can('streams:list')
 | 
			
		||||
			.then((access_data) => {
 | 
			
		||||
				const query = streamModel
 | 
			
		||||
				let query = streamModel
 | 
			
		||||
					.query()
 | 
			
		||||
					.where('is_deleted', 0)
 | 
			
		||||
					.groupBy('id')
 | 
			
		||||
					.allowGraph('[owner,certificate]')
 | 
			
		||||
					.allowGraph('[owner]')
 | 
			
		||||
					.orderBy('incoming_port', 'ASC');
 | 
			
		||||
 | 
			
		||||
				if (access_data.permission_visibility !== 'all') {
 | 
			
		||||
@@ -376,9 +305,9 @@ const internalStream = {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Query is used for searching
 | 
			
		||||
				if (typeof search_query === 'string' && search_query.length > 0) {
 | 
			
		||||
				if (typeof search_query === 'string') {
 | 
			
		||||
					query.where(function () {
 | 
			
		||||
						this.where(castJsonIfNeed('incoming_port'), 'like', `%${search_query}%`);
 | 
			
		||||
						this.where('incoming_port', 'like', '%' + search_query + '%');
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
@@ -387,13 +316,6 @@ const internalStream = {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return query.then(utils.omitRows(omissions()));
 | 
			
		||||
			})
 | 
			
		||||
			.then((rows) => {
 | 
			
		||||
				if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
 | 
			
		||||
					return internalHost.cleanAllRowsCertificateMeta(rows);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return rows;
 | 
			
		||||
			});
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -405,9 +327,9 @@ const internalStream = {
 | 
			
		||||
	 * @returns {Promise}
 | 
			
		||||
	 */
 | 
			
		||||
	getCount: (user_id, visibility) => {
 | 
			
		||||
		const query = streamModel
 | 
			
		||||
		let query = streamModel
 | 
			
		||||
			.query()
 | 
			
		||||
			.count('id AS count')
 | 
			
		||||
			.count('id as count')
 | 
			
		||||
			.where('is_deleted', 0);
 | 
			
		||||
 | 
			
		||||
		if (visibility !== 'all') {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,7 @@ const fs      = require('fs');
 | 
			
		||||
const NodeRSA = require('node-rsa');
 | 
			
		||||
const logger  = require('../logger').global;
 | 
			
		||||
 | 
			
		||||
const keysFile         = '/data/keys.json';
 | 
			
		||||
const mysqlEngine      = 'mysql2';
 | 
			
		||||
const postgresEngine   = 'pg';
 | 
			
		||||
const sqliteClientName = 'sqlite3';
 | 
			
		||||
const keysFile = '/data/keys.json';
 | 
			
		||||
 | 
			
		||||
let instance = null;
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +14,7 @@ const configure = () => {
 | 
			
		||||
		let configData;
 | 
			
		||||
		try {
 | 
			
		||||
			configData = require(filename);
 | 
			
		||||
		} catch (_) {
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
			// do nothing
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -37,7 +34,7 @@ const configure = () => {
 | 
			
		||||
		logger.info('Using MySQL configuration');
 | 
			
		||||
		instance = {
 | 
			
		||||
			database: {
 | 
			
		||||
				engine:   mysqlEngine,
 | 
			
		||||
				engine:   'mysql2',
 | 
			
		||||
				host:     envMysqlHost,
 | 
			
		||||
				port:     process.env.DB_MYSQL_PORT || 3306,
 | 
			
		||||
				user:     envMysqlUser,
 | 
			
		||||
@@ -49,33 +46,13 @@ const configure = () => {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const envPostgresHost = process.env.DB_POSTGRES_HOST || null;
 | 
			
		||||
	const envPostgresUser = process.env.DB_POSTGRES_USER || null;
 | 
			
		||||
	const envPostgresName = process.env.DB_POSTGRES_NAME || null;
 | 
			
		||||
	if (envPostgresHost && envPostgresUser && envPostgresName) {
 | 
			
		||||
		// we have enough postgres creds to go with postgres
 | 
			
		||||
		logger.info('Using Postgres configuration');
 | 
			
		||||
		instance = {
 | 
			
		||||
			database: {
 | 
			
		||||
				engine:   postgresEngine,
 | 
			
		||||
				host:     envPostgresHost,
 | 
			
		||||
				port:     process.env.DB_POSTGRES_PORT || 5432,
 | 
			
		||||
				user:     envPostgresUser,
 | 
			
		||||
				password: process.env.DB_POSTGRES_PASSWORD,
 | 
			
		||||
				name:     envPostgresName,
 | 
			
		||||
			},
 | 
			
		||||
			keys: getKeys(),
 | 
			
		||||
		};
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
 | 
			
		||||
	logger.info(`Using Sqlite: ${envSqliteFile}`);
 | 
			
		||||
	instance = {
 | 
			
		||||
		database: {
 | 
			
		||||
			engine: 'knex-native',
 | 
			
		||||
			knex:   {
 | 
			
		||||
				client:     sqliteClientName,
 | 
			
		||||
				client:     'sqlite3',
 | 
			
		||||
				connection: {
 | 
			
		||||
					filename: envSqliteFile
 | 
			
		||||
				},
 | 
			
		||||
@@ -166,27 +143,7 @@ module.exports = {
 | 
			
		||||
	 */
 | 
			
		||||
	isSqlite: function () {
 | 
			
		||||
		instance === null && configure();
 | 
			
		||||
		return instance.database.knex && instance.database.knex.client === sqliteClientName;
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Is this a mysql configuration?
 | 
			
		||||
	 *
 | 
			
		||||
	 * @returns {boolean}
 | 
			
		||||
	 */
 | 
			
		||||
	isMysql: function () {
 | 
			
		||||
		instance === null && configure();
 | 
			
		||||
		return instance.database.engine === mysqlEngine;
 | 
			
		||||
	},
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
		 * Is this a postgres configuration?
 | 
			
		||||
		 *
 | 
			
		||||
		 * @returns {boolean}
 | 
			
		||||
		 */
 | 
			
		||||
	isPostgres: function () {
 | 
			
		||||
		instance === null && configure();
 | 
			
		||||
		return instance.database.engine === postgresEngine;
 | 
			
		||||
		return instance.database.knex && instance.database.knex.client === 'sqlite3';
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,4 @@
 | 
			
		||||
const moment       = require('moment');
 | 
			
		||||
const {isPostgres} = require('./config');
 | 
			
		||||
const {ref}        = require('objection');
 | 
			
		||||
const moment = require('moment');
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
 | 
			
		||||
@@ -47,16 +45,6 @@ module.exports = {
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		return obj;
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Casts a column to json if using postgres
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param {string} colName
 | 
			
		||||
	 * @returns {string|Objection.ReferenceBuilder}
 | 
			
		||||
	 */
 | 
			
		||||
	castJsonIfNeed: function (colName) {
 | 
			
		||||
		return isPostgres() ? ref(colName).castText() : colName;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
const migrate_name = 'stream_ssl';
 | 
			
		||||
const logger       = require('../logger').migrate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Migrate
 | 
			
		||||
 *
 | 
			
		||||
 * @see http://knexjs.org/#Schema
 | 
			
		||||
 *
 | 
			
		||||
 * @param   {Object} knex
 | 
			
		||||
 * @returns {Promise}
 | 
			
		||||
 */
 | 
			
		||||
exports.up = function (knex) {
 | 
			
		||||
	logger.info('[' + migrate_name + '] Migrating Up...');
 | 
			
		||||
 | 
			
		||||
	return knex.schema.table('stream', (table) => {
 | 
			
		||||
		table.integer('certificate_id').notNull().unsigned().defaultTo(0);
 | 
			
		||||
	})
 | 
			
		||||
		.then(function () {
 | 
			
		||||
			logger.info('[' + migrate_name + '] stream Table altered');
 | 
			
		||||
		});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Undo Migrate
 | 
			
		||||
 *
 | 
			
		||||
 * @param   {Object} knex
 | 
			
		||||
 * @returns {Promise}
 | 
			
		||||
 */
 | 
			
		||||
exports.down = function (knex) {
 | 
			
		||||
	logger.info('[' + migrate_name + '] Migrating Down...');
 | 
			
		||||
 | 
			
		||||
	return knex.schema.table('stream', (table) => {
 | 
			
		||||
		table.dropColumn('certificate_id');
 | 
			
		||||
	})
 | 
			
		||||
		.then(function () {
 | 
			
		||||
			logger.info('[' + migrate_name + '] stream Table altered');
 | 
			
		||||
		});
 | 
			
		||||
};
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
const db      = require('../db');
 | 
			
		||||
const helpers = require('../lib/helpers');
 | 
			
		||||
const Model   = require('objection').Model;
 | 
			
		||||
const User    = require('./user');
 | 
			
		||||
const now     = require('./now_helper');
 | 
			
		||||
 | 
			
		||||
Model.knex(db);
 | 
			
		||||
@@ -67,11 +68,6 @@ class Certificate extends Model {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static get relationMappings () {
 | 
			
		||||
		const ProxyHost       = require('./proxy_host');
 | 
			
		||||
		const DeadHost        = require('./dead_host');
 | 
			
		||||
		const User            = require('./user');
 | 
			
		||||
		const RedirectionHost = require('./redirection_host');
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			owner: {
 | 
			
		||||
				relation:   Model.HasOneRelation,
 | 
			
		||||
@@ -83,39 +79,6 @@ class Certificate extends Model {
 | 
			
		||||
				modify: function (qb) {
 | 
			
		||||
					qb.where('user.is_deleted', 0);
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			proxy_hosts: {
 | 
			
		||||
				relation:   Model.HasManyRelation,
 | 
			
		||||
				modelClass: ProxyHost,
 | 
			
		||||
				join:       {
 | 
			
		||||
					from: 'certificate.id',
 | 
			
		||||
					to:   'proxy_host.certificate_id'
 | 
			
		||||
				},
 | 
			
		||||
				modify: function (qb) {
 | 
			
		||||
					qb.where('proxy_host.is_deleted', 0);
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			dead_hosts: {
 | 
			
		||||
				relation:   Model.HasManyRelation,
 | 
			
		||||
				modelClass: DeadHost,
 | 
			
		||||
				join:       {
 | 
			
		||||
					from: 'certificate.id',
 | 
			
		||||
					to:   'dead_host.certificate_id'
 | 
			
		||||
				},
 | 
			
		||||
				modify: function (qb) {
 | 
			
		||||
					qb.where('dead_host.is_deleted', 0);
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			redirection_hosts: {
 | 
			
		||||
				relation:   Model.HasManyRelation,
 | 
			
		||||
				modelClass: RedirectionHost,
 | 
			
		||||
				join:       {
 | 
			
		||||
					from: 'certificate.id',
 | 
			
		||||
					to:   'redirection_host.certificate_id'
 | 
			
		||||
				},
 | 
			
		||||
				modify: function (qb) {
 | 
			
		||||
					qb.where('redirection_host.is_deleted', 0);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,11 +12,7 @@ Model.knex(db);
 | 
			
		||||
 | 
			
		||||
const boolFields = [
 | 
			
		||||
	'is_deleted',
 | 
			
		||||
	'ssl_forced',
 | 
			
		||||
	'http2_support',
 | 
			
		||||
	'enabled',
 | 
			
		||||
	'hsts_enabled',
 | 
			
		||||
	'hsts_subdomains',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
class DeadHost extends Model {
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,6 @@ const boolFields = [
 | 
			
		||||
	'preserve_path',
 | 
			
		||||
	'ssl_forced',
 | 
			
		||||
	'block_exploits',
 | 
			
		||||
	'hsts_enabled',
 | 
			
		||||
	'hsts_subdomains',
 | 
			
		||||
	'http2_support',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
class RedirectionHost extends Model {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,16 @@
 | 
			
		||||
const Model       = require('objection').Model;
 | 
			
		||||
const db          = require('../db');
 | 
			
		||||
const helpers     = require('../lib/helpers');
 | 
			
		||||
const User        = require('./user');
 | 
			
		||||
const Certificate = require('./certificate');
 | 
			
		||||
const now         = require('./now_helper');
 | 
			
		||||
// Objection Docs:
 | 
			
		||||
// http://vincit.github.io/objection.js/
 | 
			
		||||
 | 
			
		||||
const db      = require('../db');
 | 
			
		||||
const helpers = require('../lib/helpers');
 | 
			
		||||
const Model   = require('objection').Model;
 | 
			
		||||
const User    = require('./user');
 | 
			
		||||
const now     = require('./now_helper');
 | 
			
		||||
 | 
			
		||||
Model.knex(db);
 | 
			
		||||
 | 
			
		||||
const boolFields = [
 | 
			
		||||
	'is_deleted',
 | 
			
		||||
	'enabled',
 | 
			
		||||
	'tcp_forwarding',
 | 
			
		||||
	'udp_forwarding',
 | 
			
		||||
];
 | 
			
		||||
@@ -63,17 +64,6 @@ class Stream extends Model {
 | 
			
		||||
				modify: function (qb) {
 | 
			
		||||
					qb.where('user.is_deleted', 0);
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			certificate: {
 | 
			
		||||
				relation:   Model.HasOneRelation,
 | 
			
		||||
				modelClass: Certificate,
 | 
			
		||||
				join:       {
 | 
			
		||||
					from: 'stream.certificate_id',
 | 
			
		||||
					to:   'certificate.id'
 | 
			
		||||
				},
 | 
			
		||||
				modify: function (qb) {
 | 
			
		||||
					qb.where('certificate.is_deleted', 0);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@
 | 
			
		||||
		"node-rsa": "^1.0.8",
 | 
			
		||||
		"objection": "3.0.1",
 | 
			
		||||
		"path": "^0.12.7",
 | 
			
		||||
		"pg": "^8.13.1",
 | 
			
		||||
		"signale": "1.4.0",
 | 
			
		||||
		"sqlite3": "5.1.6",
 | 
			
		||||
		"temp-write": "^4.0.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -181,7 +181,7 @@ router
 | 
			
		||||
				return internalUser.setPassword(res.locals.access, payload);
 | 
			
		||||
			})
 | 
			
		||||
			.then((result) => {
 | 
			
		||||
				res.status(200)
 | 
			
		||||
				res.status(201)
 | 
			
		||||
					.send(result);
 | 
			
		||||
			})
 | 
			
		||||
			.catch(next);
 | 
			
		||||
@@ -212,7 +212,7 @@ router
 | 
			
		||||
				return internalUser.setPermissions(res.locals.access, payload);
 | 
			
		||||
			})
 | 
			
		||||
			.then((result) => {
 | 
			
		||||
				res.status(200)
 | 
			
		||||
				res.status(201)
 | 
			
		||||
					.send(result);
 | 
			
		||||
			})
 | 
			
		||||
			.catch(next);
 | 
			
		||||
@@ -238,7 +238,7 @@ router
 | 
			
		||||
	.post((req, res, next) => {
 | 
			
		||||
		internalUser.loginAs(res.locals.access, {id: parseInt(req.params.user_id, 10)})
 | 
			
		||||
			.then((result) => {
 | 
			
		||||
				res.status(200)
 | 
			
		||||
				res.status(201)
 | 
			
		||||
					.send(result);
 | 
			
		||||
			})
 | 
			
		||||
			.catch(next);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,8 @@
 | 
			
		||||
		"enabled",
 | 
			
		||||
		"locations",
 | 
			
		||||
		"hsts_enabled",
 | 
			
		||||
		"hsts_subdomains"
 | 
			
		||||
		"hsts_subdomains",
 | 
			
		||||
		"certificate"
 | 
			
		||||
	],
 | 
			
		||||
	"additionalProperties": false,
 | 
			
		||||
	"properties": {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
	"type": "array",
 | 
			
		||||
	"description": "Streams list",
 | 
			
		||||
	"description": "Proxy Hosts list",
 | 
			
		||||
	"items": {
 | 
			
		||||
		"$ref": "./stream-object.json"
 | 
			
		||||
		"$ref": "./proxy-host-object.json"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,24 +53,8 @@
 | 
			
		||||
		"enabled": {
 | 
			
		||||
			"$ref": "../common.json#/properties/enabled"
 | 
			
		||||
		},
 | 
			
		||||
		"certificate_id": {
 | 
			
		||||
			"$ref": "../common.json#/properties/certificate_id"
 | 
			
		||||
		},
 | 
			
		||||
		"meta": {
 | 
			
		||||
			"type": "object"
 | 
			
		||||
		},
 | 
			
		||||
		"owner": {
 | 
			
		||||
			"$ref": "./user-object.json"
 | 
			
		||||
		},
 | 
			
		||||
		"certificate": {
 | 
			
		||||
			"oneOf": [
 | 
			
		||||
				{
 | 
			
		||||
					"type": "null"
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"$ref": "./certificate-object.json"
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,10 @@
 | 
			
		||||
	"additionalProperties": false,
 | 
			
		||||
	"properties": {
 | 
			
		||||
		"expires": {
 | 
			
		||||
			"description": "Token Expiry ISO Time String",
 | 
			
		||||
			"example": "2025-02-04T20:40:46.340Z",
 | 
			
		||||
			"type": "string"
 | 
			
		||||
			"description": "Token Expiry Unix Time",
 | 
			
		||||
			"example": 1566540249,
 | 
			
		||||
			"minimum": 1,
 | 
			
		||||
			"type": "number"
 | 
			
		||||
		},
 | 
			
		||||
		"token": {
 | 
			
		||||
			"description": "JWT Token",
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
			"description": "Expansions",
 | 
			
		||||
			"schema": {
 | 
			
		||||
				"type": "string",
 | 
			
		||||
				"enum": ["owner", "certificate"]
 | 
			
		||||
				"enum": ["access_list", "owner", "certificate"]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	],
 | 
			
		||||
@@ -40,8 +40,7 @@
 | 
			
		||||
										"nginx_online": true,
 | 
			
		||||
										"nginx_err": null
 | 
			
		||||
									},
 | 
			
		||||
									"enabled": true,
 | 
			
		||||
									"certificate_id": 0
 | 
			
		||||
									"enabled": true
 | 
			
		||||
								}
 | 
			
		||||
							]
 | 
			
		||||
						}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,9 +32,6 @@
 | 
			
		||||
						"udp_forwarding": {
 | 
			
		||||
							"$ref": "../../../components/stream-object.json#/properties/udp_forwarding"
 | 
			
		||||
						},
 | 
			
		||||
						"certificate_id": {
 | 
			
		||||
							"$ref": "../../../components/stream-object.json#/properties/certificate_id"
 | 
			
		||||
						},
 | 
			
		||||
						"meta": {
 | 
			
		||||
							"$ref": "../../../components/stream-object.json#/properties/meta"
 | 
			
		||||
						}
 | 
			
		||||
@@ -76,8 +73,7 @@
 | 
			
		||||
									"nickname": "Admin",
 | 
			
		||||
									"avatar": "",
 | 
			
		||||
									"roles": ["admin"]
 | 
			
		||||
								},
 | 
			
		||||
								"certificate_id": 0
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					},
 | 
			
		||||
 
 | 
			
		||||
@@ -40,8 +40,7 @@
 | 
			
		||||
									"nginx_online": true,
 | 
			
		||||
									"nginx_err": null
 | 
			
		||||
								},
 | 
			
		||||
								"enabled": true,
 | 
			
		||||
								"certificate_id": 0
 | 
			
		||||
								"enabled": true
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					},
 | 
			
		||||
 
 | 
			
		||||
@@ -29,26 +29,56 @@
 | 
			
		||||
					"additionalProperties": false,
 | 
			
		||||
					"minProperties": 1,
 | 
			
		||||
					"properties": {
 | 
			
		||||
						"incoming_port": {
 | 
			
		||||
							"$ref": "../../../../components/stream-object.json#/properties/incoming_port"
 | 
			
		||||
						"domain_names": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/domain_names"
 | 
			
		||||
						},
 | 
			
		||||
						"forwarding_host": {
 | 
			
		||||
							"$ref": "../../../../components/stream-object.json#/properties/forwarding_host"
 | 
			
		||||
						"forward_scheme": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/forward_scheme"
 | 
			
		||||
						},
 | 
			
		||||
						"forwarding_port": {
 | 
			
		||||
							"$ref": "../../../../components/stream-object.json#/properties/forwarding_port"
 | 
			
		||||
						"forward_host": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/forward_host"
 | 
			
		||||
						},
 | 
			
		||||
						"tcp_forwarding": {
 | 
			
		||||
							"$ref": "../../../../components/stream-object.json#/properties/tcp_forwarding"
 | 
			
		||||
						},
 | 
			
		||||
						"udp_forwarding": {
 | 
			
		||||
							"$ref": "../../../../components/stream-object.json#/properties/udp_forwarding"
 | 
			
		||||
						"forward_port": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/forward_port"
 | 
			
		||||
						},
 | 
			
		||||
						"certificate_id": {
 | 
			
		||||
							"$ref": "../../../../components/stream-object.json#/properties/certificate_id"
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/certificate_id"
 | 
			
		||||
						},
 | 
			
		||||
						"ssl_forced": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/ssl_forced"
 | 
			
		||||
						},
 | 
			
		||||
						"hsts_enabled": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/hsts_enabled"
 | 
			
		||||
						},
 | 
			
		||||
						"hsts_subdomains": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/hsts_subdomains"
 | 
			
		||||
						},
 | 
			
		||||
						"http2_support": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/http2_support"
 | 
			
		||||
						},
 | 
			
		||||
						"block_exploits": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/block_exploits"
 | 
			
		||||
						},
 | 
			
		||||
						"caching_enabled": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/caching_enabled"
 | 
			
		||||
						},
 | 
			
		||||
						"allow_websocket_upgrade": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/allow_websocket_upgrade"
 | 
			
		||||
						},
 | 
			
		||||
						"access_list_id": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/access_list_id"
 | 
			
		||||
						},
 | 
			
		||||
						"advanced_config": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/advanced_config"
 | 
			
		||||
						},
 | 
			
		||||
						"enabled": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/enabled"
 | 
			
		||||
						},
 | 
			
		||||
						"meta": {
 | 
			
		||||
							"$ref": "../../../../components/stream-object.json#/properties/meta"
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/meta"
 | 
			
		||||
						},
 | 
			
		||||
						"locations": {
 | 
			
		||||
							"$ref": "../../../../components/proxy-host-object.json#/properties/locations"
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
@@ -64,32 +94,42 @@
 | 
			
		||||
						"default": {
 | 
			
		||||
							"value": {
 | 
			
		||||
								"id": 1,
 | 
			
		||||
								"created_on": "2024-10-09T02:33:45.000Z",
 | 
			
		||||
								"modified_on": "2024-10-09T02:33:45.000Z",
 | 
			
		||||
								"created_on": "2024-10-08T23:23:03.000Z",
 | 
			
		||||
								"modified_on": "2024-10-08T23:26:37.000Z",
 | 
			
		||||
								"owner_user_id": 1,
 | 
			
		||||
								"incoming_port": 9090,
 | 
			
		||||
								"forwarding_host": "router.internal",
 | 
			
		||||
								"forwarding_port": 80,
 | 
			
		||||
								"tcp_forwarding": true,
 | 
			
		||||
								"udp_forwarding": false,
 | 
			
		||||
								"domain_names": ["test.example.com"],
 | 
			
		||||
								"forward_host": "192.168.0.10",
 | 
			
		||||
								"forward_port": 8989,
 | 
			
		||||
								"access_list_id": 0,
 | 
			
		||||
								"certificate_id": 0,
 | 
			
		||||
								"ssl_forced": false,
 | 
			
		||||
								"caching_enabled": false,
 | 
			
		||||
								"block_exploits": false,
 | 
			
		||||
								"advanced_config": "",
 | 
			
		||||
								"meta": {
 | 
			
		||||
									"nginx_online": true,
 | 
			
		||||
									"nginx_err": null
 | 
			
		||||
								},
 | 
			
		||||
								"allow_websocket_upgrade": false,
 | 
			
		||||
								"http2_support": false,
 | 
			
		||||
								"forward_scheme": "http",
 | 
			
		||||
								"enabled": true,
 | 
			
		||||
								"hsts_enabled": false,
 | 
			
		||||
								"hsts_subdomains": false,
 | 
			
		||||
								"owner": {
 | 
			
		||||
									"id": 1,
 | 
			
		||||
									"created_on": "2024-10-09T02:33:16.000Z",
 | 
			
		||||
									"modified_on": "2024-10-09T02:33:16.000Z",
 | 
			
		||||
									"created_on": "2024-10-07T22:43:55.000Z",
 | 
			
		||||
									"modified_on": "2024-10-08T12:52:54.000Z",
 | 
			
		||||
									"is_deleted": false,
 | 
			
		||||
									"is_disabled": false,
 | 
			
		||||
									"email": "admin@example.com",
 | 
			
		||||
									"name": "Administrator",
 | 
			
		||||
									"nickname": "Admin",
 | 
			
		||||
									"avatar": "",
 | 
			
		||||
									"nickname": "some guy",
 | 
			
		||||
									"avatar": "//www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?default=mm",
 | 
			
		||||
									"roles": ["admin"]
 | 
			
		||||
								},
 | 
			
		||||
								"certificate_id": 0
 | 
			
		||||
								"certificate": null,
 | 
			
		||||
								"access_list": null
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					},
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
					"examples": {
 | 
			
		||||
						"default": {
 | 
			
		||||
							"value": {
 | 
			
		||||
								"expires": "2025-02-04T20:40:46.340Z",
 | 
			
		||||
								"expires": 1566540510,
 | 
			
		||||
								"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
						"default": {
 | 
			
		||||
							"value": {
 | 
			
		||||
								"result": {
 | 
			
		||||
									"expires": "2025-02-04T20:40:46.340Z",
 | 
			
		||||
									"expires": 1566540510,
 | 
			
		||||
									"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,15 +9,6 @@
 | 
			
		||||
			"url": "http://127.0.0.1:81/api"
 | 
			
		||||
		}
 | 
			
		||||
	],
 | 
			
		||||
	"components": {
 | 
			
		||||
		"securitySchemes": {
 | 
			
		||||
			"bearerAuth": {
 | 
			
		||||
				"type": "http",
 | 
			
		||||
				"scheme": "bearer",
 | 
			
		||||
				"bearerFormat": "JWT"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	"paths": {
 | 
			
		||||
		"/": {
 | 
			
		||||
			"get": {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,18 +15,18 @@ const certbot             = require('./lib/certbot');
 | 
			
		||||
const setupDefaultUser = () => {
 | 
			
		||||
	return userModel
 | 
			
		||||
		.query()
 | 
			
		||||
		.select('id', )
 | 
			
		||||
		.select(userModel.raw('COUNT(`id`) as `count`'))
 | 
			
		||||
		.where('is_deleted', 0)
 | 
			
		||||
		.first()
 | 
			
		||||
		.then((row) => {
 | 
			
		||||
			if (!row || !row.id) {
 | 
			
		||||
			if (!row.count) {
 | 
			
		||||
				// Create a new user and set password
 | 
			
		||||
				const email    = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
 | 
			
		||||
				const password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
 | 
			
		||||
 | 
			
		||||
				let email    = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
 | 
			
		||||
				let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
 | 
			
		||||
				
 | 
			
		||||
				logger.info('Creating a new user: ' + email + ' with password: ' + password);
 | 
			
		||||
 | 
			
		||||
				const data = {
 | 
			
		||||
				let data = {
 | 
			
		||||
					is_deleted: 0,
 | 
			
		||||
					email:      email,
 | 
			
		||||
					name:       'Administrator',
 | 
			
		||||
@@ -77,11 +77,11 @@ const setupDefaultUser = () => {
 | 
			
		||||
const setupDefaultSettings = () => {
 | 
			
		||||
	return settingModel
 | 
			
		||||
		.query()
 | 
			
		||||
		.select('id')
 | 
			
		||||
		.select(settingModel.raw('COUNT(`id`) as `count`'))
 | 
			
		||||
		.where({id: 'default-site'})
 | 
			
		||||
		.first()
 | 
			
		||||
		.then((row) => {
 | 
			
		||||
			if (!row || !row.id) {
 | 
			
		||||
			if (!row.count) {
 | 
			
		||||
				settingModel
 | 
			
		||||
					.query()
 | 
			
		||||
					.insert({
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
{% if certificate.provider == "letsencrypt" %}
 | 
			
		||||
  # Let's Encrypt SSL
 | 
			
		||||
  include conf.d/include/letsencrypt-acme-challenge.conf;
 | 
			
		||||
  include conf.d/include/ssl-cache.conf;
 | 
			
		||||
  include conf.d/include/ssl-ciphers.conf;
 | 
			
		||||
  ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem;
 | 
			
		||||
  ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
{% if certificate and certificate_id > 0 %}
 | 
			
		||||
{% if certificate.provider == "letsencrypt" %}
 | 
			
		||||
  # Let's Encrypt SSL
 | 
			
		||||
  include conf.d/include/ssl-cache-stream.conf;
 | 
			
		||||
  include conf.d/include/ssl-ciphers.conf;
 | 
			
		||||
  ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem;
 | 
			
		||||
  ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem;
 | 
			
		||||
{%- else %}
 | 
			
		||||
  # Custom SSL
 | 
			
		||||
  ssl_certificate /data/custom_ssl/npm-{{ certificate_id }}/fullchain.pem;
 | 
			
		||||
  ssl_certificate_key /data/custom_ssl/npm-{{ certificate_id }}/privkey.pem;
 | 
			
		||||
{%- endif -%}
 | 
			
		||||
{%- endif -%}
 | 
			
		||||
@@ -22,7 +22,5 @@ server {
 | 
			
		||||
  }
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
  # Custom
 | 
			
		||||
  include /data/nginx/custom/server_dead[.]conf;
 | 
			
		||||
}
 | 
			
		||||
{% endif %}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,12 @@
 | 
			
		||||
{% if enabled %}
 | 
			
		||||
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
 | 
			
		||||
server {
 | 
			
		||||
  listen {{ incoming_port }} {%- if certificate %} ssl {%- endif %};
 | 
			
		||||
  {% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} {%- if certificate %} ssl {%- endif %};
 | 
			
		||||
 | 
			
		||||
  {%- include "_certificates_stream.conf" %}
 | 
			
		||||
  listen {{ incoming_port }};
 | 
			
		||||
{% if ipv6 -%}
 | 
			
		||||
  listen [::]:{{ incoming_port }};
 | 
			
		||||
{% else -%}
 | 
			
		||||
  #listen [::]:{{ incoming_port }};
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
  proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
 | 
			
		||||
 | 
			
		||||
@@ -17,12 +19,14 @@ server {
 | 
			
		||||
  include /data/nginx/custom/server_stream_tcp[.]conf;
 | 
			
		||||
}
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
{% if udp_forwarding == 1 or udp_forwarding == true -%}
 | 
			
		||||
{% if udp_forwarding == 1 or udp_forwarding == true %}
 | 
			
		||||
server {
 | 
			
		||||
  listen {{ incoming_port }} udp;
 | 
			
		||||
  {% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp;
 | 
			
		||||
 | 
			
		||||
{% if ipv6 -%}
 | 
			
		||||
  listen [::]:{{ incoming_port }} udp;
 | 
			
		||||
{% else -%}
 | 
			
		||||
  #listen [::]:{{ incoming_port }} udp;
 | 
			
		||||
{% endif %}
 | 
			
		||||
  proxy_pass {{ forwarding_host }}:{{ forwarding_port }};
 | 
			
		||||
 | 
			
		||||
  # Custom
 | 
			
		||||
 
 | 
			
		||||
@@ -492,9 +492,9 @@ boxen@^4.2.0:
 | 
			
		||||
    widest-line "^3.1.0"
 | 
			
		||||
 | 
			
		||||
brace-expansion@^1.1.7:
 | 
			
		||||
  version "1.1.12"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
 | 
			
		||||
  integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
 | 
			
		||||
  version "1.1.11"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
 | 
			
		||||
  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    balanced-match "^1.0.0"
 | 
			
		||||
    concat-map "0.0.1"
 | 
			
		||||
@@ -830,9 +830,9 @@ crc32-stream@^4.0.2:
 | 
			
		||||
    readable-stream "^3.4.0"
 | 
			
		||||
 | 
			
		||||
cross-spawn@^7.0.2:
 | 
			
		||||
  version "7.0.6"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
 | 
			
		||||
  integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
 | 
			
		||||
  version "7.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
 | 
			
		||||
  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    path-key "^3.1.0"
 | 
			
		||||
    shebang-command "^2.0.0"
 | 
			
		||||
@@ -2735,67 +2735,11 @@ path@^0.12.7:
 | 
			
		||||
    process "^0.11.1"
 | 
			
		||||
    util "^0.10.3"
 | 
			
		||||
 | 
			
		||||
pg-cloudflare@^1.1.1:
 | 
			
		||||
  version "1.1.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98"
 | 
			
		||||
  integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==
 | 
			
		||||
 | 
			
		||||
pg-connection-string@2.5.0:
 | 
			
		||||
  version "2.5.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
 | 
			
		||||
  integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
 | 
			
		||||
 | 
			
		||||
pg-connection-string@^2.7.0:
 | 
			
		||||
  version "2.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.7.0.tgz#f1d3489e427c62ece022dba98d5262efcb168b37"
 | 
			
		||||
  integrity sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==
 | 
			
		||||
 | 
			
		||||
pg-int8@1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
 | 
			
		||||
  integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
 | 
			
		||||
 | 
			
		||||
pg-pool@^3.7.0:
 | 
			
		||||
  version "3.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.7.0.tgz#d4d3c7ad640f8c6a2245adc369bafde4ebb8cbec"
 | 
			
		||||
  integrity sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==
 | 
			
		||||
 | 
			
		||||
pg-protocol@^1.7.0:
 | 
			
		||||
  version "1.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.7.0.tgz#ec037c87c20515372692edac8b63cf4405448a93"
 | 
			
		||||
  integrity sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==
 | 
			
		||||
 | 
			
		||||
pg-types@^2.1.0:
 | 
			
		||||
  version "2.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
 | 
			
		||||
  integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    pg-int8 "1.0.1"
 | 
			
		||||
    postgres-array "~2.0.0"
 | 
			
		||||
    postgres-bytea "~1.0.0"
 | 
			
		||||
    postgres-date "~1.0.4"
 | 
			
		||||
    postgres-interval "^1.1.0"
 | 
			
		||||
 | 
			
		||||
pg@^8.13.1:
 | 
			
		||||
  version "8.13.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pg/-/pg-8.13.1.tgz#6498d8b0a87ff76c2df7a32160309d3168c0c080"
 | 
			
		||||
  integrity sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    pg-connection-string "^2.7.0"
 | 
			
		||||
    pg-pool "^3.7.0"
 | 
			
		||||
    pg-protocol "^1.7.0"
 | 
			
		||||
    pg-types "^2.1.0"
 | 
			
		||||
    pgpass "1.x"
 | 
			
		||||
  optionalDependencies:
 | 
			
		||||
    pg-cloudflare "^1.1.1"
 | 
			
		||||
 | 
			
		||||
pgpass@1.x:
 | 
			
		||||
  version "1.0.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d"
 | 
			
		||||
  integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    split2 "^4.1.0"
 | 
			
		||||
 | 
			
		||||
picomatch@^2.0.4, picomatch@^2.2.1:
 | 
			
		||||
  version "2.2.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
 | 
			
		||||
@@ -2814,28 +2758,6 @@ pkg-conf@^2.1.0:
 | 
			
		||||
    find-up "^2.0.0"
 | 
			
		||||
    load-json-file "^4.0.0"
 | 
			
		||||
 | 
			
		||||
postgres-array@~2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
 | 
			
		||||
  integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
 | 
			
		||||
 | 
			
		||||
postgres-bytea@~1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35"
 | 
			
		||||
  integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==
 | 
			
		||||
 | 
			
		||||
postgres-date@~1.0.4:
 | 
			
		||||
  version "1.0.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8"
 | 
			
		||||
  integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==
 | 
			
		||||
 | 
			
		||||
postgres-interval@^1.1.0:
 | 
			
		||||
  version "1.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695"
 | 
			
		||||
  integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    xtend "^4.0.0"
 | 
			
		||||
 | 
			
		||||
prelude-ls@^1.2.1:
 | 
			
		||||
  version "1.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
 | 
			
		||||
@@ -3272,11 +3194,6 @@ socks@^2.6.2:
 | 
			
		||||
    ip "^2.0.0"
 | 
			
		||||
    smart-buffer "^4.2.0"
 | 
			
		||||
 | 
			
		||||
split2@^4.1.0:
 | 
			
		||||
  version "4.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
 | 
			
		||||
  integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
 | 
			
		||||
 | 
			
		||||
sprintf-js@~1.0.2:
 | 
			
		||||
  version "1.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
 | 
			
		||||
@@ -3748,11 +3665,6 @@ xdg-basedir@^4.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
 | 
			
		||||
  integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
 | 
			
		||||
 | 
			
		||||
xtend@^4.0.0:
 | 
			
		||||
  version "4.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
 | 
			
		||||
  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
 | 
			
		||||
 | 
			
		||||
y18n@^4.0.0:
 | 
			
		||||
  version "4.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
AUTHENTIK_SECRET_KEY=gl8woZe8L6IIX8SC0c5Ocsj0xPkX5uJo5DVZCFl+L/QGbzuplfutYuua2ODNLEiDD3aFd9H2ylJmrke0
 | 
			
		||||
AUTHENTIK_REDIS__HOST=authentik-redis
 | 
			
		||||
AUTHENTIK_POSTGRESQL__HOST=db-postgres
 | 
			
		||||
AUTHENTIK_POSTGRESQL__USER=authentik
 | 
			
		||||
AUTHENTIK_POSTGRESQL__NAME=authentik
 | 
			
		||||
AUTHENTIK_POSTGRESQL__PASSWORD=07EKS5NLI6Tpv68tbdvrxfvj
 | 
			
		||||
AUTHENTIK_BOOTSTRAP_PASSWORD=admin
 | 
			
		||||
AUTHENTIK_BOOTSTRAP_EMAIL=admin@example.com
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -29,8 +29,7 @@ COPY scripts/install-s6 /tmp/install-s6
 | 
			
		||||
RUN rm -f /etc/nginx/conf.d/production.conf \
 | 
			
		||||
	&& chmod 644 /etc/logrotate.d/nginx-proxy-manager \
 | 
			
		||||
	&& /tmp/install-s6 "${TARGETPLATFORM}" \
 | 
			
		||||
	&& rm -f /tmp/install-s6 \
 | 
			
		||||
	&& chmod 644 -R /root/.cache
 | 
			
		||||
	&& rm -f /tmp/install-s6
 | 
			
		||||
 | 
			
		||||
# Certs for testing purposes
 | 
			
		||||
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@ services:
 | 
			
		||||
      MYSQL_DATABASE: 'npm'
 | 
			
		||||
      MYSQL_USER: 'npm'
 | 
			
		||||
      MYSQL_PASSWORD: 'npmpass'
 | 
			
		||||
      MARIADB_AUTO_UPGRADE: '1'
 | 
			
		||||
    volumes:
 | 
			
		||||
      - mysql_vol:/var/lib/mysql
 | 
			
		||||
    networks:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
 | 
			
		||||
services:
 | 
			
		||||
 | 
			
		||||
  cypress:
 | 
			
		||||
    environment:
 | 
			
		||||
      CYPRESS_stack: 'postgres'
 | 
			
		||||
 | 
			
		||||
  fullstack:
 | 
			
		||||
    environment:
 | 
			
		||||
      DB_POSTGRES_HOST: 'db-postgres'
 | 
			
		||||
      DB_POSTGRES_PORT: '5432'
 | 
			
		||||
      DB_POSTGRES_USER: 'npm'
 | 
			
		||||
      DB_POSTGRES_PASSWORD: 'npmpass'
 | 
			
		||||
      DB_POSTGRES_NAME: 'npm'
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - db-postgres
 | 
			
		||||
      - authentik
 | 
			
		||||
      - authentik-worker
 | 
			
		||||
      - authentik-ldap
 | 
			
		||||
 | 
			
		||||
  db-postgres:
 | 
			
		||||
    image: postgres:latest
 | 
			
		||||
    environment:
 | 
			
		||||
      POSTGRES_USER: 'npm'
 | 
			
		||||
      POSTGRES_PASSWORD: 'npmpass'
 | 
			
		||||
      POSTGRES_DB: 'npm'
 | 
			
		||||
    volumes:
 | 
			
		||||
      - psql_vol:/var/lib/postgresql/data
 | 
			
		||||
      - ./ci/postgres:/docker-entrypoint-initdb.d
 | 
			
		||||
    networks:
 | 
			
		||||
      - fulltest
 | 
			
		||||
 | 
			
		||||
  authentik-redis:
 | 
			
		||||
    image: 'redis:alpine'
 | 
			
		||||
    command: --save 60 1 --loglevel warning
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    healthcheck:
 | 
			
		||||
      test: ['CMD-SHELL', 'redis-cli ping | grep PONG']
 | 
			
		||||
      start_period: 20s
 | 
			
		||||
      interval: 30s
 | 
			
		||||
      retries: 5
 | 
			
		||||
      timeout: 3s
 | 
			
		||||
    volumes:
 | 
			
		||||
      - redis_vol:/data
 | 
			
		||||
 | 
			
		||||
  authentik:
 | 
			
		||||
    image: ghcr.io/goauthentik/server:2024.10.1
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    command: server
 | 
			
		||||
    env_file:
 | 
			
		||||
      - ci.env
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - authentik-redis
 | 
			
		||||
      - db-postgres
 | 
			
		||||
 | 
			
		||||
  authentik-worker:
 | 
			
		||||
    image: ghcr.io/goauthentik/server:2024.10.1
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    command: worker
 | 
			
		||||
    env_file:
 | 
			
		||||
      - ci.env
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - authentik-redis
 | 
			
		||||
      - db-postgres
 | 
			
		||||
 | 
			
		||||
  authentik-ldap:
 | 
			
		||||
    image: ghcr.io/goauthentik/ldap:2024.10.1
 | 
			
		||||
    environment:
 | 
			
		||||
      AUTHENTIK_HOST: 'http://authentik:9000'
 | 
			
		||||
      AUTHENTIK_INSECURE: 'true'
 | 
			
		||||
      AUTHENTIK_TOKEN: 'wKYZuRcI0ETtb8vWzMCr04oNbhrQUUICy89hSpDln1OEKLjiNEuQ51044Vkp'
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - authentik
 | 
			
		||||
 | 
			
		||||
volumes:
 | 
			
		||||
  psql_vol:
 | 
			
		||||
  redis_vol:
 | 
			
		||||
@@ -22,10 +22,6 @@ services:
 | 
			
		||||
      test: ["CMD", "/usr/bin/check-health"]
 | 
			
		||||
      interval: 10s
 | 
			
		||||
      timeout: 3s
 | 
			
		||||
    expose:
 | 
			
		||||
      - '80-81/tcp'
 | 
			
		||||
      - '443/tcp'
 | 
			
		||||
      - '1500-1503/tcp'
 | 
			
		||||
    networks:
 | 
			
		||||
      fulltest:
 | 
			
		||||
        aliases:
 | 
			
		||||
@@ -44,7 +40,7 @@ services:
 | 
			
		||||
          - ca.internal
 | 
			
		||||
 | 
			
		||||
  pdns:
 | 
			
		||||
    image: pschiffe/pdns-mysql:4.8
 | 
			
		||||
    image: pschiffe/pdns-mysql
 | 
			
		||||
    volumes:
 | 
			
		||||
      - '/etc/localtime:/etc/localtime:ro'
 | 
			
		||||
    environment:
 | 
			
		||||
@@ -101,7 +97,7 @@ services:
 | 
			
		||||
      HTTP_PROXY: 'squid:3128'
 | 
			
		||||
      HTTPS_PROXY: 'squid:3128'
 | 
			
		||||
    volumes:
 | 
			
		||||
      - 'cypress_logs:/test/results'
 | 
			
		||||
      - 'cypress_logs:/results'
 | 
			
		||||
      - './dev/resolv.conf:/etc/resolv.conf:ro'
 | 
			
		||||
      - '/etc/localtime:/etc/localtime:ro'
 | 
			
		||||
    command: cypress run --browser chrome --config-file=cypress/config/ci.js
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,8 @@
 | 
			
		||||
services:
 | 
			
		||||
 | 
			
		||||
  fullstack:
 | 
			
		||||
    image: npm2dev:core
 | 
			
		||||
    container_name: npm2dev.core
 | 
			
		||||
    image: nginxproxymanager:dev
 | 
			
		||||
    container_name: npm_core
 | 
			
		||||
    build:
 | 
			
		||||
      context: ./
 | 
			
		||||
      dockerfile: ./dev/Dockerfile
 | 
			
		||||
@@ -26,17 +26,11 @@ services:
 | 
			
		||||
      DEVELOPMENT: 'true'
 | 
			
		||||
      LE_STAGING: 'true'
 | 
			
		||||
      # db:
 | 
			
		||||
      # DB_MYSQL_HOST: 'db'
 | 
			
		||||
      # DB_MYSQL_PORT: '3306'
 | 
			
		||||
      # DB_MYSQL_USER: 'npm'
 | 
			
		||||
      # DB_MYSQL_PASSWORD: 'npm'
 | 
			
		||||
      # DB_MYSQL_NAME: 'npm'
 | 
			
		||||
      # db-postgres:
 | 
			
		||||
      DB_POSTGRES_HOST: 'db-postgres'
 | 
			
		||||
      DB_POSTGRES_PORT: '5432'
 | 
			
		||||
      DB_POSTGRES_USER: 'npm'
 | 
			
		||||
      DB_POSTGRES_PASSWORD: 'npmpass'
 | 
			
		||||
      DB_POSTGRES_NAME: 'npm'
 | 
			
		||||
      DB_MYSQL_HOST: 'db'
 | 
			
		||||
      DB_MYSQL_PORT: '3306'
 | 
			
		||||
      DB_MYSQL_USER: 'npm'
 | 
			
		||||
      DB_MYSQL_PASSWORD: 'npm'
 | 
			
		||||
      DB_MYSQL_NAME: 'npm'
 | 
			
		||||
      # DB_SQLITE_FILE: "/data/database.sqlite"
 | 
			
		||||
      # DISABLE_IPV6: "true"
 | 
			
		||||
      # Required for DNS Certificate provisioning testing:
 | 
			
		||||
@@ -55,15 +49,11 @@ services:
 | 
			
		||||
      timeout: 3s
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - db
 | 
			
		||||
      - db-postgres
 | 
			
		||||
      - authentik
 | 
			
		||||
      - authentik-worker
 | 
			
		||||
      - authentik-ldap
 | 
			
		||||
    working_dir: /app
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    image: jc21/mariadb-aria
 | 
			
		||||
    container_name: npm2dev.db
 | 
			
		||||
    container_name: npm_db
 | 
			
		||||
    ports:
 | 
			
		||||
      - 33306:3306
 | 
			
		||||
    networks:
 | 
			
		||||
@@ -76,22 +66,8 @@ services:
 | 
			
		||||
    volumes:
 | 
			
		||||
      - db_data:/var/lib/mysql
 | 
			
		||||
 | 
			
		||||
  db-postgres:
 | 
			
		||||
    image: postgres:latest
 | 
			
		||||
    container_name: npm2dev.db-postgres
 | 
			
		||||
    networks:
 | 
			
		||||
      - nginx_proxy_manager
 | 
			
		||||
    environment:
 | 
			
		||||
      POSTGRES_USER: 'npm'
 | 
			
		||||
      POSTGRES_PASSWORD: 'npmpass'
 | 
			
		||||
      POSTGRES_DB: 'npm'
 | 
			
		||||
    volumes:
 | 
			
		||||
      - psql_data:/var/lib/postgresql/data
 | 
			
		||||
      - ./ci/postgres:/docker-entrypoint-initdb.d
 | 
			
		||||
 | 
			
		||||
  stepca:
 | 
			
		||||
    image: jc21/testca
 | 
			
		||||
    container_name: npm2dev.stepca
 | 
			
		||||
    volumes:
 | 
			
		||||
      - './dev/resolv.conf:/etc/resolv.conf:ro'
 | 
			
		||||
      - '/etc/localtime:/etc/localtime:ro'
 | 
			
		||||
@@ -102,7 +78,6 @@ services:
 | 
			
		||||
 | 
			
		||||
  dnsrouter:
 | 
			
		||||
    image: jc21/dnsrouter
 | 
			
		||||
    container_name: npm2dev.dnsrouter
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./dev/dnsrouter-config.json.tmp:/dnsrouter-config.json:ro
 | 
			
		||||
    networks:
 | 
			
		||||
@@ -110,7 +85,7 @@ services:
 | 
			
		||||
 | 
			
		||||
  swagger:
 | 
			
		||||
    image: swaggerapi/swagger-ui:latest
 | 
			
		||||
    container_name: npm2dev.swagger
 | 
			
		||||
    container_name: npm_swagger
 | 
			
		||||
    ports:
 | 
			
		||||
      - 3082:80
 | 
			
		||||
    environment:
 | 
			
		||||
@@ -121,7 +96,7 @@ services:
 | 
			
		||||
 | 
			
		||||
  squid:
 | 
			
		||||
    image: ubuntu/squid
 | 
			
		||||
    container_name: npm2dev.squid
 | 
			
		||||
    container_name: npm_squid
 | 
			
		||||
    volumes:
 | 
			
		||||
      - './dev/squid.conf:/etc/squid/squid.conf:ro'
 | 
			
		||||
      - './dev/resolv.conf:/etc/resolv.conf:ro'
 | 
			
		||||
@@ -132,8 +107,7 @@ services:
 | 
			
		||||
      - 8128:3128
 | 
			
		||||
 | 
			
		||||
  pdns:
 | 
			
		||||
    image: pschiffe/pdns-mysql:4.8
 | 
			
		||||
    container_name: npm2dev.pdns
 | 
			
		||||
    image: pschiffe/pdns-mysql
 | 
			
		||||
    volumes:
 | 
			
		||||
      - '/etc/localtime:/etc/localtime:ro'
 | 
			
		||||
    environment:
 | 
			
		||||
@@ -162,7 +136,6 @@ services:
 | 
			
		||||
 | 
			
		||||
  pdns-db:
 | 
			
		||||
    image: mariadb
 | 
			
		||||
    container_name: npm2dev.pdns-db
 | 
			
		||||
    environment:
 | 
			
		||||
      MYSQL_ROOT_PASSWORD: 'pdns'
 | 
			
		||||
      MYSQL_DATABASE: 'pdns'
 | 
			
		||||
@@ -176,8 +149,7 @@ services:
 | 
			
		||||
      - nginx_proxy_manager
 | 
			
		||||
 | 
			
		||||
  cypress:
 | 
			
		||||
    image: npm2dev:cypress
 | 
			
		||||
    container_name: npm2dev.cypress
 | 
			
		||||
    image: "npm_dev_cypress"
 | 
			
		||||
    build:
 | 
			
		||||
      context: ../
 | 
			
		||||
      dockerfile: test/cypress/Dockerfile
 | 
			
		||||
@@ -192,77 +164,16 @@ services:
 | 
			
		||||
    networks:
 | 
			
		||||
      - nginx_proxy_manager
 | 
			
		||||
 | 
			
		||||
  authentik-redis:
 | 
			
		||||
    image: 'redis:alpine'
 | 
			
		||||
    container_name: npm2dev.authentik-redis
 | 
			
		||||
    command: --save 60 1 --loglevel warning
 | 
			
		||||
    networks:
 | 
			
		||||
      - nginx_proxy_manager
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    healthcheck:
 | 
			
		||||
      test: ['CMD-SHELL', 'redis-cli ping | grep PONG']
 | 
			
		||||
      start_period: 20s
 | 
			
		||||
      interval: 30s
 | 
			
		||||
      retries: 5
 | 
			
		||||
      timeout: 3s
 | 
			
		||||
    volumes:
 | 
			
		||||
      - redis_data:/data
 | 
			
		||||
 | 
			
		||||
  authentik:
 | 
			
		||||
    image: ghcr.io/goauthentik/server:2024.10.1
 | 
			
		||||
    container_name: npm2dev.authentik
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    command: server
 | 
			
		||||
    networks:
 | 
			
		||||
      - nginx_proxy_manager
 | 
			
		||||
    env_file:
 | 
			
		||||
      - ci.env
 | 
			
		||||
    ports:
 | 
			
		||||
      - 9000:9000
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - authentik-redis
 | 
			
		||||
      - db-postgres
 | 
			
		||||
 | 
			
		||||
  authentik-worker:
 | 
			
		||||
    image: ghcr.io/goauthentik/server:2024.10.1
 | 
			
		||||
    container_name: npm2dev.authentik-worker
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    command: worker
 | 
			
		||||
    networks:
 | 
			
		||||
      - nginx_proxy_manager
 | 
			
		||||
    env_file:
 | 
			
		||||
      - ci.env
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - authentik-redis
 | 
			
		||||
      - db-postgres
 | 
			
		||||
 | 
			
		||||
  authentik-ldap:
 | 
			
		||||
    image: ghcr.io/goauthentik/ldap:2024.10.1
 | 
			
		||||
    container_name: npm2dev.authentik-ldap
 | 
			
		||||
    networks:
 | 
			
		||||
      - nginx_proxy_manager
 | 
			
		||||
    environment:
 | 
			
		||||
      AUTHENTIK_HOST: 'http://authentik:9000'
 | 
			
		||||
      AUTHENTIK_INSECURE: 'true'
 | 
			
		||||
      AUTHENTIK_TOKEN: 'wKYZuRcI0ETtb8vWzMCr04oNbhrQUUICy89hSpDln1OEKLjiNEuQ51044Vkp'
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - authentik
 | 
			
		||||
 | 
			
		||||
volumes:
 | 
			
		||||
  npm_data:
 | 
			
		||||
    name: npm2dev_core_data
 | 
			
		||||
    name: npm_core_data
 | 
			
		||||
  le_data:
 | 
			
		||||
    name: npm2dev_le_data
 | 
			
		||||
    name: npm_le_data
 | 
			
		||||
  db_data:
 | 
			
		||||
    name: npm2dev_db_data
 | 
			
		||||
    name: npm_db_data
 | 
			
		||||
  pdns_mysql:
 | 
			
		||||
    name: npnpm2dev_pdns_mysql
 | 
			
		||||
  psql_data:
 | 
			
		||||
    name: npm2dev_psql_data
 | 
			
		||||
  redis_data:
 | 
			
		||||
    name: npm2dev_redis_data
 | 
			
		||||
    name: npm_pdns_mysql
 | 
			
		||||
 | 
			
		||||
networks:
 | 
			
		||||
  nginx_proxy_manager:
 | 
			
		||||
    name: npm2dev_network
 | 
			
		||||
    name: npm_network
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|woff2|eot|ttf|svg|ico|css\.map|js\.map)$ {
 | 
			
		||||
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\.map|js\.map)$ {
 | 
			
		||||
	if_modified_since off;
 | 
			
		||||
 | 
			
		||||
	# use the public cache
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
ssl_session_timeout 5m;
 | 
			
		||||
ssl_session_cache shared:SSL_stream:50m;
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
ssl_session_timeout 5m;
 | 
			
		||||
ssl_session_cache shared:SSL:50m;
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
ssl_session_timeout 5m;
 | 
			
		||||
ssl_session_cache shared:SSL:50m;
 | 
			
		||||
 | 
			
		||||
# intermediate configuration. tweak to your needs.
 | 
			
		||||
ssl_protocols TLSv1.2 TLSv1.3;
 | 
			
		||||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
 | 
			
		||||
 
 | 
			
		||||
@@ -23,19 +23,6 @@ chown -R "$PUID:$PGID" /etc/nginx/nginx
 | 
			
		||||
chown -R "$PUID:$PGID" /etc/nginx/nginx.conf
 | 
			
		||||
chown -R "$PUID:$PGID" /etc/nginx/conf.d
 | 
			
		||||
 | 
			
		||||
# Certbot directories - optimized approach
 | 
			
		||||
CERT_INIT_FLAG="/opt/certbot/.ownership_initialized"
 | 
			
		||||
 | 
			
		||||
if [ ! -f "$CERT_INIT_FLAG" ]; then
 | 
			
		||||
    # Prevents errors when installing python certbot plugins when non-root
 | 
			
		||||
    chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin
 | 
			
		||||
 | 
			
		||||
    # Handle all site-packages directories efficiently
 | 
			
		||||
    find /opt/certbot/lib -type d -name "site-packages" | while read -r SITE_PACKAGES_DIR; do
 | 
			
		||||
        chown -R "$PUID:$PGID" "$SITE_PACKAGES_DIR"
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    # Create a flag file to skip this step on subsequent runs
 | 
			
		||||
    touch "$CERT_INIT_FLAG"
 | 
			
		||||
    chown "$PUID:$PGID" "$CERT_INIT_FLAG"
 | 
			
		||||
fi
 | 
			
		||||
# Prevents errors when installing python certbot plugins when non-root
 | 
			
		||||
chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin
 | 
			
		||||
find /opt/certbot/lib/python*/site-packages -not -user "$PUID" -execdir chown "$PUID:$PGID" {} \+
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ BLUE='\E[1;34m'
 | 
			
		||||
GREEN='\E[1;32m'
 | 
			
		||||
RESET='\E[0m'
 | 
			
		||||
 | 
			
		||||
S6_OVERLAY_VERSION=3.2.0.2
 | 
			
		||||
S6_OVERLAY_VERSION=3.1.5.0
 | 
			
		||||
TARGETPLATFORM=${1:-linux/amd64}
 | 
			
		||||
 | 
			
		||||
# Determine the correct binary file for the architecture given
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,7 @@ networks:
 | 
			
		||||
Let's look at a Portainer example:
 | 
			
		||||
 | 
			
		||||
```yml
 | 
			
		||||
version: '3.8'
 | 
			
		||||
services:
 | 
			
		||||
 | 
			
		||||
  portainer:
 | 
			
		||||
@@ -91,6 +92,8 @@ This image supports the use of Docker secrets to import from files and keep sens
 | 
			
		||||
You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
 | 
			
		||||
 | 
			
		||||
```yml
 | 
			
		||||
version: '3.8'
 | 
			
		||||
 | 
			
		||||
secrets:
 | 
			
		||||
  # Secrets are single-line text files where the sole content is the secret
 | 
			
		||||
  # Paths in this example assume that secrets are kept in local folder called ".secrets"
 | 
			
		||||
@@ -161,14 +164,6 @@ The easy fix is to add a Docker environment variable to the Nginx Proxy Manager
 | 
			
		||||
      DISABLE_IPV6: 'true'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Disabling IP Ranges Fetch
 | 
			
		||||
 | 
			
		||||
By default, NPM fetches IP ranges from CloudFront and Cloudflare during application startup. In environments with limited internet access or to speed up container startup, this fetch can be disabled:
 | 
			
		||||
 | 
			
		||||
```yml
 | 
			
		||||
    environment:
 | 
			
		||||
      IP_RANGES_FETCH_ENABLED: 'false'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Custom Nginx Configurations
 | 
			
		||||
 | 
			
		||||
@@ -189,7 +184,6 @@ You can add your custom configuration snippet files at `/data/nginx/custom` as f
 | 
			
		||||
 - `/data/nginx/custom/server_stream.conf`: Included at the end of every stream server block
 | 
			
		||||
 - `/data/nginx/custom/server_stream_tcp.conf`: Included at the end of every TCP stream server block
 | 
			
		||||
 - `/data/nginx/custom/server_stream_udp.conf`: Included at the end of every UDP stream server block
 | 
			
		||||
 - `/data/nginx/custom/server_dead.conf`: Included at the end of every 404 server block
 | 
			
		||||
 | 
			
		||||
Every file is optional.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ outline: deep
 | 
			
		||||
Create a `docker-compose.yml` file:
 | 
			
		||||
 | 
			
		||||
```yml
 | 
			
		||||
version: '3.8'
 | 
			
		||||
services:
 | 
			
		||||
  app:
 | 
			
		||||
    image: 'jc21/nginx-proxy-manager:latest'
 | 
			
		||||
@@ -21,7 +22,8 @@ services:
 | 
			
		||||
      # Add any other Stream port you want to expose
 | 
			
		||||
      # - '21:21' # FTP
 | 
			
		||||
 | 
			
		||||
    #environment:
 | 
			
		||||
    # Uncomment the next line if you uncomment anything in the section
 | 
			
		||||
    # environment:
 | 
			
		||||
      # Uncomment this if you want to change the location of
 | 
			
		||||
      # the SQLite DB file within the container
 | 
			
		||||
      # DB_SQLITE_FILE: "/data/database.sqlite"
 | 
			
		||||
@@ -53,6 +55,7 @@ are going to use.
 | 
			
		||||
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
 | 
			
		||||
 | 
			
		||||
```yml
 | 
			
		||||
version: '3.8'
 | 
			
		||||
services:
 | 
			
		||||
  app:
 | 
			
		||||
    image: 'jc21/nginx-proxy-manager:latest'
 | 
			
		||||
@@ -98,53 +101,6 @@ Please note, that `DB_MYSQL_*` environment variables will take precedent over `D
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Using Postgres database
 | 
			
		||||
 | 
			
		||||
Similar to the MySQL server setup:
 | 
			
		||||
 | 
			
		||||
```yml
 | 
			
		||||
services:
 | 
			
		||||
  app:
 | 
			
		||||
    image: 'jc21/nginx-proxy-manager:latest'
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    ports:
 | 
			
		||||
      # These ports are in format <host-port>:<container-port>
 | 
			
		||||
      - '80:80' # Public HTTP Port
 | 
			
		||||
      - '443:443' # Public HTTPS Port
 | 
			
		||||
      - '81:81' # Admin Web Port
 | 
			
		||||
      # Add any other Stream port you want to expose
 | 
			
		||||
      # - '21:21' # FTP
 | 
			
		||||
    environment:
 | 
			
		||||
      # Postgres parameters:
 | 
			
		||||
      DB_POSTGRES_HOST: 'db'
 | 
			
		||||
      DB_POSTGRES_PORT: '5432'
 | 
			
		||||
      DB_POSTGRES_USER: 'npm'
 | 
			
		||||
      DB_POSTGRES_PASSWORD: 'npmpass'
 | 
			
		||||
      DB_POSTGRES_NAME: 'npm'
 | 
			
		||||
      # Uncomment this if IPv6 is not enabled on your host
 | 
			
		||||
      # DISABLE_IPV6: 'true'
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./data:/data
 | 
			
		||||
      - ./letsencrypt:/etc/letsencrypt
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - db
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    image: postgres:latest
 | 
			
		||||
    environment:
 | 
			
		||||
      POSTGRES_USER: 'npm'
 | 
			
		||||
      POSTGRES_PASSWORD: 'npmpass'
 | 
			
		||||
      POSTGRES_DB: 'npm'
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./postgres:/var/lib/postgresql/data
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
::: warning
 | 
			
		||||
 | 
			
		||||
Custom Postgres schema is not supported, as such `public` will be used.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Running on Raspberry PI / ARM devices
 | 
			
		||||
 | 
			
		||||
The docker images support the following architectures:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								docs/src/third-party/index.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								docs/src/third-party/index.md
									
									
									
									
										vendored
									
									
								
							@@ -12,7 +12,6 @@ Known integrations:
 | 
			
		||||
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
 | 
			
		||||
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
 | 
			
		||||
- [Proxmox Scripts](https://github.com/ej52/proxmox-scripts/tree/main/apps/nginx-proxy-manager)
 | 
			
		||||
- [Proxmox VE Helper-Scripts](https://community-scripts.github.io/ProxmoxVE/scripts?id=nginxproxymanager)
 | 
			
		||||
- [nginxproxymanagerGraf](https://github.com/ma-karai/nginxproxymanagerGraf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -873,9 +873,9 @@ mitt@^3.0.1:
 | 
			
		||||
  integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
 | 
			
		||||
 | 
			
		||||
nanoid@^3.3.7:
 | 
			
		||||
  version "3.3.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
 | 
			
		||||
  integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
 | 
			
		||||
  version "3.3.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
 | 
			
		||||
  integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
 | 
			
		||||
 | 
			
		||||
oniguruma-to-js@0.4.3:
 | 
			
		||||
  version "0.4.3"
 | 
			
		||||
@@ -1065,9 +1065,9 @@ vfile@^6.0.0:
 | 
			
		||||
    vfile-message "^4.0.0"
 | 
			
		||||
 | 
			
		||||
vite@^5.4.8:
 | 
			
		||||
  version "5.4.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.19.tgz#20efd060410044b3ed555049418a5e7d1998f959"
 | 
			
		||||
  integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==
 | 
			
		||||
  version "5.4.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.8.tgz#af548ce1c211b2785478d3ba3e8da51e39a287e8"
 | 
			
		||||
  integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    esbuild "^0.21.3"
 | 
			
		||||
    postcss "^8.4.43"
 | 
			
		||||
 
 | 
			
		||||
@@ -4,438 +4,444 @@ const Tokens   = require('./tokens');
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param {String} route
 | 
			
		||||
	 * @param {Object} [options]
 | 
			
		||||
	 * @returns {Boolean}
 | 
			
		||||
	 */
 | 
			
		||||
	navigate: function (route, options) {
 | 
			
		||||
		options = options || {};
 | 
			
		||||
		Backbone.history.navigate(route.toString(), options);
 | 
			
		||||
		return true;
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {String} route
 | 
			
		||||
     * @param {Object} [options]
 | 
			
		||||
     * @returns {Boolean}
 | 
			
		||||
     */
 | 
			
		||||
    navigate: function (route, options) {
 | 
			
		||||
        options = options || {};
 | 
			
		||||
        Backbone.history.navigate(route.toString(), options);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Login
 | 
			
		||||
	 */
 | 
			
		||||
	showLogin: function () {
 | 
			
		||||
		window.location = '/login';
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Login
 | 
			
		||||
     */
 | 
			
		||||
    showLogin: function () {
 | 
			
		||||
        window.location = '/login';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Users
 | 
			
		||||
	 */
 | 
			
		||||
	showUsers: function () {
 | 
			
		||||
		const controller = this;
 | 
			
		||||
		if (Cache.User.isAdmin()) {
 | 
			
		||||
			require(['./main', './users/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/users');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			this.showDashboard();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Users
 | 
			
		||||
     */
 | 
			
		||||
    showUsers: function () {
 | 
			
		||||
        let controller = this;
 | 
			
		||||
        if (Cache.User.isAdmin()) {
 | 
			
		||||
            require(['./main', './users/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/users');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.showDashboard();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * User Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param [model]
 | 
			
		||||
	 */
 | 
			
		||||
	showUserForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin()) {
 | 
			
		||||
			require(['./main', './user/form'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * User Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param [model]
 | 
			
		||||
     */
 | 
			
		||||
    showUserForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin()) {
 | 
			
		||||
            require(['./main', './user/form'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * User Permissions Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showUserPermissions: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin()) {
 | 
			
		||||
			require(['./main', './user/permissions'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * User Permissions Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showUserPermissions: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin()) {
 | 
			
		||||
            require(['./main', './user/permissions'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * User Password Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showUserPasswordForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
 | 
			
		||||
			require(['./main', './user/password'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * User Password Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showUserPasswordForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) {
 | 
			
		||||
            require(['./main', './user/password'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * User Delete Confirm
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showUserDeleteConfirm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
 | 
			
		||||
			require(['./main', './user/delete'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * User Delete Confirm
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showUserDeleteConfirm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
 | 
			
		||||
            require(['./main', './user/delete'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Dashboard
 | 
			
		||||
	 */
 | 
			
		||||
	showDashboard: function () {
 | 
			
		||||
		const controller = this;
 | 
			
		||||
		require(['./main', './dashboard/main'], (App, View) => {
 | 
			
		||||
			controller.navigate('/');
 | 
			
		||||
			App.UI.showAppContent(new View());
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Dashboard
 | 
			
		||||
     */
 | 
			
		||||
    showDashboard: function () {
 | 
			
		||||
        let controller = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Proxy Hosts
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxProxy: function () {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
 | 
			
		||||
			const controller = this;
 | 
			
		||||
        require(['./main', './dashboard/main'], (App, View) => {
 | 
			
		||||
            controller.navigate('/');
 | 
			
		||||
            App.UI.showAppContent(new View());
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
			require(['./main', './nginx/proxy/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/nginx/proxy');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Proxy Hosts
 | 
			
		||||
     */
 | 
			
		||||
    showNginxProxy: function () {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
 | 
			
		||||
            let controller = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Proxy Host Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param [model]
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxProxyForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
 | 
			
		||||
			require(['./main', './nginx/proxy/form'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
            require(['./main', './nginx/proxy/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/nginx/proxy');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Proxy Host Delete Confirm
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxProxyDeleteConfirm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
 | 
			
		||||
			require(['./main', './nginx/proxy/delete'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Proxy Host Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param [model]
 | 
			
		||||
     */
 | 
			
		||||
    showNginxProxyForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
 | 
			
		||||
            require(['./main', './nginx/proxy/form'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Redirection Hosts
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxRedirection: function () {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
 | 
			
		||||
			const controller = this;
 | 
			
		||||
			require(['./main', './nginx/redirection/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/nginx/redirection');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Proxy Host Delete Confirm
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxProxyDeleteConfirm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('proxy_hosts')) {
 | 
			
		||||
            require(['./main', './nginx/proxy/delete'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Redirection Host Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param [model]
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxRedirectionForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
 | 
			
		||||
			require(['./main', './nginx/redirection/form'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Redirection Hosts
 | 
			
		||||
     */
 | 
			
		||||
    showNginxRedirection: function () {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
 | 
			
		||||
            let controller = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Proxy Redirection Delete Confirm
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxRedirectionDeleteConfirm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
 | 
			
		||||
			require(['./main', './nginx/redirection/delete'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
            require(['./main', './nginx/redirection/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/nginx/redirection');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Stream Hosts
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxStream: function () {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
 | 
			
		||||
			const controller = this;
 | 
			
		||||
			require(['./main', './nginx/stream/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/nginx/stream');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Redirection Host Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param [model]
 | 
			
		||||
     */
 | 
			
		||||
    showNginxRedirectionForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
 | 
			
		||||
            require(['./main', './nginx/redirection/form'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Stream Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param [model]
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxStreamForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
 | 
			
		||||
			require(['./main', './nginx/stream/form'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Proxy Redirection Delete Confirm
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxRedirectionDeleteConfirm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('redirection_hosts')) {
 | 
			
		||||
            require(['./main', './nginx/redirection/delete'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Stream Delete Confirm
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxStreamDeleteConfirm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
 | 
			
		||||
			require(['./main', './nginx/stream/delete'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Stream Hosts
 | 
			
		||||
     */
 | 
			
		||||
    showNginxStream: function () {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
 | 
			
		||||
            let controller = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Dead Hosts
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxDead: function () {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
 | 
			
		||||
			const controller = this;
 | 
			
		||||
			require(['./main', './nginx/dead/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/nginx/404');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
            require(['./main', './nginx/stream/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/nginx/stream');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Dead Host Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param [model]
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxDeadForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
 | 
			
		||||
			require(['./main', './nginx/dead/form'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Stream Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param [model]
 | 
			
		||||
     */
 | 
			
		||||
    showNginxStreamForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
 | 
			
		||||
            require(['./main', './nginx/stream/form'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Dead Host Delete Confirm
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxDeadDeleteConfirm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
 | 
			
		||||
			require(['./main', './nginx/dead/delete'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Stream Delete Confirm
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxStreamDeleteConfirm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('streams')) {
 | 
			
		||||
            require(['./main', './nginx/stream/delete'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Help Dialog
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param {String}  title
 | 
			
		||||
	 * @param {String}  content
 | 
			
		||||
	 */
 | 
			
		||||
	showHelp: function (title, content) {
 | 
			
		||||
		require(['./main', './help/main'], function (App, View) {
 | 
			
		||||
			App.UI.showModalDialog(new View({title: title, content: content}));
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Dead Hosts
 | 
			
		||||
     */
 | 
			
		||||
    showNginxDead: function () {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
 | 
			
		||||
            let controller = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Access
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxAccess: function () {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
 | 
			
		||||
			const controller = this;
 | 
			
		||||
			require(['./main', './nginx/access/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/nginx/access');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
            require(['./main', './nginx/dead/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/nginx/404');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Access List Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param [model]
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxAccessListForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
 | 
			
		||||
			require(['./main', './nginx/access/form'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Dead Host Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param [model]
 | 
			
		||||
     */
 | 
			
		||||
    showNginxDeadForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
 | 
			
		||||
            require(['./main', './nginx/dead/form'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Access List Delete Confirm
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxAccessListDeleteConfirm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
 | 
			
		||||
			require(['./main', './nginx/access/delete'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Dead Host Delete Confirm
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxDeadDeleteConfirm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('dead_hosts')) {
 | 
			
		||||
            require(['./main', './nginx/dead/delete'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Certificates
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxCertificates: function () {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
 | 
			
		||||
			const controller = this;
 | 
			
		||||
			require(['./main', './nginx/certificates/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/nginx/certificates');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Help Dialog
 | 
			
		||||
     *
 | 
			
		||||
     * @param {String}  title
 | 
			
		||||
     * @param {String}  content
 | 
			
		||||
     */
 | 
			
		||||
    showHelp: function (title, content) {
 | 
			
		||||
        require(['./main', './help/main'], function (App, View) {
 | 
			
		||||
            App.UI.showModalDialog(new View({title: title, content: content}));
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nginx Certificate Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param [model]
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxCertificateForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
			require(['./main', './nginx/certificates/form'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Access
 | 
			
		||||
     */
 | 
			
		||||
    showNginxAccess: function () {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
 | 
			
		||||
            let controller = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Certificate Renew
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxCertificateRenew: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
			require(['./main', './nginx/certificates/renew'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
            require(['./main', './nginx/access/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/nginx/access');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Certificate Delete Confirm
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxCertificateDeleteConfirm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
			require(['./main', './nginx/certificates/delete'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Access List Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param [model]
 | 
			
		||||
     */
 | 
			
		||||
    showNginxAccessListForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
 | 
			
		||||
            require(['./main', './nginx/access/form'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Certificate Test Reachability
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showNginxCertificateTestReachability: function (model) {
 | 
			
		||||
	  if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
		require(['./main', './nginx/certificates/test'], function (App, View) {
 | 
			
		||||
		  App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
		});
 | 
			
		||||
	  }
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Access List Delete Confirm
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxAccessListDeleteConfirm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('access_lists')) {
 | 
			
		||||
            require(['./main', './nginx/access/delete'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Audit Log
 | 
			
		||||
	 */
 | 
			
		||||
	showAuditLog: function () {
 | 
			
		||||
		const controller = this;
 | 
			
		||||
		if (Cache.User.isAdmin()) {
 | 
			
		||||
			require(['./main', './audit-log/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/audit-log');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			this.showDashboard();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Certificates
 | 
			
		||||
     */
 | 
			
		||||
    showNginxCertificates: function () {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
 | 
			
		||||
            let controller = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Audit Log Metadata
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showAuditMeta: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin()) {
 | 
			
		||||
			require(['./main', './audit-log/meta'], function (App, View) {
 | 
			
		||||
				App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
            require(['./main', './nginx/certificates/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/nginx/certificates');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Settings
 | 
			
		||||
	 */
 | 
			
		||||
	showSettings: function () {
 | 
			
		||||
		const controller = this;
 | 
			
		||||
		if (Cache.User.isAdmin()) {
 | 
			
		||||
			require(['./main', './settings/main'], (App, View) => {
 | 
			
		||||
				controller.navigate('/settings');
 | 
			
		||||
				App.UI.showAppContent(new View());
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			this.showDashboard();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Nginx Certificate Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param [model]
 | 
			
		||||
     */
 | 
			
		||||
    showNginxCertificateForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
            require(['./main', './nginx/certificates/form'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Settings Item Form
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param model
 | 
			
		||||
	 */
 | 
			
		||||
	showSettingForm: function (model) {
 | 
			
		||||
		if (Cache.User.isAdmin()) {
 | 
			
		||||
			if (model.get('id') === 'default-site') {
 | 
			
		||||
				require(['./main', './settings/default-site/main'], function (App, View) {
 | 
			
		||||
					App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    /**
 | 
			
		||||
     * Certificate Renew
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxCertificateRenew: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
            require(['./main', './nginx/certificates/renew'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Logout
 | 
			
		||||
	 */
 | 
			
		||||
	logout: function () {
 | 
			
		||||
		Tokens.dropTopToken();
 | 
			
		||||
		this.showLogin();
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * Certificate Delete Confirm
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxCertificateDeleteConfirm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
            require(['./main', './nginx/certificates/delete'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Certificate Test Reachability
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showNginxCertificateTestReachability: function (model) {
 | 
			
		||||
      if (Cache.User.isAdmin() || Cache.User.canManage('certificates')) {
 | 
			
		||||
        require(['./main', './nginx/certificates/test'], function (App, View) {
 | 
			
		||||
          App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Audit Log
 | 
			
		||||
     */
 | 
			
		||||
    showAuditLog: function () {
 | 
			
		||||
        let controller = this;
 | 
			
		||||
        if (Cache.User.isAdmin()) {
 | 
			
		||||
            require(['./main', './audit-log/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/audit-log');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.showDashboard();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Audit Log Metadata
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showAuditMeta: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin()) {
 | 
			
		||||
            require(['./main', './audit-log/meta'], function (App, View) {
 | 
			
		||||
                App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Settings
 | 
			
		||||
     */
 | 
			
		||||
    showSettings: function () {
 | 
			
		||||
        let controller = this;
 | 
			
		||||
        if (Cache.User.isAdmin()) {
 | 
			
		||||
            require(['./main', './settings/main'], (App, View) => {
 | 
			
		||||
                controller.navigate('/settings');
 | 
			
		||||
                App.UI.showAppContent(new View());
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.showDashboard();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Settings Item Form
 | 
			
		||||
     *
 | 
			
		||||
     * @param model
 | 
			
		||||
     */
 | 
			
		||||
    showSettingForm: function (model) {
 | 
			
		||||
        if (Cache.User.isAdmin()) {
 | 
			
		||||
            if (model.get('id') === 'default-site') {
 | 
			
		||||
                require(['./main', './settings/default-site/main'], function (App, View) {
 | 
			
		||||
                    App.UI.showModalDialog(new View({model: model}));
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Logout
 | 
			
		||||
     */
 | 
			
		||||
    logout: function () {
 | 
			
		||||
        Tokens.dropTopToken();
 | 
			
		||||
        this.showLogin();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -6,85 +6,87 @@ const Helpers    = require('../../lib/helpers');
 | 
			
		||||
const template   = require('./main.ejs');
 | 
			
		||||
 | 
			
		||||
module.exports = Mn.View.extend({
 | 
			
		||||
	template: template,
 | 
			
		||||
	id:       'dashboard',
 | 
			
		||||
	columns:  0,
 | 
			
		||||
    template: template,
 | 
			
		||||
    id:       'dashboard',
 | 
			
		||||
    columns:  0,
 | 
			
		||||
 | 
			
		||||
	stats: {},
 | 
			
		||||
    stats: {},
 | 
			
		||||
 | 
			
		||||
	ui: {
 | 
			
		||||
		links: 'a'
 | 
			
		||||
	},
 | 
			
		||||
    ui: {
 | 
			
		||||
        links: 'a'
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	events: {
 | 
			
		||||
		'click @ui.links': function (e) {
 | 
			
		||||
			e.preventDefault();
 | 
			
		||||
			Controller.navigate($(e.currentTarget).attr('href'), true);
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    events: {
 | 
			
		||||
        'click @ui.links': function (e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            Controller.navigate($(e.currentTarget).attr('href'), true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	templateContext: function () {
 | 
			
		||||
		const view = this;
 | 
			
		||||
    templateContext: function () {
 | 
			
		||||
        let view = this;
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			getUserName: function () {
 | 
			
		||||
				return Cache.User.get('nickname') || Cache.User.get('name');
 | 
			
		||||
			},
 | 
			
		||||
        return {
 | 
			
		||||
            getUserName: function () {
 | 
			
		||||
                return Cache.User.get('nickname') || Cache.User.get('name');
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
			getHostStat: function (type) {
 | 
			
		||||
				if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
 | 
			
		||||
					return Helpers.niceNumber(view.stats.hosts[type]);
 | 
			
		||||
				}
 | 
			
		||||
            getHostStat: function (type) {
 | 
			
		||||
                if (view.stats && typeof view.stats.hosts !== 'undefined' && typeof view.stats.hosts[type] !== 'undefined') {
 | 
			
		||||
                    return Helpers.niceNumber(view.stats.hosts[type]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
				return '-';
 | 
			
		||||
			},
 | 
			
		||||
                return '-';
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
			canShow: function (perm) {
 | 
			
		||||
				return Cache.User.isAdmin() || Cache.User.canView(perm);
 | 
			
		||||
			},
 | 
			
		||||
            canShow: function (perm) {
 | 
			
		||||
                return Cache.User.isAdmin() || Cache.User.canView(perm);
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
			columns: view.columns
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
            columns: view.columns
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
	onRender: function () {
 | 
			
		||||
		const view = this;
 | 
			
		||||
		if (typeof view.stats.hosts === 'undefined') {
 | 
			
		||||
			Api.Reports.getHostStats()
 | 
			
		||||
				.then(response => {
 | 
			
		||||
					if (!view.isDestroyed()) {
 | 
			
		||||
						view.stats.hosts = response;
 | 
			
		||||
						view.render();
 | 
			
		||||
					}
 | 
			
		||||
				})
 | 
			
		||||
				.catch(err => {
 | 
			
		||||
					console.log(err);
 | 
			
		||||
				});
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
    onRender: function () {
 | 
			
		||||
        let view = this;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param {Object}  [model]
 | 
			
		||||
	 */
 | 
			
		||||
	preRender: function (model) {
 | 
			
		||||
		this.columns = 0;
 | 
			
		||||
        if (typeof view.stats.hosts === 'undefined') {
 | 
			
		||||
            Api.Reports.getHostStats()
 | 
			
		||||
                .then(response => {
 | 
			
		||||
                    if (!view.isDestroyed()) {
 | 
			
		||||
                        view.stats.hosts = response;
 | 
			
		||||
                        view.render();
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .catch(err => {
 | 
			
		||||
                    console.log(err);
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
		// calculate the available columns based on permissions for the objects
 | 
			
		||||
		// and store as a variable
 | 
			
		||||
		const perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object}  [model]
 | 
			
		||||
     */
 | 
			
		||||
    preRender: function (model) {
 | 
			
		||||
        this.columns = 0;
 | 
			
		||||
 | 
			
		||||
		perms.map(perm => {
 | 
			
		||||
			this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
 | 
			
		||||
		});
 | 
			
		||||
        // calculate the available columns based on permissions for the objects
 | 
			
		||||
        // and store as a variable
 | 
			
		||||
        //let view = this;
 | 
			
		||||
        let perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
 | 
			
		||||
 | 
			
		||||
		// Prevent double rendering on initial calls
 | 
			
		||||
		if (typeof model !== 'undefined') {
 | 
			
		||||
			this.render();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
        perms.map(perm => {
 | 
			
		||||
            this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
	initialize: function () {
 | 
			
		||||
		this.preRender();
 | 
			
		||||
		this.listenTo(Cache.User, 'change', this.preRender);
 | 
			
		||||
	}
 | 
			
		||||
        // Prevent double rendering on initial calls
 | 
			
		||||
        if (typeof model !== 'undefined') {
 | 
			
		||||
            this.render();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    initialize: function () {
 | 
			
		||||
        this.preRender();
 | 
			
		||||
        this.listenTo(Cache.User, 'change', this.preRender);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -33,13 +33,6 @@
 | 
			
		||||
<td class="<%- isExpired() ? 'text-danger' : '' %>">
 | 
			
		||||
    <%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
 | 
			
		||||
</td>
 | 
			
		||||
<td>
 | 
			
		||||
    <% if (active_domain_names().length > 0) { %>
 | 
			
		||||
        <span class="status-icon bg-success"></span> <%- i18n('certificates', 'in-use') %>
 | 
			
		||||
    <% } else { %>
 | 
			
		||||
        <span class="status-icon bg-danger"></span> <%- i18n('certificates', 'inactive') %>
 | 
			
		||||
    <% } %>
 | 
			
		||||
</td>
 | 
			
		||||
<% if (canManage) { %>
 | 
			
		||||
<td class="text-right">
 | 
			
		||||
    <div class="item-action dropdown">
 | 
			
		||||
@@ -55,14 +48,7 @@
 | 
			
		||||
                <div class="dropdown-divider"></div>
 | 
			
		||||
            <% } %>
 | 
			
		||||
            <a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
 | 
			
		||||
            <% if (active_domain_names().length > 0) { %>
 | 
			
		||||
                <div class="dropdown-divider"></div>
 | 
			
		||||
                <span class="dropdown-header"><%- i18n('certificates', 'active-domain_names') %></span>
 | 
			
		||||
                <% active_domain_names().forEach(function(host) { %>
 | 
			
		||||
                    <a href="https://<%- host %>" class="dropdown-item" target="_blank"><%- host %></a>
 | 
			
		||||
                <% }); %>
 | 
			
		||||
            <% } %>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</td>
 | 
			
		||||
<% } %>
 | 
			
		||||
<% } %>
 | 
			
		||||
 
 | 
			
		||||
@@ -44,24 +44,14 @@ module.exports = Mn.View.extend({
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    templateContext: function () {
 | 
			
		||||
        return {
 | 
			
		||||
            canManage: App.Cache.User.canManage('certificates'),
 | 
			
		||||
            isExpired: function () {
 | 
			
		||||
                return moment(this.expires_on).isBefore(moment());
 | 
			
		||||
            },
 | 
			
		||||
            dns_providers: dns_providers,
 | 
			
		||||
            active_domain_names: function () {
 | 
			
		||||
                const { proxy_hosts = [], redirect_hosts = [], dead_hosts = [] } = this;
 | 
			
		||||
                return [...proxy_hosts, ...redirect_hosts, ...dead_hosts].reduce((acc, host) => {
 | 
			
		||||
                    acc.push(...(host.domain_names || []));
 | 
			
		||||
                    return acc;
 | 
			
		||||
                }, []);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    templateContext: {
 | 
			
		||||
        canManage: App.Cache.User.canManage('certificates'),
 | 
			
		||||
        isExpired: function () {
 | 
			
		||||
            return moment(this.expires_on).isBefore(moment());
 | 
			
		||||
        },
 | 
			
		||||
        dns_providers: dns_providers
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    initialize: function () {
 | 
			
		||||
        this.listenTo(this.model, 'change', this.render);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
    <th><%- i18n('str', 'name') %></th>
 | 
			
		||||
    <th><%- i18n('all-hosts', 'cert-provider') %></th>
 | 
			
		||||
    <th><%- i18n('str', 'expires') %></th>
 | 
			
		||||
    <th><%- i18n('str', 'status') %></th>
 | 
			
		||||
    <% if (canManage) { %>
 | 
			
		||||
    <th> </th>
 | 
			
		||||
    <% } %>
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,7 @@ module.exports = Mn.View.extend({
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            let query = this.ui.query.val();
 | 
			
		||||
 | 
			
		||||
            this.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'], query)
 | 
			
		||||
            this.fetch(['owner'], query)
 | 
			
		||||
                .then(response => this.showData(response))
 | 
			
		||||
                .catch(err => {
 | 
			
		||||
                    this.showError(err);
 | 
			
		||||
@@ -89,7 +89,7 @@ module.exports = Mn.View.extend({
 | 
			
		||||
    onRender: function () {
 | 
			
		||||
        let view = this;
 | 
			
		||||
 | 
			
		||||
        view.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'])
 | 
			
		||||
        view.fetch(['owner'])
 | 
			
		||||
            .then(response => {
 | 
			
		||||
                if (!view.isDestroyed()) {
 | 
			
		||||
                    if (response && response.length) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,187 +3,48 @@
 | 
			
		||||
        <h5 class="modal-title"><%- i18n('streams', 'form-title', {id: id}) %></h5>
 | 
			
		||||
        <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="modal-body has-tabs">
 | 
			
		||||
        <div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
 | 
			
		||||
    <div class="modal-body">
 | 
			
		||||
        <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="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
            <div class="tab-content">
 | 
			
		||||
                <!-- Details -->
 | 
			
		||||
                <div role="tabpanel" class="tab-pane active" id="details">
 | 
			
		||||
                    <div class="row">
 | 
			
		||||
                        <div class="col-sm-12 col-md-12">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="form-label"><%- i18n('streams', 'incoming-port') %> <span class="form-required">*</span></label>
 | 
			
		||||
                                <input name="incoming_port" type="number" class="form-control text-monospace" placeholder="eg: 8080" min="1" max="65535" value="<%- incoming_port %>" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-8 col-md-8">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="form-label"><%- i18n('streams', 'forwarding-host') %><span class="form-required">*</span></label>
 | 
			
		||||
                                <input type="text" name="forwarding_host" class="form-control text-monospace" placeholder="example.com or 10.0.0.1 or 2001:db8:3333:4444:5555:6666:7777:8888" value="<%- forwarding_host %>" autocomplete="off" maxlength="255" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-4 col-md-4">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="form-label"><%- i18n('streams', 'forwarding-port') %> <span class="form-required">*</span></label>
 | 
			
		||||
                                <input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 80" min="1" max="65535" value="<%- forwarding_port %>" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-6 col-md-6">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="custom-switch">
 | 
			
		||||
                                    <input type="checkbox" class="custom-switch-input" name="tcp_forwarding" value="1"<%- tcp_forwarding ? ' checked' : '' %>>
 | 
			
		||||
                                    <span class="custom-switch-indicator"></span>
 | 
			
		||||
                                    <span class="custom-switch-description"><%- i18n('streams', 'tcp-forwarding') %></span>
 | 
			
		||||
                                </label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-6 col-md-6">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="custom-switch">
 | 
			
		||||
                                    <input type="checkbox" class="custom-switch-input" name="udp_forwarding" value="1"<%- udp_forwarding ? ' checked' : '' %>>
 | 
			
		||||
                                    <span class="custom-switch-indicator"></span>
 | 
			
		||||
                                    <span class="custom-switch-description"><%- i18n('streams', 'udp-forwarding') %></span>
 | 
			
		||||
                                </label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-12 col-md-12">
 | 
			
		||||
                            <div class="forward-type-error invalid-feedback"><%- i18n('streams', 'forward-type-error') %></div>
 | 
			
		||||
                        </div>
 | 
			
		||||
            <div class="row">
 | 
			
		||||
                <div class="col-sm-12 col-md-12">
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
                        <label class="form-label"><%- i18n('streams', 'incoming-port') %> <span class="form-required">*</span></label>
 | 
			
		||||
                        <input name="incoming_port" type="number" class="form-control text-monospace" placeholder="eg: 8080" min="1" max="65535" value="<%- incoming_port %>" required>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <!-- SSL -->
 | 
			
		||||
                <div role="tabpanel" class="tab-pane" id="ssl-options">
 | 
			
		||||
                    <div class="row">
 | 
			
		||||
                        <div class="col-sm-12 col-md-12">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="form-label"><%- i18n('streams', 'ssl-certificate') %></label>
 | 
			
		||||
                                <select name="certificate_id" class="form-control custom-select" placeholder="<%- i18n('all-hosts', 'none') %>">
 | 
			
		||||
                                    <option selected value="0" data-data="{"id":0}" <%- certificate_id ? '' : 'selected' %>><%- i18n('all-hosts', 'none') %></option>
 | 
			
		||||
                                    <option selected value="new" data-data="{"id":"new"}"><%- i18n('all-hosts', 'new-cert') %></option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
                        <!-- DNS challenge -->
 | 
			
		||||
                        <div class="col-sm-12 col-md-12 letsencrypt">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label>
 | 
			
		||||
                                <input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>">
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="custom-switch">
 | 
			
		||||
                                    <input
 | 
			
		||||
                                            type="checkbox"
 | 
			
		||||
                                            class="custom-switch-input"
 | 
			
		||||
                                            name="meta[dns_challenge]"
 | 
			
		||||
                                            value="1"
 | 
			
		||||
                                            checked
 | 
			
		||||
                                            disabled
 | 
			
		||||
                                    >
 | 
			
		||||
                                    <span class="custom-switch-indicator"></span>
 | 
			
		||||
                                    <span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
 | 
			
		||||
                                </label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-12 col-md-12 letsencrypt">
 | 
			
		||||
                            <fieldset class="form-fieldset dns-challenge">
 | 
			
		||||
                                <div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
 | 
			
		||||
 | 
			
		||||
                                <!-- Certbot DNS plugin selection -->
 | 
			
		||||
                                <div class="row">
 | 
			
		||||
                                    <div class="col-sm-12 col-md-12">
 | 
			
		||||
                                        <div class="form-group">
 | 
			
		||||
                                            <label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
 | 
			
		||||
                                            <select
 | 
			
		||||
                                                    name="meta[dns_provider]"
 | 
			
		||||
                                                    id="dns_provider"
 | 
			
		||||
                                                    class="form-control custom-select"
 | 
			
		||||
                                            >
 | 
			
		||||
                                                <option
 | 
			
		||||
                                                        value=""
 | 
			
		||||
                                                        disabled
 | 
			
		||||
                                                        hidden
 | 
			
		||||
                                                        <%- getDnsProvider() === null ? 'selected' : '' %>
 | 
			
		||||
                                                >Please Choose...</option>
 | 
			
		||||
                                                <% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
 | 
			
		||||
                                                    <option
 | 
			
		||||
                                                            value="<%- plugin_name %>"
 | 
			
		||||
                                                            <%- getDnsProvider() === plugin_name ? 'selected' : '' %>
 | 
			
		||||
                                                    ><%- plugin_info.name %></option>
 | 
			
		||||
                                                <% }); %>
 | 
			
		||||
                                            </select>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
 | 
			
		||||
                                <!-- Certbot credentials file content -->
 | 
			
		||||
                                <div class="row credentials-file-content">
 | 
			
		||||
                                    <div class="col-sm-12 col-md-12">
 | 
			
		||||
                                        <div class="form-group">
 | 
			
		||||
                                            <label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
 | 
			
		||||
                                            <textarea
 | 
			
		||||
                                                    name="meta[dns_provider_credentials]"
 | 
			
		||||
                                                    class="form-control text-monospace"
 | 
			
		||||
                                                    id="dns_provider_credentials"
 | 
			
		||||
                                            ><%- getDnsProviderCredentials() %></textarea>
 | 
			
		||||
                                            <div class="text-secondary small">
 | 
			
		||||
                                                <i class="fe fe-info"></i>
 | 
			
		||||
                                                <%= i18n('ssl', 'credentials-file-content-info') %>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div class="text-red small">
 | 
			
		||||
                                                <i class="fe fe-alert-triangle"></i>
 | 
			
		||||
                                                <%= i18n('ssl', 'stored-as-plaintext-info') %>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
 | 
			
		||||
                                <!-- DNS propagation delay -->
 | 
			
		||||
                                <div class="row">
 | 
			
		||||
                                    <div class="col-sm-12 col-md-12">
 | 
			
		||||
                                        <div class="form-group mb-0">
 | 
			
		||||
                                            <label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
 | 
			
		||||
                                            <input
 | 
			
		||||
                                                    type="number"
 | 
			
		||||
                                                    min="0"
 | 
			
		||||
                                                    name="meta[propagation_seconds]"
 | 
			
		||||
                                                    class="form-control"
 | 
			
		||||
                                                    id="propagation_seconds"
 | 
			
		||||
                                                    value="<%- getPropagationSeconds() %>"
 | 
			
		||||
                                            >
 | 
			
		||||
                                            <div class="text-secondary small">
 | 
			
		||||
                                                <i class="fe fe-info"></i>
 | 
			
		||||
                                                <%= i18n('ssl', 'propagation-seconds-info') %>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </fieldset>
 | 
			
		||||
                        </div>
 | 
			
		||||
 | 
			
		||||
                        <!-- Lets encrypt -->
 | 
			
		||||
                        <div class="col-sm-12 col-md-12 letsencrypt">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="form-label"><%- i18n('ssl', 'letsencrypt-email') %> <span class="form-required">*</span></label>
 | 
			
		||||
                                <input name="meta[letsencrypt_email]" type="email" class="form-control" placeholder="" value="<%- getLetsencryptEmail() %>" required disabled>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-sm-12 col-md-12 letsencrypt">
 | 
			
		||||
                            <div class="form-group">
 | 
			
		||||
                                <label class="custom-switch">
 | 
			
		||||
                                    <input type="checkbox" class="custom-switch-input" name="meta[letsencrypt_agree]" value="1" required disabled>
 | 
			
		||||
                                    <span class="custom-switch-indicator"></span>
 | 
			
		||||
                                    <span class="custom-switch-description"><%= i18n('ssl', 'letsencrypt-agree', {url: 'https://letsencrypt.org/repository/'}) %> <span class="form-required">*</span></span>
 | 
			
		||||
                                </label>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                <div class="col-sm-8 col-md-8">
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
                        <label class="form-label"><%- i18n('streams', 'forwarding-host') %><span class="form-required">*</span></label>
 | 
			
		||||
                        <input type="text" name="forwarding_host" class="form-control text-monospace" placeholder="example.com or 10.0.0.1 or 2001:db8:3333:4444:5555:6666:7777:8888" value="<%- forwarding_host %>" autocomplete="off" maxlength="255" required>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="col-sm-4 col-md-4">
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
                        <label class="form-label"><%- i18n('streams', 'forwarding-port') %> <span class="form-required">*</span></label>
 | 
			
		||||
                        <input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 80" min="1" max="65535" value="<%- forwarding_port %>" required>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="col-sm-6 col-md-6">
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
                        <label class="custom-switch">
 | 
			
		||||
                            <input type="checkbox" class="custom-switch-input" name="tcp_forwarding" value="1"<%- tcp_forwarding ? ' checked' : '' %>>
 | 
			
		||||
                            <span class="custom-switch-indicator"></span>
 | 
			
		||||
                            <span class="custom-switch-description"><%- i18n('streams', 'tcp-forwarding') %></span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="col-sm-6 col-md-6">
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
                        <label class="custom-switch">
 | 
			
		||||
                            <input type="checkbox" class="custom-switch-input" name="udp_forwarding" value="1"<%- udp_forwarding ? ' checked' : '' %>>
 | 
			
		||||
                            <span class="custom-switch-indicator"></span>
 | 
			
		||||
                            <span class="custom-switch-description"><%- i18n('streams', 'udp-forwarding') %></span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="col-sm-12 col-md-12">
 | 
			
		||||
                    <div class="forward-type-error invalid-feedback"><%- i18n('streams', 'forward-type-error') %></div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,38 +1,24 @@
 | 
			
		||||
const Mn            = require('backbone.marionette');
 | 
			
		||||
const App           = require('../../main');
 | 
			
		||||
const StreamModel   = require('../../../models/stream');
 | 
			
		||||
const template      = require('./form.ejs');
 | 
			
		||||
const dns_providers = require('../../../../../global/certbot-dns-plugins');
 | 
			
		||||
const Mn          = require('backbone.marionette');
 | 
			
		||||
const App         = require('../../main');
 | 
			
		||||
const StreamModel = require('../../../models/stream');
 | 
			
		||||
const template    = require('./form.ejs');
 | 
			
		||||
 | 
			
		||||
require('jquery-serializejson');
 | 
			
		||||
require('jquery-mask-plugin');
 | 
			
		||||
require('selectize');
 | 
			
		||||
const Helpers = require("../../../lib/helpers");
 | 
			
		||||
const certListItemTemplate = require("../certificates-list-item.ejs");
 | 
			
		||||
const i18n = require("../../i18n");
 | 
			
		||||
 | 
			
		||||
module.exports = Mn.View.extend({
 | 
			
		||||
    template:  template,
 | 
			
		||||
    className: 'modal-dialog',
 | 
			
		||||
 | 
			
		||||
    ui: {
 | 
			
		||||
        form:                     'form',
 | 
			
		||||
        forwarding_host:          'input[name="forwarding_host"]',
 | 
			
		||||
        type_error:               '.forward-type-error',
 | 
			
		||||
        buttons:                  '.modal-footer button',
 | 
			
		||||
        switches:                 '.custom-switch-input',
 | 
			
		||||
        cancel:                   'button.cancel',
 | 
			
		||||
        save:                     'button.save',
 | 
			
		||||
        le_error_info:            '#le-error-info',
 | 
			
		||||
        certificate_select:       'select[name="certificate_id"]',
 | 
			
		||||
        domain_names:             'input[name="domain_names"]',
 | 
			
		||||
        dns_challenge_switch:     'input[name="meta[dns_challenge]"]',
 | 
			
		||||
        dns_challenge_content:    '.dns-challenge',
 | 
			
		||||
        dns_provider:             'select[name="meta[dns_provider]"]',
 | 
			
		||||
        credentials_file_content: '.credentials-file-content',
 | 
			
		||||
        dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
 | 
			
		||||
        propagation_seconds:      'input[name="meta[propagation_seconds]"]',
 | 
			
		||||
        letsencrypt:              '.letsencrypt'
 | 
			
		||||
        form:       'form',
 | 
			
		||||
        forwarding_host: 'input[name="forwarding_host"]',
 | 
			
		||||
        type_error: '.forward-type-error',
 | 
			
		||||
        buttons:    '.modal-footer button',
 | 
			
		||||
        switches:   '.custom-switch-input',
 | 
			
		||||
        cancel:     'button.cancel',
 | 
			
		||||
        save:       'button.save'
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    events: {
 | 
			
		||||
@@ -62,35 +48,6 @@ module.exports = Mn.View.extend({
 | 
			
		||||
            data.tcp_forwarding  = !!data.tcp_forwarding;
 | 
			
		||||
            data.udp_forwarding  = !!data.udp_forwarding;
 | 
			
		||||
 | 
			
		||||
            if (typeof data.meta === 'undefined') data.meta = {};
 | 
			
		||||
            data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
 | 
			
		||||
            data.meta.dns_challenge = true;
 | 
			
		||||
 | 
			
		||||
            if (data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
 | 
			
		||||
 | 
			
		||||
            if (typeof data.domain_names === 'string' && data.domain_names) {
 | 
			
		||||
                data.domain_names = data.domain_names.split(',');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Check for any domain names containing wildcards, which are not allowed with letsencrypt
 | 
			
		||||
            if (data.certificate_id === 'new') {
 | 
			
		||||
                let domain_err = false;
 | 
			
		||||
                if (!data.meta.dns_challenge) {
 | 
			
		||||
                    data.domain_names.map(function (name) {
 | 
			
		||||
                        if (name.match(/\*/im)) {
 | 
			
		||||
                            domain_err = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (domain_err) {
 | 
			
		||||
                    alert(i18n('ssl', 'no-wildcard-without-dns'));
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                data.certificate_id = parseInt(data.certificate_id, 10);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let method = App.Api.Nginx.Streams.create;
 | 
			
		||||
            let is_new = true;
 | 
			
		||||
 | 
			
		||||
@@ -113,108 +70,10 @@ module.exports = Mn.View.extend({
 | 
			
		||||
                    });
 | 
			
		||||
                })
 | 
			
		||||
                .catch(err => {
 | 
			
		||||
                    let more_info = '';
 | 
			
		||||
                    if (err.code === 500 && err.debug) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            more_info = JSON.parse(err.debug).debug.stack.join("\n");
 | 
			
		||||
                        } catch (e) {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>` : ''}`;
 | 
			
		||||
                    this.ui.le_error_info.show();
 | 
			
		||||
                    this.ui.le_error_info[0].scrollIntoView();
 | 
			
		||||
                    alert(err.message);
 | 
			
		||||
                    this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
 | 
			
		||||
                    this.ui.save.removeClass('btn-loading');
 | 
			
		||||
                });
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        'change @ui.certificate_select': function () {
 | 
			
		||||
            let id = this.ui.certificate_select.val();
 | 
			
		||||
            if (id === 'new') {
 | 
			
		||||
                this.ui.letsencrypt.show().find('input').prop('disabled', false);
 | 
			
		||||
                this.ui.domain_names.prop('required', 'required');
 | 
			
		||||
 | 
			
		||||
                this.ui.dns_challenge_switch
 | 
			
		||||
                    .prop('disabled', true)
 | 
			
		||||
                    .parents('.form-group')
 | 
			
		||||
                    .css('opacity', 0.5);
 | 
			
		||||
 | 
			
		||||
                this.ui.dns_provider.prop('required', 'required');
 | 
			
		||||
                const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
 | 
			
		||||
                if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
 | 
			
		||||
                    this.ui.dns_provider_credentials.prop('required', 'required');
 | 
			
		||||
                }
 | 
			
		||||
                this.ui.dns_challenge_content.show();
 | 
			
		||||
            } else {
 | 
			
		||||
                this.ui.letsencrypt.hide().find('input').prop('disabled', true);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        'change @ui.dns_provider': function () {
 | 
			
		||||
            const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
 | 
			
		||||
            if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
 | 
			
		||||
                this.ui.dns_provider_credentials.prop('required', 'required');
 | 
			
		||||
                this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
 | 
			
		||||
                this.ui.credentials_file_content.show();
 | 
			
		||||
            } else {
 | 
			
		||||
                this.ui.dns_provider_credentials.prop('required', false);
 | 
			
		||||
                this.ui.credentials_file_content.hide();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    templateContext: {
 | 
			
		||||
        getLetsencryptEmail: function () {
 | 
			
		||||
            return App.Cache.User.get('email');
 | 
			
		||||
        },
 | 
			
		||||
        getDnsProvider: function () {
 | 
			
		||||
            return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
 | 
			
		||||
        },
 | 
			
		||||
        getDnsProviderCredentials: function () {
 | 
			
		||||
            return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
 | 
			
		||||
        },
 | 
			
		||||
        getPropagationSeconds: function () {
 | 
			
		||||
            return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
 | 
			
		||||
        },
 | 
			
		||||
        dns_plugins: dns_providers,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onRender: function () {
 | 
			
		||||
        let view = this;
 | 
			
		||||
 | 
			
		||||
        // Certificates
 | 
			
		||||
        this.ui.le_error_info.hide();
 | 
			
		||||
        this.ui.dns_challenge_content.hide();
 | 
			
		||||
        this.ui.credentials_file_content.hide();
 | 
			
		||||
        this.ui.letsencrypt.hide();
 | 
			
		||||
        this.ui.certificate_select.selectize({
 | 
			
		||||
            valueField:       'id',
 | 
			
		||||
            labelField:       'nice_name',
 | 
			
		||||
            searchField:      ['nice_name', 'domain_names'],
 | 
			
		||||
            create:           false,
 | 
			
		||||
            preload:          true,
 | 
			
		||||
            allowEmptyOption: true,
 | 
			
		||||
            render:           {
 | 
			
		||||
                option: function (item) {
 | 
			
		||||
                    item.i18n         = App.i18n;
 | 
			
		||||
                    item.formatDbDate = Helpers.formatDbDate;
 | 
			
		||||
                    return certListItemTemplate(item);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            load:             function (query, callback) {
 | 
			
		||||
                App.Api.Nginx.Certificates.getAll()
 | 
			
		||||
                    .then(rows => {
 | 
			
		||||
                        callback(rows);
 | 
			
		||||
                    })
 | 
			
		||||
                    .catch(err => {
 | 
			
		||||
                        console.error(err);
 | 
			
		||||
                        callback();
 | 
			
		||||
                    });
 | 
			
		||||
            },
 | 
			
		||||
            onLoad:           function () {
 | 
			
		||||
                view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id'));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    initialize: function (options) {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,7 @@
 | 
			
		||||
</td>
 | 
			
		||||
<td>
 | 
			
		||||
    <div>
 | 
			
		||||
        <% if (certificate) { %>
 | 
			
		||||
            <span class="tag"><%- i18n('streams', 'tcp+ssl') %></span>
 | 
			
		||||
        <% }
 | 
			
		||||
        else if (tcp_forwarding) { %>
 | 
			
		||||
        <% if (tcp_forwarding) { %>
 | 
			
		||||
            <span class="tag"><%- i18n('streams', 'tcp') %></span>
 | 
			
		||||
        <% }
 | 
			
		||||
        if (udp_forwarding) { %>
 | 
			
		||||
@@ -27,9 +24,6 @@
 | 
			
		||||
        <% } %>
 | 
			
		||||
    </div>
 | 
			
		||||
</td>
 | 
			
		||||
<td>
 | 
			
		||||
    <div><%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('all-hosts', 'none') %></div>
 | 
			
		||||
</td>
 | 
			
		||||
<td>
 | 
			
		||||
    <%
 | 
			
		||||
    var o = isOnline();
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
    <th><%- i18n('streams', 'incoming-port') %></th>
 | 
			
		||||
    <th><%- i18n('str', 'destination') %></th>
 | 
			
		||||
    <th><%- i18n('streams', 'protocol') %></th>
 | 
			
		||||
    <th><%- i18n('str', 'ssl') %></th>
 | 
			
		||||
    <th><%- i18n('str', 'status') %></th>
 | 
			
		||||
    <% if (canManage) { %>
 | 
			
		||||
    <th> </th>
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,7 @@ module.exports = Mn.View.extend({
 | 
			
		||||
    onRender: function () {
 | 
			
		||||
        let view = this;
 | 
			
		||||
 | 
			
		||||
        view.fetch(['owner', 'certificate'])
 | 
			
		||||
        view.fetch(['owner'])
 | 
			
		||||
            .then(response => {
 | 
			
		||||
                if (!view.isDestroyed()) {
 | 
			
		||||
                    if (response && response.length) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
<div class="modal-content">
 | 
			
		||||
    <form>
 | 
			
		||||
        <div class="modal-header">
 | 
			
		||||
            <h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
 | 
			
		||||
            <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="modal-body">
 | 
			
		||||
    <div class="modal-header">
 | 
			
		||||
        <h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
 | 
			
		||||
        <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="modal-body">
 | 
			
		||||
        <form>
 | 
			
		||||
            <div class="row">
 | 
			
		||||
                <div class="col-sm-6 col-md-6">
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
@@ -49,10 +49,10 @@
 | 
			
		||||
                </div>
 | 
			
		||||
                <% } %>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="modal-footer">
 | 
			
		||||
            <button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
 | 
			
		||||
            <button type="submit" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
        </form>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="modal-footer">
 | 
			
		||||
        <button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
 | 
			
		||||
        <button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ module.exports = Mn.View.extend({
 | 
			
		||||
 | 
			
		||||
    events: {
 | 
			
		||||
 | 
			
		||||
        'submit @ui.form': function (e) {
 | 
			
		||||
        'click @ui.save': function (e) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            this.ui.error.hide();
 | 
			
		||||
            let view = this;
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "footer": {
 | 
			
		||||
      "fork-me": "Fork me on Github",
 | 
			
		||||
      "copy": "© 2025 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
 | 
			
		||||
      "copy": "© 2024 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
 | 
			
		||||
      "theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
 | 
			
		||||
    },
 | 
			
		||||
    "dashboard": {
 | 
			
		||||
@@ -179,9 +179,7 @@
 | 
			
		||||
      "delete-confirm": "Are you sure you want to delete this Stream?",
 | 
			
		||||
      "help-title": "What is a Stream?",
 | 
			
		||||
      "help-content": "A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP traffic directly to another computer on the network.\nIf you're running game servers, FTP or SSH servers this can come in handy.",
 | 
			
		||||
      "search": "Search Incoming Port…",
 | 
			
		||||
      "ssl-certificate": "SSL Certificate for TCP Forwarding",
 | 
			
		||||
      "tcp+ssl": "TCP+SSL"
 | 
			
		||||
      "search": "Search Incoming Port…"
 | 
			
		||||
    },
 | 
			
		||||
    "certificates": {
 | 
			
		||||
      "title": "SSL Certificates",
 | 
			
		||||
@@ -208,10 +206,7 @@
 | 
			
		||||
      "reachability-other": "There is a server found at this domain but it returned an unexpected status code {code}. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
 | 
			
		||||
      "download": "Download",
 | 
			
		||||
      "renew-title": "Renew Let's Encrypt Certificate",
 | 
			
		||||
      "search": "Search Certificate…",
 | 
			
		||||
      "in-use"  : "In use",
 | 
			
		||||
      "inactive": "Inactive",
 | 
			
		||||
      "active-domain_names": "Active domain names"
 | 
			
		||||
      "search": "Search Certificate…"
 | 
			
		||||
    },
 | 
			
		||||
    "access-lists": {
 | 
			
		||||
      "title": "Access Lists",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,11 +15,8 @@ const model = Backbone.Model.extend({
 | 
			
		||||
            udp_forwarding:  false,
 | 
			
		||||
            enabled:         true,
 | 
			
		||||
            meta:            {},
 | 
			
		||||
            certificate_id:  0,
 | 
			
		||||
            domain_names:    [],
 | 
			
		||||
            // The following are expansions:
 | 
			
		||||
            owner:           null,
 | 
			
		||||
            certificate:     null
 | 
			
		||||
            owner:           null
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -167,5 +167,4 @@ $pink: #f66d9b;
 | 
			
		||||
 | 
			
		||||
textarea.form-control.text-monospace {
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    font-family: monospace;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -907,13 +907,6 @@ atob@^2.1.2:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
 | 
			
		||||
  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
 | 
			
		||||
 | 
			
		||||
available-typed-arrays@^1.0.7:
 | 
			
		||||
  version "1.0.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
 | 
			
		||||
  integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    possible-typed-array-names "^1.0.0"
 | 
			
		||||
 | 
			
		||||
babel-code-frame@^6.26.0:
 | 
			
		||||
  version "6.26.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
 | 
			
		||||
@@ -1797,32 +1790,6 @@ cacheable-request@^6.0.0:
 | 
			
		||||
    normalize-url "^4.1.0"
 | 
			
		||||
    responselike "^1.0.2"
 | 
			
		||||
 | 
			
		||||
call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
 | 
			
		||||
  integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
    function-bind "^1.1.2"
 | 
			
		||||
 | 
			
		||||
call-bind@^1.0.8:
 | 
			
		||||
  version "1.0.8"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
 | 
			
		||||
  integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    call-bind-apply-helpers "^1.0.0"
 | 
			
		||||
    es-define-property "^1.0.0"
 | 
			
		||||
    get-intrinsic "^1.2.4"
 | 
			
		||||
    set-function-length "^1.2.2"
 | 
			
		||||
 | 
			
		||||
call-bound@^1.0.3, call-bound@^1.0.4:
 | 
			
		||||
  version "1.0.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
 | 
			
		||||
  integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    call-bind-apply-helpers "^1.0.2"
 | 
			
		||||
    get-intrinsic "^1.3.0"
 | 
			
		||||
 | 
			
		||||
callsites@^3.0.0:
 | 
			
		||||
  version "3.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
 | 
			
		||||
@@ -2265,7 +2232,7 @@ create-ecdh@^4.0.0:
 | 
			
		||||
    bn.js "^4.1.0"
 | 
			
		||||
    elliptic "^6.5.3"
 | 
			
		||||
 | 
			
		||||
create-hash@^1.1.0, create-hash@^1.2.0:
 | 
			
		||||
create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
 | 
			
		||||
  version "1.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
 | 
			
		||||
  integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
 | 
			
		||||
@@ -2276,17 +2243,7 @@ create-hash@^1.1.0, create-hash@^1.2.0:
 | 
			
		||||
    ripemd160 "^2.0.1"
 | 
			
		||||
    sha.js "^2.4.0"
 | 
			
		||||
 | 
			
		||||
create-hash@~1.1.3:
 | 
			
		||||
  version "1.1.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
 | 
			
		||||
  integrity sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    cipher-base "^1.0.1"
 | 
			
		||||
    inherits "^2.0.1"
 | 
			
		||||
    ripemd160 "^2.0.0"
 | 
			
		||||
    sha.js "^2.4.0"
 | 
			
		||||
 | 
			
		||||
create-hmac@^1.1.0, create-hmac@^1.1.7:
 | 
			
		||||
create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
 | 
			
		||||
  version "1.1.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
 | 
			
		||||
  integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
 | 
			
		||||
@@ -2457,15 +2414,6 @@ defer-to-connect@^1.0.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
 | 
			
		||||
  integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
 | 
			
		||||
 | 
			
		||||
define-data-property@^1.1.4:
 | 
			
		||||
  version "1.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
 | 
			
		||||
  integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    es-define-property "^1.0.0"
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
    gopd "^1.0.1"
 | 
			
		||||
 | 
			
		||||
define-properties@^1.1.2, define-properties@^1.1.3:
 | 
			
		||||
  version "1.1.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
 | 
			
		||||
@@ -2631,15 +2579,6 @@ dot-prop@^5.2.0:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    is-obj "^2.0.0"
 | 
			
		||||
 | 
			
		||||
dunder-proto@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
 | 
			
		||||
  integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    call-bind-apply-helpers "^1.0.1"
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
    gopd "^1.2.0"
 | 
			
		||||
 | 
			
		||||
duplexer3@^0.1.4:
 | 
			
		||||
  version "0.1.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
 | 
			
		||||
@@ -2709,9 +2648,9 @@ electron-to-chromium@^1.3.47:
 | 
			
		||||
  integrity sha512-67V62Z4CFOiAtox+o+tosGfVk0QX4DJgH609tjT8QymbJZVAI/jWnAthnr8c5hnRNziIRwkc9EMQYejiVz3/9Q==
 | 
			
		||||
 | 
			
		||||
elliptic@^6.5.3, elliptic@^6.5.4:
 | 
			
		||||
  version "6.6.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06"
 | 
			
		||||
  integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==
 | 
			
		||||
  version "6.5.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b"
 | 
			
		||||
  integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    bn.js "^4.11.9"
 | 
			
		||||
    brorand "^1.1.0"
 | 
			
		||||
@@ -2823,23 +2762,6 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.5:
 | 
			
		||||
    string.prototype.trimend "^1.0.1"
 | 
			
		||||
    string.prototype.trimstart "^1.0.1"
 | 
			
		||||
 | 
			
		||||
es-define-property@^1.0.0, es-define-property@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
 | 
			
		||||
  integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
 | 
			
		||||
 | 
			
		||||
es-errors@^1.3.0:
 | 
			
		||||
  version "1.3.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
 | 
			
		||||
  integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
 | 
			
		||||
 | 
			
		||||
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
 | 
			
		||||
  version "1.1.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
 | 
			
		||||
  integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
 | 
			
		||||
es-to-primitive@^1.2.1:
 | 
			
		||||
  version "1.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
 | 
			
		||||
@@ -3209,13 +3131,6 @@ flush-write-stream@^1.0.0:
 | 
			
		||||
    inherits "^2.0.3"
 | 
			
		||||
    readable-stream "^2.3.6"
 | 
			
		||||
 | 
			
		||||
for-each@^0.3.5:
 | 
			
		||||
  version "0.3.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47"
 | 
			
		||||
  integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    is-callable "^1.2.7"
 | 
			
		||||
 | 
			
		||||
for-in@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
 | 
			
		||||
@@ -3276,11 +3191,6 @@ function-bind@^1.1.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
 | 
			
		||||
  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 | 
			
		||||
 | 
			
		||||
function-bind@^1.1.2:
 | 
			
		||||
  version "1.1.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
 | 
			
		||||
  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
 | 
			
		||||
 | 
			
		||||
functional-red-black-tree@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
 | 
			
		||||
@@ -3317,30 +3227,6 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
 | 
			
		||||
  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 | 
			
		||||
 | 
			
		||||
get-intrinsic@^1.2.4, get-intrinsic@^1.3.0:
 | 
			
		||||
  version "1.3.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
 | 
			
		||||
  integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    call-bind-apply-helpers "^1.0.2"
 | 
			
		||||
    es-define-property "^1.0.1"
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
    es-object-atoms "^1.1.1"
 | 
			
		||||
    function-bind "^1.1.2"
 | 
			
		||||
    get-proto "^1.0.1"
 | 
			
		||||
    gopd "^1.2.0"
 | 
			
		||||
    has-symbols "^1.1.0"
 | 
			
		||||
    hasown "^2.0.2"
 | 
			
		||||
    math-intrinsics "^1.1.0"
 | 
			
		||||
 | 
			
		||||
get-proto@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
 | 
			
		||||
  integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    dunder-proto "^1.0.1"
 | 
			
		||||
    es-object-atoms "^1.0.0"
 | 
			
		||||
 | 
			
		||||
get-stdin@^4.0.1:
 | 
			
		||||
  version "4.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
 | 
			
		||||
@@ -3507,11 +3393,6 @@ globule@^1.0.0:
 | 
			
		||||
    lodash "~4.17.10"
 | 
			
		||||
    minimatch "~3.0.2"
 | 
			
		||||
 | 
			
		||||
gopd@^1.0.1, gopd@^1.2.0:
 | 
			
		||||
  version "1.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
 | 
			
		||||
  integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
 | 
			
		||||
 | 
			
		||||
got@^9.6.0:
 | 
			
		||||
  version "9.6.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
 | 
			
		||||
@@ -3561,30 +3442,11 @@ has-flag@^4.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
 | 
			
		||||
  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 | 
			
		||||
 | 
			
		||||
has-property-descriptors@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
 | 
			
		||||
  integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    es-define-property "^1.0.0"
 | 
			
		||||
 | 
			
		||||
has-symbols@^1.0.0, has-symbols@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
 | 
			
		||||
  integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
 | 
			
		||||
 | 
			
		||||
has-symbols@^1.0.3, has-symbols@^1.1.0:
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
 | 
			
		||||
  integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
 | 
			
		||||
 | 
			
		||||
has-tostringtag@^1.0.2:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
 | 
			
		||||
  integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    has-symbols "^1.0.3"
 | 
			
		||||
 | 
			
		||||
has-unicode@^2.0.1:
 | 
			
		||||
  version "2.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
 | 
			
		||||
@@ -3633,13 +3495,6 @@ has@^1.0.3:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    function-bind "^1.1.1"
 | 
			
		||||
 | 
			
		||||
hash-base@^2.0.0:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
 | 
			
		||||
  integrity sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    inherits "^2.0.1"
 | 
			
		||||
 | 
			
		||||
hash-base@^3.0.0:
 | 
			
		||||
  version "3.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33"
 | 
			
		||||
@@ -3657,13 +3512,6 @@ hash.js@^1.0.0, hash.js@^1.0.3:
 | 
			
		||||
    inherits "^2.0.3"
 | 
			
		||||
    minimalistic-assert "^1.0.1"
 | 
			
		||||
 | 
			
		||||
hasown@^2.0.2:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
 | 
			
		||||
  integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    function-bind "^1.1.2"
 | 
			
		||||
 | 
			
		||||
he@1.2.x, he@^1.2.0:
 | 
			
		||||
  version "1.2.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
 | 
			
		||||
@@ -4008,11 +3856,6 @@ is-callable@^1.1.4, is-callable@^1.2.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
 | 
			
		||||
  integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==
 | 
			
		||||
 | 
			
		||||
is-callable@^1.2.7:
 | 
			
		||||
  version "1.2.7"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
 | 
			
		||||
  integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
 | 
			
		||||
 | 
			
		||||
is-ci@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
 | 
			
		||||
@@ -4176,13 +4019,6 @@ is-symbol@^1.0.2:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    has-symbols "^1.0.1"
 | 
			
		||||
 | 
			
		||||
is-typed-array@^1.1.14:
 | 
			
		||||
  version "1.1.15"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b"
 | 
			
		||||
  integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    which-typed-array "^1.1.16"
 | 
			
		||||
 | 
			
		||||
is-typedarray@^1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
 | 
			
		||||
@@ -4208,11 +4044,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
 | 
			
		||||
  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
 | 
			
		||||
 | 
			
		||||
isarray@^2.0.5:
 | 
			
		||||
  version "2.0.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
 | 
			
		||||
  integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
 | 
			
		||||
 | 
			
		||||
isexe@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
 | 
			
		||||
@@ -4605,11 +4436,6 @@ marionette.templatecache@^1.0.0:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    backbone.marionette "^4.0.0, 4.0.0-beta.1"
 | 
			
		||||
 | 
			
		||||
math-intrinsics@^1.1.0:
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
 | 
			
		||||
  integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
 | 
			
		||||
 | 
			
		||||
md5.js@^1.3.4:
 | 
			
		||||
  version "1.3.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
 | 
			
		||||
@@ -5463,16 +5289,15 @@ path-type@^4.0.0:
 | 
			
		||||
  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
 | 
			
		||||
 | 
			
		||||
pbkdf2@^3.0.3:
 | 
			
		||||
  version "3.1.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.3.tgz#8be674d591d65658113424592a95d1517318dd4b"
 | 
			
		||||
  integrity sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==
 | 
			
		||||
  version "3.1.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94"
 | 
			
		||||
  integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    create-hash "~1.1.3"
 | 
			
		||||
    create-hmac "^1.1.7"
 | 
			
		||||
    ripemd160 "=2.0.1"
 | 
			
		||||
    safe-buffer "^5.2.1"
 | 
			
		||||
    sha.js "^2.4.11"
 | 
			
		||||
    to-buffer "^1.2.0"
 | 
			
		||||
    create-hash "^1.1.2"
 | 
			
		||||
    create-hmac "^1.1.4"
 | 
			
		||||
    ripemd160 "^2.0.1"
 | 
			
		||||
    safe-buffer "^5.0.1"
 | 
			
		||||
    sha.js "^2.4.8"
 | 
			
		||||
 | 
			
		||||
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
 | 
			
		||||
  version "2.2.2"
 | 
			
		||||
@@ -5501,11 +5326,6 @@ posix-character-classes@^0.1.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
 | 
			
		||||
  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 | 
			
		||||
 | 
			
		||||
possible-typed-array-names@^1.0.0:
 | 
			
		||||
  version "1.1.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae"
 | 
			
		||||
  integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==
 | 
			
		||||
 | 
			
		||||
postcss-modules-extract-imports@^2.0.0:
 | 
			
		||||
  version "2.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e"
 | 
			
		||||
@@ -6066,14 +5886,6 @@ rimraf@^3.0.2:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    glob "^7.1.3"
 | 
			
		||||
 | 
			
		||||
ripemd160@=2.0.1:
 | 
			
		||||
  version "2.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
 | 
			
		||||
  integrity sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    hash-base "^2.0.0"
 | 
			
		||||
    inherits "^2.0.1"
 | 
			
		||||
 | 
			
		||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
 | 
			
		||||
@@ -6225,18 +6037,6 @@ set-blocking@^2.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
 | 
			
		||||
  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
 | 
			
		||||
 | 
			
		||||
set-function-length@^1.2.2:
 | 
			
		||||
  version "1.2.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
 | 
			
		||||
  integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    define-data-property "^1.1.4"
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
    function-bind "^1.1.2"
 | 
			
		||||
    get-intrinsic "^1.2.4"
 | 
			
		||||
    gopd "^1.0.1"
 | 
			
		||||
    has-property-descriptors "^1.0.2"
 | 
			
		||||
 | 
			
		||||
set-value@^2.0.0, set-value@^2.0.1:
 | 
			
		||||
  version "2.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
 | 
			
		||||
@@ -6252,7 +6052,7 @@ setimmediate@^1.0.4:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
 | 
			
		||||
  integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
 | 
			
		||||
 | 
			
		||||
sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8:
 | 
			
		||||
sha.js@^2.4.0, sha.js@^2.4.8:
 | 
			
		||||
  version "2.4.11"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
 | 
			
		||||
  integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
 | 
			
		||||
@@ -6792,15 +6592,6 @@ to-arraybuffer@^1.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
 | 
			
		||||
  integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
 | 
			
		||||
 | 
			
		||||
to-buffer@^1.2.0:
 | 
			
		||||
  version "1.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.2.1.tgz#2ce650cdb262e9112a18e65dc29dcb513c8155e0"
 | 
			
		||||
  integrity sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    isarray "^2.0.5"
 | 
			
		||||
    safe-buffer "^5.2.1"
 | 
			
		||||
    typed-array-buffer "^1.0.3"
 | 
			
		||||
 | 
			
		||||
to-fast-properties@^1.0.3:
 | 
			
		||||
  version "1.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
 | 
			
		||||
@@ -6907,15 +6698,6 @@ type-fest@^0.8.1:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
 | 
			
		||||
  integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
 | 
			
		||||
 | 
			
		||||
typed-array-buffer@^1.0.3:
 | 
			
		||||
  version "1.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536"
 | 
			
		||||
  integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    call-bound "^1.0.3"
 | 
			
		||||
    es-errors "^1.3.0"
 | 
			
		||||
    is-typed-array "^1.1.14"
 | 
			
		||||
 | 
			
		||||
typedarray-to-buffer@^3.1.5:
 | 
			
		||||
  version "3.1.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
 | 
			
		||||
@@ -7242,19 +7024,6 @@ which-module@^2.0.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
 | 
			
		||||
  integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
 | 
			
		||||
 | 
			
		||||
which-typed-array@^1.1.16:
 | 
			
		||||
  version "1.1.19"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956"
 | 
			
		||||
  integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    available-typed-arrays "^1.0.7"
 | 
			
		||||
    call-bind "^1.0.8"
 | 
			
		||||
    call-bound "^1.0.4"
 | 
			
		||||
    for-each "^0.3.5"
 | 
			
		||||
    get-proto "^1.0.1"
 | 
			
		||||
    gopd "^1.2.0"
 | 
			
		||||
    has-tostringtag "^1.0.2"
 | 
			
		||||
 | 
			
		||||
which@^1.2.14, which@^1.2.9, which@^1.3.1:
 | 
			
		||||
  version "1.3.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,18 +7,18 @@
 | 
			
		||||
		"credentials": "dns_acmedns_api_url = http://acmedns-server/\ndns_acmedns_registration_file = /data/acme-registration.json",
 | 
			
		||||
		"full_plugin_name": "dns-acmedns"
 | 
			
		||||
	},
 | 
			
		||||
	"active24": {
 | 
			
		||||
	"active24":{
 | 
			
		||||
		"name": "Active24",
 | 
			
		||||
		"package_name": "certbot-dns-active24",
 | 
			
		||||
		"version": "~=2.0.0",
 | 
			
		||||
		"version": "~=1.5.1",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_active24_api_key = <identifier>\ndns_active24_secret = <secret>",
 | 
			
		||||
		"credentials": "dns_active24_token=\"TOKEN\"",
 | 
			
		||||
		"full_plugin_name": "dns-active24"
 | 
			
		||||
	},
 | 
			
		||||
	"aliyun": {
 | 
			
		||||
		"name": "Aliyun",
 | 
			
		||||
		"package_name": "certbot-dns-aliyun",
 | 
			
		||||
		"version": "~=2.0.0",
 | 
			
		||||
		"version": "~=0.38.1",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_aliyun_access_key = 12345678\ndns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef",
 | 
			
		||||
		"full_plugin_name": "dns-aliyun"
 | 
			
		||||
@@ -31,22 +31,6 @@
 | 
			
		||||
		"credentials": "# This plugin supported API authentication using either Service Principals or utilizing a Managed Identity assigned to the virtual machine.\n# Regardless which authentication method used, the identity will need the “DNS Zone Contributor” role assigned to it.\n# As multiple Azure DNS Zones in multiple resource groups can exist, the config file needs a mapping of zone to resource group ID. Multiple zones -> ID mappings can be listed by using the key dns_azure_zoneX where X is a unique number. At least 1 zone mapping is required.\n\n# Using a service principal (option 1)\ndns_azure_sp_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\ndns_azure_sp_client_secret = E-xqXU83Y-jzTI6xe9fs2YC~mck3ZzUih9\ndns_azure_tenant_id = ed1090f3-ab18-4b12-816c-599af8a88cf7\n\n# Using used assigned MSI (option 2)\n# dns_azure_msi_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\n\n# Using system assigned MSI (option 3)\n# dns_azure_msi_system_assigned = true\n\n# Zones (at least one always required)\ndns_azure_zone1 = example.com:/subscriptions/c135abce-d87d-48df-936c-15596c6968a5/resourceGroups/dns1\ndns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf2744622/resourceGroups/dns2",
 | 
			
		||||
		"full_plugin_name": "dns-azure"
 | 
			
		||||
	},
 | 
			
		||||
	"baidu": {
 | 
			
		||||
		"name": "baidu",
 | 
			
		||||
		"package_name": "certbot-dns-baidu",
 | 
			
		||||
		"version": "~=0.1.1",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_baidu_access_key = 12345678\ndns_baidu_secret_key = 1234567890abcdef1234567890abcdef",
 | 
			
		||||
		"full_plugin_name": "dns-baidu"
 | 
			
		||||
	},
 | 
			
		||||
	"beget": {
 | 
			
		||||
		"name":"Beget",
 | 
			
		||||
		"package_name": "certbot-beget-plugin",
 | 
			
		||||
		"version": "~=1.0.0.dev9",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "# Beget API credentials used by Certbot\nbeget_plugin_username = username\nbeget_plugin_password = password",
 | 
			
		||||
		"full_plugin_name": "beget-plugin"
 | 
			
		||||
	},
 | 
			
		||||
	"bunny": {
 | 
			
		||||
		"name": "bunny.net",
 | 
			
		||||
		"package_name": "certbot-dns-bunny",
 | 
			
		||||
@@ -55,20 +39,12 @@
 | 
			
		||||
		"credentials": "# Bunny API token used by Certbot (see https://dash.bunny.net/account/settings)\ndns_bunny_api_key = xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
 | 
			
		||||
		"full_plugin_name": "dns-bunny"
 | 
			
		||||
	},
 | 
			
		||||
	"cdmon": {
 | 
			
		||||
                "name": "cdmon",
 | 
			
		||||
                "package_name": "certbot-dns-cdmon",
 | 
			
		||||
                "version": "~=0.4.1",
 | 
			
		||||
                "dependencies": "",
 | 
			
		||||
                "credentials": "dns_cdmon_api_key=your-cdmon-api-token\ndns_cdmon_domain=your_domain_is_optional",
 | 
			
		||||
                "full_plugin_name": "dns-cdmon"
 | 
			
		||||
        },
 | 
			
		||||
	"cloudflare": {
 | 
			
		||||
		"name": "Cloudflare",
 | 
			
		||||
		"package_name": "certbot-dns-cloudflare",
 | 
			
		||||
		"version": "=={{certbot-version}}",
 | 
			
		||||
		"dependencies": "cloudflare==4.0.* acme=={{certbot-version}}",
 | 
			
		||||
		"credentials": "# Cloudflare API credentials used by Certbot\ndns_cloudflare_email = cloudflare@example.com\ndns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234",
 | 
			
		||||
		"dependencies": "cloudflare==2.19.* acme=={{certbot-version}}",
 | 
			
		||||
		"credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
 | 
			
		||||
		"full_plugin_name": "dns-cloudflare"
 | 
			
		||||
	},
 | 
			
		||||
	"cloudns": {
 | 
			
		||||
@@ -106,19 +82,11 @@
 | 
			
		||||
	"cpanel": {
 | 
			
		||||
		"name": "cPanel",
 | 
			
		||||
		"package_name": "certbot-dns-cpanel",
 | 
			
		||||
		"version": "~=0.4.0",
 | 
			
		||||
		"version": "~=0.2.2",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = your_username\ncpanel_password = your_password\ncpanel_token = your_api_token",
 | 
			
		||||
		"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = user\ncpanel_password = hunter2",
 | 
			
		||||
		"full_plugin_name": "cpanel"
 | 
			
		||||
	},
 | 
			
		||||
	"ddnss": {
 | 
			
		||||
		"name": "DDNSS",
 | 
			
		||||
		"package_name": "certbot-dns-ddnss",
 | 
			
		||||
		"version": "~=1.1.0",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_ddnss_token = YOUR_DDNSS_API_TOKEN",
 | 
			
		||||
		"full_plugin_name": "dns-ddnss"
 | 
			
		||||
	},
 | 
			
		||||
	"desec": {
 | 
			
		||||
		"name": "deSEC",
 | 
			
		||||
		"package_name": "certbot-dns-desec",
 | 
			
		||||
@@ -185,11 +153,11 @@
 | 
			
		||||
	},
 | 
			
		||||
	"domainoffensive": {
 | 
			
		||||
		"name": "DomainOffensive (do.de)",
 | 
			
		||||
		"package_name": "certbot-dns-domainoffensive",
 | 
			
		||||
		"version": "~=2.0.0",
 | 
			
		||||
		"package_name": "certbot-dns-do",
 | 
			
		||||
		"version": "~=0.31.0",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_domainoffensive_api_token = YOUR_DO_DE_AUTH_TOKEN",
 | 
			
		||||
		"full_plugin_name": "dns-domainoffensive"
 | 
			
		||||
		"credentials": "dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN",
 | 
			
		||||
		"full_plugin_name": "dns-do"
 | 
			
		||||
	},
 | 
			
		||||
	"domeneshop": {
 | 
			
		||||
		"name": "Domeneshop",
 | 
			
		||||
@@ -223,14 +191,6 @@
 | 
			
		||||
		"credentials": "dns_eurodns_applicationId = myuser\ndns_eurodns_apiKey = mysecretpassword\ndns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy",
 | 
			
		||||
		"full_plugin_name": "dns-eurodns"
 | 
			
		||||
	},
 | 
			
		||||
	"firstdomains": {
 | 
			
		||||
                "name": "First Domains",
 | 
			
		||||
                "package_name": "certbot-dns-firstdomains",
 | 
			
		||||
                "version": ">=1.0",
 | 
			
		||||
                "dependencies": "",
 | 
			
		||||
                "credentials": "dns_firstdomains_username = myremoteuser\ndns_firstdomains_password = verysecureremoteuserpassword",
 | 
			
		||||
                "full_plugin_name": "dns-firstdomains"
 | 
			
		||||
        },
 | 
			
		||||
	"freedns": {
 | 
			
		||||
		"name": "FreeDNS",
 | 
			
		||||
		"package_name": "certbot-dns-freedns",
 | 
			
		||||
@@ -241,20 +201,12 @@
 | 
			
		||||
	},
 | 
			
		||||
	"gandi": {
 | 
			
		||||
		"name": "Gandi Live DNS",
 | 
			
		||||
		"package_name": "certbot-dns-gandi",
 | 
			
		||||
		"version": "~=1.6.1",
 | 
			
		||||
		"package_name": "certbot_plugin_gandi",
 | 
			
		||||
		"version": "~=1.5.0",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "# Gandi personal access token\ndns_gandi_token=PERSONAL_ACCESS_TOKEN",
 | 
			
		||||
		"full_plugin_name": "dns-gandi"
 | 
			
		||||
	},
 | 
			
		||||
	"gcore": {
 | 
			
		||||
		"name": "Gcore DNS",
 | 
			
		||||
		"package_name": "certbot-dns-gcore",
 | 
			
		||||
		"version": "~=0.1.8",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_gcore_apitoken = 0123456789abcdef0123456789abcdef01234567",
 | 
			
		||||
		"full_plugin_name": "dns-gcore"
 | 
			
		||||
	},
 | 
			
		||||
	"godaddy": {
 | 
			
		||||
		"name": "GoDaddy",
 | 
			
		||||
		"package_name": "certbot-dns-godaddy",
 | 
			
		||||
@@ -295,14 +247,6 @@
 | 
			
		||||
		"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
 | 
			
		||||
		"full_plugin_name": "dns-hetzner"
 | 
			
		||||
	},
 | 
			
		||||
	"hostingnl": {
 | 
			
		||||
		"name": "Hosting.nl",
 | 
			
		||||
		"package_name": "certbot-dns-hostingnl",
 | 
			
		||||
		"version": "~=0.1.5",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_hostingnl_api_key = 0123456789abcdef0123456789abcdef",
 | 
			
		||||
		"full_plugin_name": "dns-hostingnl"
 | 
			
		||||
	},
 | 
			
		||||
	"hover": {
 | 
			
		||||
		"name": "Hover",
 | 
			
		||||
		"package_name": "certbot-dns-hover",
 | 
			
		||||
@@ -396,7 +340,7 @@
 | 
			
		||||
		"package_name": "certbot-dns-mijn-host",
 | 
			
		||||
		"version": "~=0.0.4",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_mijn_host_api_key=0123456789abcdef0123456789abcdef",
 | 
			
		||||
		"credentials": "dns-mijn-host-credentials = /etc/letsencrypt/mijnhost-credentials.ini",
 | 
			
		||||
		"full_plugin_name": "dns-mijn-host"
 | 
			
		||||
	},
 | 
			
		||||
	"namecheap": {
 | 
			
		||||
@@ -415,14 +359,6 @@
 | 
			
		||||
		"credentials": "dns_netcup_customer_id  = 123456\ndns_netcup_api_key      = 0123456789abcdef0123456789abcdef01234567\ndns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123",
 | 
			
		||||
		"full_plugin_name": "dns-netcup"
 | 
			
		||||
	},
 | 
			
		||||
	"nicru": {
 | 
			
		||||
		"name": "nic.ru",
 | 
			
		||||
		"package_name": "certbot-dns-nicru",
 | 
			
		||||
		"version": "~=1.0.3",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_nicru_client_id = application-id\ndns_nicru_client_secret = application-token\ndns_nicru_username = 0001110/NIC-D\ndns_nicru_password = password\ndns_nicru_scope = .+:.+/zones/example.com(/.+)?\ndns_nicru_service = DNS_SERVICE_NAME\ndns_nicru_zone = example.com",
 | 
			
		||||
		"full_plugin_name": "dns-nicru"
 | 
			
		||||
	},
 | 
			
		||||
	"njalla": {
 | 
			
		||||
		"name": "Njalla",
 | 
			
		||||
		"package_name": "certbot-dns-njalla",
 | 
			
		||||
@@ -466,7 +402,7 @@
 | 
			
		||||
	"porkbun": {
 | 
			
		||||
		"name": "Porkbun",
 | 
			
		||||
		"package_name": "certbot-dns-porkbun",
 | 
			
		||||
		"version": "~=0.9",
 | 
			
		||||
		"version": "~=0.2",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_porkbun_key=your-porkbun-api-key\ndns_porkbun_secret=your-porkbun-api-secret",
 | 
			
		||||
		"full_plugin_name": "dns-porkbun"
 | 
			
		||||
@@ -511,30 +447,14 @@
 | 
			
		||||
		"credentials": "[default]\naws_access_key_id=AKIAIOSFODNN7EXAMPLE\naws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
 | 
			
		||||
		"full_plugin_name": "dns-route53"
 | 
			
		||||
	},
 | 
			
		||||
	"spaceship": {
 | 
			
		||||
		"name": "Spaceship",
 | 
			
		||||
		"package_name": "certbot-dns-spaceship",
 | 
			
		||||
		"version": "~=1.0.4",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "[spaceship]\napi_key=your_api_key\napi_secret=your_api_secret",
 | 
			
		||||
		"full_plugin_name": "dns-spaceship"
 | 
			
		||||
	},
 | 
			
		||||
	"strato": {
 | 
			
		||||
		"name": "Strato",
 | 
			
		||||
		"package_name": "certbot-dns-strato",
 | 
			
		||||
		"version": "~=0.2.2",
 | 
			
		||||
		"version": "~=0.2.1",
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "dns_strato_username = user\ndns_strato_password = pass\n# uncomment if youre using two factor authentication:\n# dns_strato_totp_devicename = 2fa_device\n# dns_strato_totp_secret = 2fa_secret\n#\n# uncomment if domain name contains special characters\n# insert domain display name as seen on your account page here\n# dns_strato_domain_display_name = my-punicode-url.de\n#\n# if youre not using strato.de or another special endpoint you can customise it below\n# you will probably only need to adjust the host, but you can also change the complete endpoint url\n# dns_strato_custom_api_scheme = https\n# dns_strato_custom_api_host = www.strato.de\n# dns_strato_custom_api_port = 443\n# dns_strato_custom_api_path = \"/apps/CustomerService\"",
 | 
			
		||||
		"full_plugin_name": "dns-strato"
 | 
			
		||||
	},
 | 
			
		||||
	        "selectelv2": {
 | 
			
		||||
                "name": "Selectel api v2",
 | 
			
		||||
                "package_name": "certbot-dns-selectel-api-v2",
 | 
			
		||||
                "version": "~=0.3.0",
 | 
			
		||||
                "dependencies": "",
 | 
			
		||||
                "credentials": "dns_selectel_api_v2_account_id = your_account_id\ndns_selectel_api_v2_project_name = your_project\ndns_selectel_api_v2_username = your_username\ndns_selectel_api_v2_password = your_password",
 | 
			
		||||
                "full_plugin_name": "dns-selectel-api-v2"
 | 
			
		||||
        },
 | 
			
		||||
	"timeweb": {
 | 
			
		||||
		"name": "Timeweb Cloud",
 | 
			
		||||
		"package_name": "certbot-dns-timeweb",
 | 
			
		||||
@@ -575,7 +495,7 @@
 | 
			
		||||
		"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>",
 | 
			
		||||
		"full_plugin_name": "dns-websupport"
 | 
			
		||||
	},
 | 
			
		||||
	"wedos": {
 | 
			
		||||
	"wedos":{
 | 
			
		||||
		"name": "Wedos",
 | 
			
		||||
		"package_name": "certbot-dns-wedos",
 | 
			
		||||
		"version": "~=2.2",
 | 
			
		||||
@@ -590,13 +510,5 @@
 | 
			
		||||
		"dependencies": "",
 | 
			
		||||
		"credentials": "edgedns_client_secret = as3d1asd5d1a32sdfsdfs2d1asd5=\nedgedns_host = sdflskjdf-dfsdfsdf-sdfsdfsdf.luna.akamaiapis.net\nedgedns_access_token = kjdsi3-34rfsdfsdf-234234fsdfsdf\nedgedns_client_token = dkfjdf-342fsdfsd-23fsdfsdfsdf",
 | 
			
		||||
		"full_plugin_name": "edgedns"
 | 
			
		||||
	},
 | 
			
		||||
	"zoneedit": {
 | 
			
		||||
		"name": "ZoneEdit",
 | 
			
		||||
		"package_name": "certbot-dns-zoneedit",
 | 
			
		||||
		"version": "~=0.3.2",
 | 
			
		||||
		"dependencies": "--no-deps dnspython",
 | 
			
		||||
		"credentials": "dns_zoneedit_user = <login-user-id>\ndns_zoneedit_token = <dyn-authentication-token>",
 | 
			
		||||
		"full_plugin_name": "dns-zoneedit"
 | 
			
		||||
 	}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ YELLOW='\E[1;33m'
 | 
			
		||||
export BLUE CYAN GREEN RED RESET YELLOW
 | 
			
		||||
 | 
			
		||||
# Docker Compose
 | 
			
		||||
COMPOSE_PROJECT_NAME="npm2dev"
 | 
			
		||||
COMPOSE_PROJECT_NAME="npmdev"
 | 
			
		||||
COMPOSE_FILE="docker/docker-compose.dev.yml"
 | 
			
		||||
 | 
			
		||||
export COMPOSE_FILE COMPOSE_PROJECT_NAME
 | 
			
		||||
 
 | 
			
		||||
@@ -67,8 +67,6 @@ printf "nameserver %s\noptions ndots:0" "${DNSROUTER_IP}" > "${LOCAL_RESOLVE}"
 | 
			
		||||
# bring up all remaining containers, except cypress!
 | 
			
		||||
docker-compose up -d --remove-orphans stepca squid
 | 
			
		||||
docker-compose pull db-mysql || true # ok to fail
 | 
			
		||||
docker-compose pull db-postgres || true # ok to fail
 | 
			
		||||
docker-compose pull authentik authentik-redis authentik-ldap || true # ok to fail
 | 
			
		||||
docker-compose up -d --remove-orphans --pull=never fullstack
 | 
			
		||||
 | 
			
		||||
# wait for main container to be healthy
 | 
			
		||||
 
 | 
			
		||||
@@ -36,11 +36,12 @@ if hash docker-compose 2>/dev/null; then
 | 
			
		||||
 | 
			
		||||
	# bring up all remaining containers, except cypress!
 | 
			
		||||
	docker-compose up -d --remove-orphans stepca squid
 | 
			
		||||
	docker-compose pull db db-postgres authentik-redis authentik authentik-worker authentik-ldap
 | 
			
		||||
	docker-compose build --pull --parallel fullstack
 | 
			
		||||
	docker-compose up -d --remove-orphans fullstack
 | 
			
		||||
	docker-compose pull db
 | 
			
		||||
	docker-compose up -d --remove-orphans --pull=never fullstack
 | 
			
		||||
	docker-compose up -d --remove-orphans swagger
 | 
			
		||||
 | 
			
		||||
	# docker-compose up -d --remove-orphans --force-recreate --build
 | 
			
		||||
 | 
			
		||||
	# wait for main container to be healthy
 | 
			
		||||
	bash "$DIR/wait-healthy" "$(docker-compose ps --all -q fullstack)" 120
 | 
			
		||||
 | 
			
		||||
@@ -52,10 +53,10 @@ if hash docker-compose 2>/dev/null; then
 | 
			
		||||
 | 
			
		||||
	if [ "$1" == "-f" ]; then
 | 
			
		||||
		echo -e "${BLUE}❯ ${YELLOW}Following Backend Container:${RESET}"
 | 
			
		||||
		docker logs -f npm2dev.core
 | 
			
		||||
		docker logs -f npm_core
 | 
			
		||||
	else
 | 
			
		||||
		echo -e "${YELLOW}Hint:${RESET} You can follow the output of some of the containers with:"
 | 
			
		||||
		echo "  docker logs -f npm2dev.core"
 | 
			
		||||
		echo "  docker logs -f npm_core"
 | 
			
		||||
	fi
 | 
			
		||||
else
 | 
			
		||||
	echo -e "${RED}❯ docker-compose command is not available${RESET}"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,11 @@
 | 
			
		||||
FROM cypress/included:14.0.1
 | 
			
		||||
FROM cypress/included:13.9.0
 | 
			
		||||
 | 
			
		||||
COPY --chown=1000 ./test /test
 | 
			
		||||
 | 
			
		||||
# Disable Cypress CLI colors
 | 
			
		||||
ENV FORCE_COLOR=0
 | 
			
		||||
ENV NO_COLOR=1
 | 
			
		||||
 | 
			
		||||
# testssl.sh and mkcert
 | 
			
		||||
RUN wget "https://github.com/testssl/testssl.sh/archive/refs/tags/v3.2rc4.tar.gz" -O /tmp/testssl.tgz -q \
 | 
			
		||||
	&& tar -xzf /tmp/testssl.tgz -C /tmp \
 | 
			
		||||
	&& mv /tmp/testssl.sh-3.2rc4 /testssl \
 | 
			
		||||
	&& rm /tmp/testssl.tgz \
 | 
			
		||||
	&& apt-get update \
 | 
			
		||||
	&& apt-get install -y bsdmainutils curl dnsutils \
 | 
			
		||||
	&& apt-get clean \
 | 
			
		||||
	&& rm -rf /var/lib/apt/lists/* \
 | 
			
		||||
	&& wget "https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64" -O /bin/mkcert \
 | 
			
		||||
	&& chmod +x /bin/mkcert
 | 
			
		||||
 | 
			
		||||
COPY --chown=1000 ./test /test
 | 
			
		||||
WORKDIR /test
 | 
			
		||||
RUN yarn install && yarn cache clean
 | 
			
		||||
ENTRYPOINT []
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
/// <reference types="cypress" />
 | 
			
		||||
 | 
			
		||||
describe('Dashboard endpoints', () => {
 | 
			
		||||
	let token;
 | 
			
		||||
 | 
			
		||||
	before(() => {
 | 
			
		||||
		cy.getToken().then((tok) => {
 | 
			
		||||
			token = tok;
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	it('Should be able to get host counts', function() {
 | 
			
		||||
		cy.task('backendApiGet', {
 | 
			
		||||
			token: token,
 | 
			
		||||
			path:  '/api/reports/hosts'
 | 
			
		||||
		}).then((data) => {
 | 
			
		||||
			cy.validateSwaggerSchema('get', 200, '/reports/hosts', data);
 | 
			
		||||
			expect(data).to.have.property('dead');
 | 
			
		||||
			expect(data).to.have.property('proxy');
 | 
			
		||||
			expect(data).to.have.property('redirection');
 | 
			
		||||
			expect(data).to.have.property('stream');
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
/// <reference types="cypress" />
 | 
			
		||||
 | 
			
		||||
describe('LDAP with Authentik', () => {
 | 
			
		||||
	let token;
 | 
			
		||||
	if (Cypress.env('skipStackCheck') === 'true' || Cypress.env('stack') === 'postgres') {
 | 
			
		||||
 | 
			
		||||
		before(() => {
 | 
			
		||||
			cy.getToken().then((tok) => {
 | 
			
		||||
				token = tok;
 | 
			
		||||
 | 
			
		||||
				// cy.task('backendApiPut', {
 | 
			
		||||
				// 	token: token,
 | 
			
		||||
				// 	path:  '/api/settings/ldap-auth',
 | 
			
		||||
				// 	data:  {
 | 
			
		||||
				// 		value: {
 | 
			
		||||
				// 			host: 'authentik-ldap:3389',
 | 
			
		||||
				// 			base_dn: 'ou=users,DC=ldap,DC=goauthentik,DC=io',
 | 
			
		||||
				// 			user_dn: 'cn={{USERNAME}},ou=users,DC=ldap,DC=goauthentik,DC=io',
 | 
			
		||||
				// 			email_property: 'mail',
 | 
			
		||||
				// 			name_property: 'sn',
 | 
			
		||||
				// 			self_filter: '(&(cn={{USERNAME}})(ak-active=TRUE))',
 | 
			
		||||
				// 			auto_create_user: true
 | 
			
		||||
				// 		}
 | 
			
		||||
				// 	}
 | 
			
		||||
				// }).then((data) => {
 | 
			
		||||
				// 	cy.validateSwaggerSchema('put', 200, '/settings/{name}', data);
 | 
			
		||||
				// 	expect(data.result).to.have.property('id');
 | 
			
		||||
				// 	expect(data.result.id).to.be.greaterThan(0);
 | 
			
		||||
				// });
 | 
			
		||||
 | 
			
		||||
				// cy.task('backendApiPut', {
 | 
			
		||||
				// 	token: token,
 | 
			
		||||
				// 	path:  '/api/settings/auth-methods',
 | 
			
		||||
				// 	data:  {
 | 
			
		||||
				// 		value: [
 | 
			
		||||
				// 			'local',
 | 
			
		||||
				// 			'ldap'
 | 
			
		||||
				// 		]
 | 
			
		||||
				// 	}
 | 
			
		||||
				// }).then((data) => {
 | 
			
		||||
				// 	cy.validateSwaggerSchema('put', 200, '/settings/{name}', data);
 | 
			
		||||
				// 	expect(data.result).to.have.property('id');
 | 
			
		||||
				// 	expect(data.result.id).to.be.greaterThan(0);
 | 
			
		||||
				// });
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it.skip('Should log in with LDAP', function() {
 | 
			
		||||
			// cy.task('backendApiPost', {
 | 
			
		||||
			// 	token: token,
 | 
			
		||||
			// 	path:  '/api/auth',
 | 
			
		||||
			// 	data:  {
 | 
			
		||||
			// 		// Authentik LDAP creds:
 | 
			
		||||
			// 		type: 'ldap',
 | 
			
		||||
			// 		identity: 'cypress',
 | 
			
		||||
			// 		secret: 'fqXBfUYqHvYqiwBHWW7f'
 | 
			
		||||
			// 	}
 | 
			
		||||
			// }).then((data) => {
 | 
			
		||||
			// 	cy.validateSwaggerSchema('post', 200, '/auth', data);
 | 
			
		||||
			// 	expect(data.result).to.have.property('token');
 | 
			
		||||
			// });
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
@@ -1,97 +0,0 @@
 | 
			
		||||
/// <reference types="cypress" />
 | 
			
		||||
 | 
			
		||||
describe('OAuth with Authentik', () => {
 | 
			
		||||
	let token;
 | 
			
		||||
	if (Cypress.env('skipStackCheck') === 'true' || Cypress.env('stack') === 'postgres') {
 | 
			
		||||
 | 
			
		||||
		before(() => {
 | 
			
		||||
			cy.getToken().then((tok) => {
 | 
			
		||||
				token = tok;
 | 
			
		||||
 | 
			
		||||
				// cy.task('backendApiPut', {
 | 
			
		||||
				// 	token: token,
 | 
			
		||||
				// 	path:  '/api/settings/oauth-auth',
 | 
			
		||||
				// 	data:  {
 | 
			
		||||
				// 		value: {
 | 
			
		||||
				// 			client_id: '7iO2AvuUp9JxiSVkCcjiIbQn4mHmUMBj7yU8EjqU',
 | 
			
		||||
				// 			client_secret: 'VUMZzaGTrmXJ8PLksyqzyZ6lrtz04VvejFhPMBP9hGZNCMrn2LLBanySs4ta7XGrDr05xexPyZT1XThaf4ubg00WqvHRVvlu4Naa1aMootNmSRx3VAk6RSslUJmGyHzq',
 | 
			
		||||
				// 			authorization_url: 'http://authentik:9000/application/o/authorize/',
 | 
			
		||||
				// 			resource_url: 'http://authentik:9000/application/o/userinfo/',
 | 
			
		||||
				// 			token_url: 'http://authentik:9000/application/o/token/',
 | 
			
		||||
				// 			logout_url: 'http://authentik:9000/application/o/npm/end-session/',
 | 
			
		||||
				// 			identifier: 'preferred_username',
 | 
			
		||||
				// 			scopes: [],
 | 
			
		||||
				// 			auto_create_user: true
 | 
			
		||||
				// 		}
 | 
			
		||||
				// 	}
 | 
			
		||||
				// }).then((data) => {
 | 
			
		||||
				// 	cy.validateSwaggerSchema('put', 200, '/settings/{name}', data);
 | 
			
		||||
				// 	expect(data.result).to.have.property('id');
 | 
			
		||||
				// 	expect(data.result.id).to.be.greaterThan(0);
 | 
			
		||||
				// });
 | 
			
		||||
 | 
			
		||||
				// cy.task('backendApiPut', {
 | 
			
		||||
				// 	token: token,
 | 
			
		||||
				// 	path:  '/api/settings/auth-methods',
 | 
			
		||||
				// 	data:  {
 | 
			
		||||
				// 		value: [
 | 
			
		||||
				// 			'local',
 | 
			
		||||
				// 			'oauth'
 | 
			
		||||
				// 		]
 | 
			
		||||
				// 	}
 | 
			
		||||
				// }).then((data) => {
 | 
			
		||||
				// 	cy.validateSwaggerSchema('put', 200, '/settings/{name}', data);
 | 
			
		||||
				// 	expect(data.result).to.have.property('id');
 | 
			
		||||
				// 	expect(data.result.id).to.be.greaterThan(0);
 | 
			
		||||
				// });
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it.skip('Should log in with OAuth', function() {
 | 
			
		||||
			// cy.task('backendApiGet', {
 | 
			
		||||
			// 	path:  '/oauth/login?redirect_base=' + encodeURI(Cypress.config('baseUrl')),
 | 
			
		||||
			// }).then((data) => {
 | 
			
		||||
			// 	expect(data).to.have.property('result');
 | 
			
		||||
 | 
			
		||||
			// 	cy.origin('http://authentik:9000', {args: data.result}, (url) => {
 | 
			
		||||
			// 		cy.visit(url);
 | 
			
		||||
			// 		cy.get('ak-flow-executor')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('ak-stage-identification')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('input[name="uidField"]', { visible: true })
 | 
			
		||||
			// 		.type('cypress');
 | 
			
		||||
 | 
			
		||||
			// 	cy.get('ak-flow-executor')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('ak-stage-identification')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('button[type="submit"]', { visible: true })
 | 
			
		||||
			// 		.click();
 | 
			
		||||
 | 
			
		||||
			// 	cy.get('ak-flow-executor')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('ak-stage-password')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('input[name="password"]', { visible: true })
 | 
			
		||||
			// 		.type('fqXBfUYqHvYqiwBHWW7f');
 | 
			
		||||
 | 
			
		||||
			// 	cy.get('ak-flow-executor')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('ak-stage-password')
 | 
			
		||||
			// 		.shadow()
 | 
			
		||||
			// 		.find('button[type="submit"]', { visible: true })
 | 
			
		||||
			// 		.click();
 | 
			
		||||
			// 	})
 | 
			
		||||
 | 
			
		||||
			// 	// we should be logged in
 | 
			
		||||
			// 	cy.get('#root p.chakra-text')
 | 
			
		||||
			// 		.first()
 | 
			
		||||
			// 		.should('have.text', 'Nginx Proxy Manager');
 | 
			
		||||
 | 
			
		||||
			// 	// logout:
 | 
			
		||||
			// 	cy.clearLocalStorage();
 | 
			
		||||
			// });
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
@@ -1,225 +0,0 @@
 | 
			
		||||
/// <reference types="cypress" />
 | 
			
		||||
 | 
			
		||||
describe('Streams', () => {
 | 
			
		||||
	let token;
 | 
			
		||||
 | 
			
		||||
	before(() => {
 | 
			
		||||
		cy.getToken().then((tok) => {
 | 
			
		||||
			token = tok;
 | 
			
		||||
			// Set default site content
 | 
			
		||||
			cy.task('backendApiPut', {
 | 
			
		||||
				token: token,
 | 
			
		||||
				path:  '/api/settings/default-site',
 | 
			
		||||
				data: {
 | 
			
		||||
					value: 'html',
 | 
			
		||||
					meta: {
 | 
			
		||||
						html: '<p>yay it works</p>'
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			}).then((data) => {
 | 
			
		||||
				cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// Create a custom cert pair
 | 
			
		||||
		cy.exec('mkcert -cert-file=/test/cypress/fixtures/website1.pem -key-file=/test/cypress/fixtures/website1.key.pem website1.example.com').then((result) => {
 | 
			
		||||
			expect(result.code).to.eq(0);
 | 
			
		||||
			// Install CA
 | 
			
		||||
			cy.exec('mkcert -install').then((result) => {
 | 
			
		||||
				expect(result.code).to.eq(0);
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		cy.exec('rm -f /test/results/testssl.json');
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	it('Should be able to create TCP Stream', function() {
 | 
			
		||||
		cy.task('backendApiPost', {
 | 
			
		||||
			token: token,
 | 
			
		||||
			path:  '/api/nginx/streams',
 | 
			
		||||
			data:  {
 | 
			
		||||
				incoming_port: 1500,
 | 
			
		||||
				forwarding_host: '127.0.0.1',
 | 
			
		||||
				forwarding_port: 80,
 | 
			
		||||
				certificate_id: 0,
 | 
			
		||||
				meta: {
 | 
			
		||||
					dns_provider_credentials: "",
 | 
			
		||||
					letsencrypt_agree: false,
 | 
			
		||||
					dns_challenge: true
 | 
			
		||||
				},
 | 
			
		||||
				tcp_forwarding: true,
 | 
			
		||||
				udp_forwarding: false
 | 
			
		||||
			}
 | 
			
		||||
		}).then((data) => {
 | 
			
		||||
			cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
 | 
			
		||||
			expect(data).to.have.property('id');
 | 
			
		||||
			expect(data.id).to.be.greaterThan(0);
 | 
			
		||||
			expect(data).to.have.property('enabled', true);
 | 
			
		||||
			expect(data).to.have.property('tcp_forwarding', true);
 | 
			
		||||
			expect(data).to.have.property('udp_forwarding', false);
 | 
			
		||||
 | 
			
		||||
			cy.exec('curl --noproxy -- http://website1.example.com:1500').then((result) => {
 | 
			
		||||
				expect(result.code).to.eq(0);
 | 
			
		||||
				expect(result.stdout).to.contain('yay it works');
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	it('Should be able to create UDP Stream', function() {
 | 
			
		||||
		cy.task('backendApiPost', {
 | 
			
		||||
			token: token,
 | 
			
		||||
			path:  '/api/nginx/streams',
 | 
			
		||||
			data:  {
 | 
			
		||||
				incoming_port: 1501,
 | 
			
		||||
				forwarding_host: '127.0.0.1',
 | 
			
		||||
				forwarding_port: 80,
 | 
			
		||||
				certificate_id: 0,
 | 
			
		||||
				meta: {
 | 
			
		||||
					dns_provider_credentials: "",
 | 
			
		||||
					letsencrypt_agree: false,
 | 
			
		||||
					dns_challenge: true
 | 
			
		||||
				},
 | 
			
		||||
				tcp_forwarding: false,
 | 
			
		||||
				udp_forwarding: true
 | 
			
		||||
			}
 | 
			
		||||
		}).then((data) => {
 | 
			
		||||
			cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
 | 
			
		||||
			expect(data).to.have.property('id');
 | 
			
		||||
			expect(data.id).to.be.greaterThan(0);
 | 
			
		||||
			expect(data).to.have.property('enabled', true);
 | 
			
		||||
			expect(data).to.have.property('tcp_forwarding', false);
 | 
			
		||||
			expect(data).to.have.property('udp_forwarding', true);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	it('Should be able to create TCP/UDP Stream', function() {
 | 
			
		||||
		cy.task('backendApiPost', {
 | 
			
		||||
			token: token,
 | 
			
		||||
			path:  '/api/nginx/streams',
 | 
			
		||||
			data:  {
 | 
			
		||||
				incoming_port: 1502,
 | 
			
		||||
				forwarding_host: '127.0.0.1',
 | 
			
		||||
				forwarding_port: 80,
 | 
			
		||||
				certificate_id: 0,
 | 
			
		||||
				meta: {
 | 
			
		||||
					dns_provider_credentials: "",
 | 
			
		||||
					letsencrypt_agree: false,
 | 
			
		||||
					dns_challenge: true
 | 
			
		||||
				},
 | 
			
		||||
				tcp_forwarding: true,
 | 
			
		||||
				udp_forwarding: true
 | 
			
		||||
			}
 | 
			
		||||
		}).then((data) => {
 | 
			
		||||
			cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
 | 
			
		||||
			expect(data).to.have.property('id');
 | 
			
		||||
			expect(data.id).to.be.greaterThan(0);
 | 
			
		||||
			expect(data).to.have.property('enabled', true);
 | 
			
		||||
			expect(data).to.have.property('tcp_forwarding', true);
 | 
			
		||||
			expect(data).to.have.property('udp_forwarding', true);
 | 
			
		||||
 | 
			
		||||
			cy.exec('curl --noproxy -- http://website1.example.com:1502').then((result) => {
 | 
			
		||||
				expect(result.code).to.eq(0);
 | 
			
		||||
				expect(result.stdout).to.contain('yay it works');
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	it('Should be able to create SSL TCP Stream', function() {
 | 
			
		||||
		let certID = 0;
 | 
			
		||||
 | 
			
		||||
		// Create custom cert
 | 
			
		||||
		cy.task('backendApiPost', {
 | 
			
		||||
			token: token,
 | 
			
		||||
			path:  '/api/nginx/certificates',
 | 
			
		||||
			data:  {
 | 
			
		||||
				provider: "other",
 | 
			
		||||
				nice_name: "Custom Certificate for SSL Stream",
 | 
			
		||||
			},
 | 
			
		||||
		}).then((data) => {
 | 
			
		||||
			cy.validateSwaggerSchema('post', 201, '/nginx/certificates', data);
 | 
			
		||||
			expect(data).to.have.property('id');
 | 
			
		||||
			certID = data.id;
 | 
			
		||||
 | 
			
		||||
			// Upload files
 | 
			
		||||
			cy.task('backendApiPostFiles', {
 | 
			
		||||
				token: token,
 | 
			
		||||
				path:  `/api/nginx/certificates/${certID}/upload`,
 | 
			
		||||
				files:  {
 | 
			
		||||
					certificate: 'website1.pem',
 | 
			
		||||
					certificate_key: 'website1.key.pem',
 | 
			
		||||
				},
 | 
			
		||||
			}).then((data) => {
 | 
			
		||||
				cy.validateSwaggerSchema('post', 200, '/nginx/certificates/{certID}/upload', data);
 | 
			
		||||
				expect(data).to.have.property('certificate');
 | 
			
		||||
				expect(data).to.have.property('certificate_key');
 | 
			
		||||
 | 
			
		||||
				// Create the stream
 | 
			
		||||
				cy.task('backendApiPost', {
 | 
			
		||||
					token: token,
 | 
			
		||||
					path:  '/api/nginx/streams',
 | 
			
		||||
					data:  {
 | 
			
		||||
						incoming_port: 1503,
 | 
			
		||||
						forwarding_host: '127.0.0.1',
 | 
			
		||||
						forwarding_port: 80,
 | 
			
		||||
						certificate_id: certID,
 | 
			
		||||
						meta: {
 | 
			
		||||
							dns_provider_credentials: "",
 | 
			
		||||
							letsencrypt_agree: false,
 | 
			
		||||
							dns_challenge: true
 | 
			
		||||
						},
 | 
			
		||||
						tcp_forwarding: true,
 | 
			
		||||
						udp_forwarding: false
 | 
			
		||||
					}
 | 
			
		||||
				}).then((data) => {
 | 
			
		||||
					cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
 | 
			
		||||
					expect(data).to.have.property('id');
 | 
			
		||||
					expect(data.id).to.be.greaterThan(0);
 | 
			
		||||
					expect(data).to.have.property("enabled", true);
 | 
			
		||||
					expect(data).to.have.property('tcp_forwarding', true);
 | 
			
		||||
					expect(data).to.have.property('udp_forwarding', false);
 | 
			
		||||
					expect(data).to.have.property('certificate_id', certID);
 | 
			
		||||
 | 
			
		||||
					// Check the ssl termination
 | 
			
		||||
					cy.task('log', '[testssl.sh] Running ...');
 | 
			
		||||
					cy.exec('/testssl/testssl.sh --quiet --add-ca="$(/bin/mkcert -CAROOT)/rootCA.pem" --jsonfile=/test/results/testssl.json website1.example.com:1503', {
 | 
			
		||||
						timeout: 120000, // 2 minutes
 | 
			
		||||
					}).then((result) => {
 | 
			
		||||
						cy.task('log', '[testssl.sh] ' + result.stdout);
 | 
			
		||||
 | 
			
		||||
						const allowedSeverities = ["INFO", "OK", "LOW", "MEDIUM"];
 | 
			
		||||
						const ignoredIDs = [
 | 
			
		||||
							'cert_chain_of_trust',
 | 
			
		||||
							'cert_extlifeSpan',
 | 
			
		||||
							'cert_revocation',
 | 
			
		||||
							'overall_grade',
 | 
			
		||||
						];
 | 
			
		||||
 | 
			
		||||
						cy.readFile('/test/results/testssl.json').then((data) => {
 | 
			
		||||
							// Parse each array item
 | 
			
		||||
							for (let i = 0; i < data.length; i++) {
 | 
			
		||||
								const item = data[i];
 | 
			
		||||
								if (ignoredIDs.includes(item.id)) {
 | 
			
		||||
									continue;
 | 
			
		||||
								}
 | 
			
		||||
								expect(item.severity).to.be.oneOf(allowedSeverities);
 | 
			
		||||
							}
 | 
			
		||||
						});
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	it('Should be able to List Streams', function() {
 | 
			
		||||
		cy.task('backendApiGet', {
 | 
			
		||||
			token: token,
 | 
			
		||||
			path:  '/api/nginx/streams?expand=owner,certificate',
 | 
			
		||||
		}).then((data) => {
 | 
			
		||||
			cy.validateSwaggerSchema('get', 200, '/nginx/streams', data);
 | 
			
		||||
			expect(data.length).to.be.greaterThan(0);
 | 
			
		||||
			expect(data[0]).to.have.property('id');
 | 
			
		||||
			expect(data[0]).to.have.property('enabled');
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
@@ -4,18 +4,18 @@
 | 
			
		||||
	"description": "",
 | 
			
		||||
	"main": "index.js",
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"@jc21/cypress-swagger-validation": "^0.3.2",
 | 
			
		||||
		"axios": "^1.7.9",
 | 
			
		||||
		"cypress": "^14.0.1",
 | 
			
		||||
		"cypress-multi-reporters": "^2.0.5",
 | 
			
		||||
		"@jc21/cypress-swagger-validation": "^0.3.1",
 | 
			
		||||
		"axios": "^1.7.7",
 | 
			
		||||
		"cypress": "^13.15.0",
 | 
			
		||||
		"cypress-multi-reporters": "^1.6.4",
 | 
			
		||||
		"cypress-wait-until": "^3.0.2",
 | 
			
		||||
		"eslint": "^9.19.0",
 | 
			
		||||
		"eslint": "^9.12.0",
 | 
			
		||||
		"eslint-plugin-align-assignments": "^1.1.2",
 | 
			
		||||
		"eslint-plugin-chai-friendly": "^1.0.1",
 | 
			
		||||
		"eslint-plugin-cypress": "^4.1.0",
 | 
			
		||||
		"eslint-plugin-cypress": "^3.5.0",
 | 
			
		||||
		"form-data": "^4.0.1",
 | 
			
		||||
		"lodash": "^4.17.21",
 | 
			
		||||
		"mocha": "^11.1.0",
 | 
			
		||||
		"mocha": "^10.7.3",
 | 
			
		||||
		"mocha-junit-reporter": "^2.2.1"
 | 
			
		||||
	},
 | 
			
		||||
	"scripts": {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										949
									
								
								test/yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										949
									
								
								test/yarn.lock
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user